summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--Android.mk36
-rw-r--r--apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java2
-rw-r--r--api/current.txt1115
-rw-r--r--api/system-current.txt58
-rw-r--r--api/system-removed.txt50
-rw-r--r--api/test-current.txt24
-rw-r--r--cmds/content/src/com/android/commands/content/Content.java6
-rw-r--r--cmds/statsd/Android.mk11
-rw-r--r--cmds/statsd/src/HashableDimensionKey.cpp26
-rw-r--r--cmds/statsd/src/HashableDimensionKey.h61
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h5
-rw-r--r--cmds/statsd/src/StatsService.cpp6
-rw-r--r--cmds/statsd/src/StatsService.h5
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp18
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.h20
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp12
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.h8
-rw-r--r--cmds/statsd/src/atoms.proto45
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.cpp32
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.h14
-rw-r--r--cmds/statsd/src/condition/ConditionTracker.h16
-rw-r--r--cmds/statsd/src/condition/ConditionWizard.cpp19
-rw-r--r--cmds/statsd/src/condition/ConditionWizard.h9
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp123
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.h12
-rw-r--r--cmds/statsd/src/condition/condition_util.cpp3
-rw-r--r--cmds/statsd/src/dimension.cpp43
-rw-r--r--cmds/statsd/src/dimension.h3
-rw-r--r--cmds/statsd/src/external/ResourceHealthManagerPuller.cpp101
-rw-r--r--cmds/statsd/src/external/ResourceHealthManagerPuller.h37
-rw-r--r--cmds/statsd/src/external/StatsPuller.cpp5
-rw-r--r--cmds/statsd/src/external/StatsPuller.h2
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.h6
-rw-r--r--cmds/statsd/src/external/StatsPullerManagerImpl.cpp14
-rw-r--r--cmds/statsd/src/external/StatsPullerManagerImpl.h2
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp14
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp35
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h6
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp100
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h12
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp25
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h8
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp48
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h6
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp23
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h4
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp29
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h8
-rw-r--r--cmds/statsd/src/metrics/duration_helper/DurationTracker.h18
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp32
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h13
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp41
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h12
-rw-r--r--cmds/statsd/src/stats_log_util.cpp6
-rw-r--r--cmds/statsd/src/stats_util.h3
-rw-r--r--cmds/statsd/src/subscriber/SubscriberReporter.cpp8
-rw-r--r--cmds/statsd/src/subscriber/SubscriberReporter.h6
-rw-r--r--cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp36
-rw-r--r--cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp37
-rw-r--r--cmds/statsd/tests/dimension_test.cpp149
-rw-r--r--cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp725
-rw-r--r--cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp22
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp34
-rw-r--r--cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp8
-rw-r--r--cmds/statsd/tests/metrics/EventMetricProducer_test.cpp4
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp6
-rw-r--r--cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp79
-rw-r--r--cmds/statsd/tests/metrics/OringDurationTracker_test.cpp141
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp10
-rw-r--r--cmds/statsd/tests/metrics/metrics_test_helper.cpp7
-rw-r--r--cmds/statsd/tests/metrics/metrics_test_helper.h7
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp87
-rw-r--r--cmds/statsd/tests/statsd_test_util.h36
-rw-r--r--cmds/statsd/tools/dogfood/Android.mk1
-rw-r--r--cmds/statsd/tools/dogfood/AndroidManifest.xml1
-rw-r--r--cmds/statsd/tools/dogfood/res/raw/statsd_baseline_configbin4785 -> 15841 bytes
-rw-r--r--cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java20
-rw-r--r--cmds/svc/src/com/android/commands/svc/UsbCommand.java43
-rw-r--r--config/hiddenapi-light-greylist.txt1164
-rw-r--r--core/java/android/app/Activity.java18
-rw-r--r--core/java/android/app/ActivityThread.java24
-rw-r--r--core/java/android/app/EphemeralResolverService.java116
-rw-r--r--core/java/android/app/IInstantAppResolver.aidl8
-rw-r--r--core/java/android/app/InstantAppResolverService.java113
-rw-r--r--core/java/android/app/WallpaperManager.java7
-rw-r--r--core/java/android/app/WindowConfiguration.java3
-rw-r--r--core/java/android/app/slice/ISliceManager.aidl3
-rw-r--r--core/java/android/app/slice/SliceManager.java101
-rw-r--r--core/java/android/app/slice/SliceProvider.java11
-rw-r--r--core/java/android/content/Intent.java131
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java9
-rw-r--r--core/java/android/content/pm/AuxiliaryResolveInfo.java99
-rw-r--r--core/java/android/content/pm/EphemeralIntentFilter.java85
-rw-r--r--core/java/android/content/pm/EphemeralResolveInfo.java224
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl3
-rw-r--r--core/java/android/content/pm/InstantAppResolveInfo.java96
-rw-r--r--core/java/android/content/pm/PackageInstaller.java12
-rw-r--r--core/java/android/hardware/biometrics/BiometricAuthenticator.java184
-rw-r--r--core/java/android/hardware/biometrics/BiometricFingerprintConstants.java168
-rw-r--r--core/java/android/hardware/biometrics/CryptoObject.java79
-rw-r--r--core/java/android/hardware/display/AmbientBrightnessDayStats.aidl19
-rw-r--r--core/java/android/hardware/display/AmbientBrightnessDayStats.java211
-rw-r--r--core/java/android/hardware/display/BrightnessChangeEvent.java51
-rw-r--r--core/java/android/hardware/display/DisplayManager.java10
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java15
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java4
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl3
-rw-r--r--core/java/android/hardware/fingerprint/Fingerprint.java3
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintDialog.java283
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java235
-rw-r--r--core/java/android/hardware/radio/RadioManager.java98
-rw-r--r--core/java/android/hardware/radio/Utils.java23
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl18
-rw-r--r--core/java/android/hardware/usb/UsbManager.java260
-rw-r--r--core/java/android/net/IpSecTransform.java2
-rw-r--r--core/java/android/net/TrafficStats.java20
-rw-r--r--core/java/android/net/Uri.java8
-rw-r--r--core/java/android/os/FileUtils.java342
-rw-r--r--core/java/android/os/UserManager.java8
-rw-r--r--core/java/android/print/PrintFileDocumentAdapter.java33
-rw-r--r--core/java/android/provider/Settings.java17
-rw-r--r--core/java/android/se/omapi/Channel.java9
-rw-r--r--core/java/android/se/omapi/Reader.java8
-rw-r--r--core/java/android/se/omapi/SEService.java17
-rw-r--r--core/java/android/se/omapi/Session.java21
-rw-r--r--core/java/android/security/ConfirmationAlreadyPresentingException.java29
-rw-r--r--core/java/android/security/ConfirmationCallback.java54
-rw-r--r--core/java/android/security/ConfirmationDialog.java283
-rw-r--r--core/java/android/security/ConfirmationNotAvailableException.java30
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java1
-rw-r--r--core/java/android/security/keystore/recovery/RecoveryController.java78
-rw-r--r--core/java/android/service/autofill/AutofillService.java9
-rw-r--r--core/java/android/service/autofill/CustomDescription.java5
-rw-r--r--core/java/android/service/autofill/Dataset.java108
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java96
-rw-r--r--core/java/android/service/settings/suggestions/Suggestion.java5
-rw-r--r--core/java/android/text/style/DrawableMarginSpan.java84
-rw-r--r--core/java/android/text/style/DynamicDrawableSpan.java79
-rw-r--r--core/java/android/text/style/IconMarginSpan.java75
-rw-r--r--core/java/android/text/style/ImageSpan.java161
-rw-r--r--core/java/android/text/style/LineHeightSpan.java40
-rw-r--r--core/java/android/text/style/MaskFilterSpan.java23
-rw-r--r--core/java/android/text/style/StyleSpan.java44
-rw-r--r--core/java/android/text/style/TabStopSpan.java55
-rw-r--r--core/java/android/text/style/TypefaceSpan.java44
-rw-r--r--core/java/android/text/style/URLSpan.java42
-rw-r--r--core/java/android/util/FeatureFlagUtils.java1
-rw-r--r--core/java/android/util/StatsManager.java44
-rw-r--r--core/java/android/util/apk/ApkVerityBuilder.java21
-rw-r--r--core/java/android/view/PointerIcon.java37
-rw-r--r--core/java/android/view/RenderNode.java29
-rw-r--r--core/java/android/view/View.java78
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java21
-rw-r--r--core/java/android/view/textclassifier/TextClassifierImpl.java3
-rw-r--r--core/java/android/view/textclassifier/TextLinks.java8
-rw-r--r--core/java/android/widget/ImageView.java23
-rw-r--r--core/java/android/widget/MediaControlView2.java32
-rw-r--r--core/java/android/widget/VideoView2.java15
-rw-r--r--core/java/com/android/internal/alsa/AlsaCardsParser.java203
-rw-r--r--core/java/com/android/internal/alsa/AlsaDevicesParser.java22
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java13
-rw-r--r--core/java/com/android/internal/app/UnlaunchableAppActivity.java18
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java157
-rw-r--r--core/java/com/android/internal/os/Zygote.java2
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
-rw-r--r--core/java/com/android/internal/policy/DecorView.java3
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl6
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl6
-rw-r--r--core/java/com/android/internal/util/FileRotator.java2
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl1
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl2
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java18
-rw-r--r--core/java/com/android/server/backup/SliceBackupHelper.java74
-rw-r--r--core/java/com/android/server/backup/SystemBackupAgent.java3
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp15
-rw-r--r--core/jni/android/graphics/AnimatedImageDrawable.cpp61
-rw-r--r--core/jni/android/graphics/FontFamily.cpp18
-rw-r--r--core/jni/android_media_AudioFormat.h5
-rw-r--r--core/jni/android_media_AudioRecord.cpp54
-rw-r--r--core/jni/android_media_AudioSystem.cpp45
-rw-r--r--core/jni/android_media_AudioTrack.cpp13
-rw-r--r--core/jni/android_media_MicrophoneInfo.cpp187
-rw-r--r--core/jni/android_media_MicrophoneInfo.h33
-rw-r--r--core/jni/android_view_RenderNode.cpp26
-rw-r--r--core/proto/android/providers/settings.proto2
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/res/AndroidManifest.xml18
-rw-r--r--core/res/res/layout/popup_menu_item_layout.xml19
-rw-r--r--core/res/res/layout/unlaunchable_app_activity.xml35
-rw-r--r--core/res/res/values/attrs.xml22
-rw-r--r--core/res/res/values/config.xml14
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/strings.xml31
-rw-r--r--core/res/res/values/symbols.xml12
-rw-r--r--core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java106
-rw-r--r--core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java171
-rw-r--r--core/tests/coretests/src/android/net/UriTest.java6
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java134
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--core/tests/overlaytests/device/Android.mk31
-rw-r--r--core/tests/overlaytests/device/AndroidManifest.xml8
-rw-r--r--core/tests/overlaytests/device/AndroidTest.xml47
-rw-r--r--core/tests/overlaytests/device/OverlayAppFiltered/Android.mk10
-rw-r--r--core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml9
-rw-r--r--core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt1
-rw-r--r--core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml4
-rw-r--r--core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml2
-rw-r--r--core/tests/overlaytests/device/OverlayAppFirst/Android.mk10
-rw-r--r--core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml6
-rw-r--r--core/tests/overlaytests/device/OverlayAppSecond/Android.mk10
-rw-r--r--core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml6
-rw-r--r--core/tests/overlaytests/device/OverlayTest/Android.mk16
-rw-r--r--core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml10
-rw-r--r--core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java7
-rw-r--r--core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java7
-rw-r--r--core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java7
-rw-r--r--core/tests/overlaytests/device/OverlayTestOverlay/Android.mk10
-rw-r--r--core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml6
-rw-r--r--core/tests/overlaytests/device/res/drawable-nodpi/drawable.jpg (renamed from core/tests/overlaytests/device/OverlayTest/res/drawable-nodpi/drawable.jpg)bin414 -> 414 bytes
-rw-r--r--core/tests/overlaytests/device/res/raw/lorem_ipsum.txt (renamed from core/tests/overlaytests/device/OverlayTest/res/raw/lorem_ipsum.txt)0
-rw-r--r--core/tests/overlaytests/device/res/values-sv/config.xml (renamed from core/tests/overlaytests/device/OverlayTest/res/values-sv/config.xml)0
-rw-r--r--core/tests/overlaytests/device/res/values/config.xml (renamed from core/tests/overlaytests/device/OverlayTest/res/values/config.xml)0
-rw-r--r--core/tests/overlaytests/device/res/xml/integer.xml (renamed from core/tests/overlaytests/device/OverlayTest/res/xml/integer.xml)0
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java (renamed from core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java)171
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java36
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java37
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java36
-rw-r--r--core/tests/overlaytests/device/test-apps/Android.mk15
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk22
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml22
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/res/drawable-nodpi/drawable.jpg (renamed from core/tests/overlaytests/device/OverlayAppFirst/res/drawable-nodpi/drawable.jpg)bin399 -> 399 bytes
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/res/raw/lorem_ipsum.txt (renamed from core/tests/overlaytests/device/OverlayAppFirst/res/raw/lorem_ipsum.txt)0
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values-sv/config.xml (renamed from core/tests/overlaytests/device/OverlayAppFirst/res/values-sv/config.xml)0
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml (renamed from core/tests/overlaytests/device/OverlayAppFirst/res/values/config.xml)0
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/integer.xml (renamed from core/tests/overlaytests/device/OverlayAppFirst/res/xml/integer.xml)0
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk22
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml22
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/raw/lorem_ipsum.txt (renamed from core/tests/overlaytests/device/OverlayAppSecond/res/raw/lorem_ipsum.txt)0
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values-sv/config.xml (renamed from core/tests/overlaytests/device/OverlayAppSecond/res/values-sv/config.xml)0
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values/config.xml (renamed from core/tests/overlaytests/device/OverlayAppSecond/res/values/config.xml)0
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/xml/integer.xml (renamed from core/tests/overlaytests/device/OverlayAppSecond/res/xml/integer.xml)0
-rw-r--r--core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk22
-rw-r--r--core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml22
-rw-r--r--core/tests/overlaytests/device/test-apps/FrameworkOverlay/res/values/config.xml (renamed from core/tests/overlaytests/device/OverlayTestOverlay/res/values/config.xml)0
-rw-r--r--core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml2
-rwxr-xr-xcore/tests/overlaytests/testrunner.py732
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--data/fonts/fonts.xml6
-rw-r--r--docs/html/reference/images/text/style/drawablemarginspan.pngbin0 -> 18930 bytes
-rw-r--r--docs/html/reference/images/text/style/dynamicdrawablespan.pngbin0 -> 17443 bytes
-rw-r--r--docs/html/reference/images/text/style/iconmarginspan.pngbin0 -> 22271 bytes
-rw-r--r--docs/html/reference/images/text/style/imagespan.pngbin0 -> 33683 bytes
-rw-r--r--docs/html/reference/images/text/style/maskfilterspan.pngbin0 -> 6411 bytes
-rw-r--r--docs/html/reference/images/text/style/stylespan.pngbin0 -> 6371 bytes
-rw-r--r--docs/html/reference/images/text/style/tabstopspan.pngbin0 -> 8963 bytes
-rw-r--r--docs/html/reference/images/text/style/typefacespan.pngbin0 -> 7749 bytes
-rw-r--r--docs/html/reference/images/text/style/urlspan.pngbin0 -> 9769 bytes
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java15
-rw-r--r--graphics/java/android/graphics/Bitmap.java86
-rw-r--r--graphics/java/android/graphics/FontFamily.java22
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java51
-rw-r--r--graphics/java/android/graphics/Picture.java38
-rw-r--r--graphics/java/android/graphics/Typeface.java18
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedImageDrawable.java39
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java85
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java43
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java21
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java6
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java6
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java5
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreSpi.java6
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java52
-rw-r--r--keystore/java/android/security/keystore/KeyInfo.java26
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java52
-rw-r--r--keystore/java/android/security/keystore/KeymasterUtils.java10
-rw-r--r--libs/hwui/Android.bp17
-rw-r--r--libs/hwui/JankTracker.cpp2
-rw-r--r--libs/hwui/RenderProperties.h19
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp5
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.h4
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp26
-rw-r--r--libs/hwui/hwui/Typeface.cpp9
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp8
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp5
-rw-r--r--location/java/android/location/ILocationManager.aidl5
-rw-r--r--media/java/android/media/AudioFormat.java12
-rw-r--r--media/java/android/media/AudioManager.java47
-rw-r--r--media/java/android/media/AudioPresentation.java181
-rw-r--r--media/java/android/media/AudioRecord.java33
-rw-r--r--media/java/android/media/AudioSystem.java2
-rw-r--r--media/java/android/media/AudioTrack.java20
-rw-r--r--media/java/android/media/MediaBrowser2.java20
-rw-r--r--media/java/android/media/MediaController2.java20
-rw-r--r--media/java/android/media/MediaExtractor.java13
-rw-r--r--media/java/android/media/MediaLibraryService2.java112
-rw-r--r--media/java/android/media/MediaPlayer2.java29
-rw-r--r--media/java/android/media/MediaPlayer2Impl.java47
-rw-r--r--media/java/android/media/MediaRecorder.java28
-rw-r--r--media/java/android/media/MediaSession2.java187
-rw-r--r--media/java/android/media/MediaSessionService2.java35
-rw-r--r--media/java/android/media/MicrophoneInfo.java342
-rw-r--r--media/java/android/media/RingtoneManager.java15
-rw-r--r--media/java/android/media/VolumeProvider2.java2
-rw-r--r--media/java/android/media/audiofx/Visualizer.java25
-rw-r--r--media/java/android/media/update/FrameLayoutHelper.java128
-rw-r--r--media/java/android/media/update/MediaBrowser2Provider.java2
-rw-r--r--media/java/android/media/update/MediaControlView2Provider.java7
-rw-r--r--media/java/android/media/update/MediaLibraryService2Provider.java5
-rw-r--r--media/java/android/media/update/MediaSession2Provider.java24
-rw-r--r--media/java/android/media/update/MediaSessionService2Provider.java6
-rw-r--r--media/java/android/media/update/StaticProvider.java21
-rw-r--r--media/java/android/media/update/VideoView2Provider.java9
-rw-r--r--media/java/android/media/update/ViewGroupHelper.java354
-rw-r--r--media/java/android/media/update/ViewGroupProvider.java (renamed from media/java/android/media/update/ViewProvider.java)27
-rw-r--r--media/jni/android_media_AudioPresentation.h173
-rw-r--r--media/jni/android_media_MediaPlayer2.cpp126
-rw-r--r--media/jni/android_media_MediaRecorder.cpp60
-rw-r--r--native/android/Android.bp1
-rw-r--r--native/android/libandroid.map.txt8
-rw-r--r--native/android/surface_texture.cpp75
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java4
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/Assistant.java2
-rw-r--r--packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java134
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java29
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java8
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java48
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_drag_handle.xml22
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml166
-rw-r--r--packages/SystemUI/res/layout/screen_pinning_request_buttons.xml20
-rw-r--r--packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml20
-rw-r--r--packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml22
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml16
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_row.xml23
-rw-r--r--packages/SystemUI/res/values-af/config.xml1
-rw-r--r--packages/SystemUI/res/values-am/config.xml1
-rw-r--r--packages/SystemUI/res/values-ar/config.xml1
-rw-r--r--packages/SystemUI/res/values-az/config.xml1
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/config.xml1
-rw-r--r--packages/SystemUI/res/values-be/config.xml1
-rw-r--r--packages/SystemUI/res/values-bg/config.xml1
-rw-r--r--packages/SystemUI/res/values-bn/config.xml1
-rw-r--r--packages/SystemUI/res/values-bs/config.xml1
-rw-r--r--packages/SystemUI/res/values-ca/config.xml1
-rw-r--r--packages/SystemUI/res/values-cs/config.xml1
-rw-r--r--packages/SystemUI/res/values-da/config.xml1
-rw-r--r--packages/SystemUI/res/values-de/config.xml1
-rw-r--r--packages/SystemUI/res/values-el/config.xml1
-rw-r--r--packages/SystemUI/res/values-en-rAU/config.xml1
-rw-r--r--packages/SystemUI/res/values-en-rCA/config.xml1
-rw-r--r--packages/SystemUI/res/values-en-rGB/config.xml1
-rw-r--r--packages/SystemUI/res/values-en-rIN/config.xml1
-rw-r--r--packages/SystemUI/res/values-en-rXC/config.xml1
-rw-r--r--packages/SystemUI/res/values-es-rUS/config.xml1
-rw-r--r--packages/SystemUI/res/values-es/config.xml1
-rw-r--r--packages/SystemUI/res/values-et/config.xml1
-rw-r--r--packages/SystemUI/res/values-eu/config.xml1
-rw-r--r--packages/SystemUI/res/values-fa/config.xml1
-rw-r--r--packages/SystemUI/res/values-fi/config.xml1
-rw-r--r--packages/SystemUI/res/values-fr-rCA/config.xml1
-rw-r--r--packages/SystemUI/res/values-fr/config.xml1
-rw-r--r--packages/SystemUI/res/values-gl/config.xml1
-rw-r--r--packages/SystemUI/res/values-gu/config.xml1
-rw-r--r--packages/SystemUI/res/values-hi/config.xml1
-rw-r--r--packages/SystemUI/res/values-hr/config.xml1
-rw-r--r--packages/SystemUI/res/values-hu/config.xml1
-rw-r--r--packages/SystemUI/res/values-hy/config.xml1
-rw-r--r--packages/SystemUI/res/values-in/config.xml1
-rw-r--r--packages/SystemUI/res/values-is/config.xml1
-rw-r--r--packages/SystemUI/res/values-it/config.xml1
-rw-r--r--packages/SystemUI/res/values-iw/config.xml1
-rw-r--r--packages/SystemUI/res/values-ja/config.xml1
-rw-r--r--packages/SystemUI/res/values-ka/config.xml1
-rw-r--r--packages/SystemUI/res/values-kk/config.xml1
-rw-r--r--packages/SystemUI/res/values-km/config.xml1
-rw-r--r--packages/SystemUI/res/values-kn/config.xml1
-rw-r--r--packages/SystemUI/res/values-ko/config.xml1
-rw-r--r--packages/SystemUI/res/values-ky/config.xml1
-rw-r--r--packages/SystemUI/res/values-lo/config.xml1
-rw-r--r--packages/SystemUI/res/values-lt/config.xml1
-rw-r--r--packages/SystemUI/res/values-lv/config.xml1
-rw-r--r--packages/SystemUI/res/values-mk/config.xml1
-rw-r--r--packages/SystemUI/res/values-ml/config.xml1
-rw-r--r--packages/SystemUI/res/values-mn/config.xml1
-rw-r--r--packages/SystemUI/res/values-mr/config.xml1
-rw-r--r--packages/SystemUI/res/values-ms/config.xml1
-rw-r--r--packages/SystemUI/res/values-my/config.xml1
-rw-r--r--packages/SystemUI/res/values-nb/config.xml1
-rw-r--r--packages/SystemUI/res/values-ne/config.xml1
-rw-r--r--packages/SystemUI/res/values-nl/config.xml1
-rw-r--r--packages/SystemUI/res/values-pa/config.xml1
-rw-r--r--packages/SystemUI/res/values-pl/config.xml1
-rw-r--r--packages/SystemUI/res/values-pt-rBR/config.xml1
-rw-r--r--packages/SystemUI/res/values-pt-rPT/config.xml1
-rw-r--r--packages/SystemUI/res/values-pt/config.xml1
-rw-r--r--packages/SystemUI/res/values-ro/config.xml1
-rw-r--r--packages/SystemUI/res/values-ru/config.xml1
-rw-r--r--packages/SystemUI/res/values-si/config.xml1
-rw-r--r--packages/SystemUI/res/values-sk/config.xml1
-rw-r--r--packages/SystemUI/res/values-sl/config.xml1
-rw-r--r--packages/SystemUI/res/values-sq/config.xml1
-rw-r--r--packages/SystemUI/res/values-sr/config.xml1
-rw-r--r--packages/SystemUI/res/values-sv/config.xml1
-rw-r--r--packages/SystemUI/res/values-sw/config.xml1
-rw-r--r--packages/SystemUI/res/values-ta/config.xml1
-rw-r--r--packages/SystemUI/res/values-te/config.xml1
-rw-r--r--packages/SystemUI/res/values-th/config.xml1
-rw-r--r--packages/SystemUI/res/values-tl/config.xml1
-rw-r--r--packages/SystemUI/res/values-tr/config.xml1
-rw-r--r--packages/SystemUI/res/values-uk/config.xml1
-rw-r--r--packages/SystemUI/res/values-ur/config.xml1
-rw-r--r--packages/SystemUI/res/values-uz/config.xml1
-rw-r--r--packages/SystemUI/res/values-vi/config.xml1
-rw-r--r--packages/SystemUI/res/values-zh-rCN/config.xml1
-rw-r--r--packages/SystemUI/res/values-zh-rHK/config.xml1
-rw-r--r--packages/SystemUI/res/values-zh-rTW/config.xml1
-rw-r--r--packages/SystemUI/res/values-zu/config.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml15
-rw-r--r--packages/SystemUI/res/values/strings.xml13
-rw-r--r--packages/SystemUI/res/values/styles.xml11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/Interpolators.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/OverviewProxyService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SysUIToast.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java455
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java (renamed from services/core/java/com/android/server/am/LockTaskNotify.java)27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java566
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java220
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java220
-rw-r--r--proto/src/metrics_constants.proto12
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--proto/src/wifi.proto27
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java114
-rw-r--r--services/core/java/com/android/server/EventLogTags.logtags2
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java16
-rw-r--r--services/core/java/com/android/server/IpSecService.java57
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java171
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java55
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java14
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java15
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java30
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java18
-rw-r--r--services/core/java/com/android/server/am/LockTaskController.java28
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/Convert.java10
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java12
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java3
-rw-r--r--services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java363
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java8
-rw-r--r--services/core/java/com/android/server/display/BrightnessIdleJob.java2
-rw-r--r--services/core/java/com/android/server/display/BrightnessMappingStrategy.java26
-rw-r--r--services/core/java/com/android/server/display/BrightnessTracker.java159
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java23
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java37
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java3
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java63
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java21
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java70
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java113
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java2
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/Installer.java5
-rw-r--r--services/core/java/com/android/server/pm/InstantAppResolver.java356
-rw-r--r--services/core/java/com/android/server/pm/InstantAppResolverConnection.java (renamed from services/core/java/com/android/server/pm/EphemeralResolverConnection.java)67
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java25
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java56
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java329
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java55
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java8
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java70
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java5
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/dex/DexoptOptions.java11
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java4
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java8
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java72
-rw-r--r--services/core/java/com/android/server/slice/SliceFullAccessList.java17
-rw-r--r--services/core/java/com/android/server/slice/SliceManagerService.java123
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java11
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java21
-rw-r--r--services/core/java/com/android/server/wm/AlertWindowNotification.java17
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java43
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java23
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java72
-rw-r--r--services/core/java/com/android/server/wm/Session.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java32
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java18
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java52
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java73
-rw-r--r--services/core/jni/BroadcastRadio/convert.cpp5
-rw-r--r--services/java/com/android/server/SystemServer.java63
-rw-r--r--services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java443
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java50
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java20
-rw-r--r--services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java12
-rw-r--r--services/usage/java/com/android/server/usage/AppIdleHistory.java4
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java28
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaDevice.java160
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java486
-rw-r--r--services/usb/java/com/android/server/usb/UsbAudioDevice.java77
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java834
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java17
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java65
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java157
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java19
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java28
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthCdma.java51
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthGsm.java66
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java66
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthWcdma.java48
-rw-r--r--telephony/java/android/telephony/LocationAccessPolicy.java160
-rw-r--r--telephony/java/android/telephony/ServiceState.java37
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java6
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java5
-rw-r--r--tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml10
-rw-r--r--tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java55
-rw-r--r--tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java70
-rw-r--r--tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java2
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml3
-rw-r--r--tests/HwAccelerationTest/res/values/styles.xml7
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java30
-rw-r--r--tests/UsbTests/Android.mk44
-rw-r--r--tests/UsbTests/AndroidManifest.xml30
-rw-r--r--tests/UsbTests/AndroidTest.xml29
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java316
-rw-r--r--tests/net/java/com/android/server/connectivity/TetheringTest.java2
-rw-r--r--wifi/java/android/net/wifi/RttManager.java372
596 files changed, 17834 insertions, 8541 deletions
diff --git a/Android.bp b/Android.bp
index 129d6769cb98..d5e04f9f4e2d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -676,6 +676,7 @@ java_library {
"android.hardware.vibrator-V1.1-java-constants",
"android.hardware.wifi-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
+ "android.hardware.usb.gadget-V1.0-java",
],
// Loaded with System.loadLibrary by android.view.textclassifier
diff --git a/Android.mk b/Android.mk
index 35b5f92be310..a78a01a1600f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -821,39 +821,41 @@ LOCAL_PROTOC_FLAGS := \
LOCAL_SRC_FILES := \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto/android/os)
+# Protos have lots of MissingOverride and similar.
+LOCAL_ERROR_PRONE_FLAGS := -XepDisableAllChecks
include $(BUILD_STATIC_JAVA_LIBRARY)
# ==== hiddenapi lists =======================================
-# Copy blacklist and dark greylist over into the build folder.
+# Copy blacklist and light greylist over into the build folder.
# This is for ART buildbots which need to mock these lists and have alternative
# rules for building them. Other rules in the build system should depend on the
# files in the build folder.
$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
-$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
-
-# Generate light greylist as private API minus (blacklist plus dark greylist).
-
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
- if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
- echo "There should be no overlap between $(BLACKLIST) and $(DARK_GREYLIST)" 1>&2; \
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-light-greylist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)))
+
+# Generate dark greylist as private API minus (blacklist plus light greylist).
+
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+ if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \
+ echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \
exit 1; \
elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
exit 2; \
- elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
- echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \
+ echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
exit 3; \
fi
- comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(DARK_GREYLIST)) > $@
+ comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@
# Include subdirectory makefiles
# ============================================================
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index bab2a859698c..231aaf2ca074 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -232,7 +232,7 @@ public class StaticLayoutPerfTest {
while (state.keepRunning()) {
state.pauseTiming();
final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
.build();
diff --git a/api/current.txt b/api/current.txt
index 44f2f0836049..10fdea4da895 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -969,7 +969,9 @@ package android {
field public static final int orderingFromXml = 16843239; // 0x10101e7
field public static final int orientation = 16842948; // 0x10100c4
field public static final int outAnimation = 16843128; // 0x1010178
+ field public static final int outlineAmbientShadowColor = 16844162; // 0x1010582
field public static final int outlineProvider = 16843960; // 0x10104b8
+ field public static final int outlineSpotShadowColor = 16844161; // 0x1010581
field public static final int overScrollFooter = 16843459; // 0x10102c3
field public static final int overScrollHeader = 16843458; // 0x10102c2
field public static final int overScrollMode = 16843457; // 0x10102c1
@@ -6085,16 +6087,16 @@ package android.app {
method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
- method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+ method public android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public boolean injectInputEvent(android.view.InputEvent, boolean);
- method public final boolean performGlobalAction(int);
+ method public boolean performGlobalAction(int);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
method public void setRunAsMonkey(boolean);
- method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
+ method public void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public android.graphics.Bitmap takeScreenshot();
method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1
@@ -7250,6 +7252,7 @@ package android.app.slice {
method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
+ method public android.net.Uri mapIntentToUri(android.content.Intent);
method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor);
@@ -10050,6 +10053,7 @@ package android.content {
field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
field public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 4096; // 0x1000
+ field public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 2048; // 0x800
field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
@@ -11612,15 +11616,15 @@ package android.content.res {
public final class AssetManager implements java.lang.AutoCloseable {
method public void close();
- method public java.lang.String[] getLocales();
- method public java.lang.String[] list(java.lang.String) throws java.io.IOException;
- method public java.io.InputStream open(java.lang.String) throws java.io.IOException;
- method public java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
- method public android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
- method public android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
- method public android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
- method public android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
- method public android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
+ method public final java.lang.String[] getLocales();
+ method public final java.lang.String[] list(java.lang.String) throws java.io.IOException;
+ method public final java.io.InputStream open(java.lang.String) throws java.io.IOException;
+ method public final java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
+ method public final android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
+ method public final android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
+ method public final android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
+ method public final android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
+ method public final android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
field public static final int ACCESS_BUFFER = 3; // 0x3
field public static final int ACCESS_RANDOM = 1; // 0x1
field public static final int ACCESS_STREAMING = 2; // 0x2
@@ -11628,15 +11632,9 @@ package android.content.res {
}
public final class AssetManager.AssetInputStream extends java.io.InputStream {
- method public final int available() throws java.io.IOException;
- method public final void close() throws java.io.IOException;
- method public final void mark(int);
- method public final boolean markSupported();
- method public final int read() throws java.io.IOException;
- method public final int read(byte[]) throws java.io.IOException;
- method public final int read(byte[], int, int) throws java.io.IOException;
- method public final void reset() throws java.io.IOException;
- method public final long skip(long) throws java.io.IOException;
+ method public void mark(int);
+ method public int read() throws java.io.IOException;
+ method public void reset() throws java.io.IOException;
}
public class ColorStateList implements android.os.Parcelable {
@@ -12398,7 +12396,7 @@ package android.database.sqlite {
method public java.util.List<android.util.Pair<java.lang.String, java.lang.String>> getAttachedDbs();
method public long getMaximumSize();
method public long getPageSize();
- method public final java.lang.String getPath();
+ method public java.lang.String getPath();
method public deprecated java.util.Map<java.lang.String, java.lang.String> getSyncedTables();
method public int getVersion();
method public boolean inTransaction();
@@ -13050,34 +13048,36 @@ package android.graphics {
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean);
method public int describeContents();
method public void eraseColor(int);
method public android.graphics.Bitmap extractAlpha();
method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
- method public final int getAllocationByteCount();
- method public final int getByteCount();
- method public final android.graphics.ColorSpace getColorSpace();
- method public final android.graphics.Bitmap.Config getConfig();
+ method public int getAllocationByteCount();
+ method public int getByteCount();
+ method public android.graphics.ColorSpace getColorSpace();
+ method public android.graphics.Bitmap.Config getConfig();
method public int getDensity();
method public int getGenerationId();
- method public final int getHeight();
+ method public int getHeight();
method public byte[] getNinePatchChunk();
method public int getPixel(int, int);
method public void getPixels(int[], int, int, int, int, int, int);
- method public final int getRowBytes();
+ method public int getRowBytes();
method public int getScaledHeight(android.graphics.Canvas);
method public int getScaledHeight(android.util.DisplayMetrics);
method public int getScaledHeight(int);
method public int getScaledWidth(android.graphics.Canvas);
method public int getScaledWidth(android.util.DisplayMetrics);
method public int getScaledWidth(int);
- method public final int getWidth();
- method public final boolean hasAlpha();
- method public final boolean hasMipMap();
- method public final boolean isMutable();
- method public final boolean isPremultiplied();
- method public final boolean isRecycled();
+ method public int getWidth();
+ method public boolean hasAlpha();
+ method public boolean hasMipMap();
+ method public boolean isMutable();
+ method public boolean isPremultiplied();
+ method public boolean isRecycled();
method public void prepareToDraw();
method public void reconfigure(int, int, android.graphics.Bitmap.Config);
method public void recycle();
@@ -13085,11 +13085,11 @@ package android.graphics {
method public void setConfig(android.graphics.Bitmap.Config);
method public void setDensity(int);
method public void setHasAlpha(boolean);
- method public final void setHasMipMap(boolean);
+ method public void setHasMipMap(boolean);
method public void setHeight(int);
method public void setPixel(int, int, int);
method public void setPixels(int[], int, int, int, int, int, int);
- method public final void setPremultiplied(boolean);
+ method public void setPremultiplied(boolean);
method public void setWidth(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.Bitmap> CREATOR;
@@ -13161,7 +13161,7 @@ package android.graphics {
method public android.graphics.Bitmap decodeRegion(android.graphics.Rect, android.graphics.BitmapFactory.Options);
method public int getHeight();
method public int getWidth();
- method public final boolean isRecycled();
+ method public boolean isRecycled();
method public static android.graphics.BitmapRegionDecoder newInstance(byte[], int, int, boolean) throws java.io.IOException;
method public static android.graphics.BitmapRegionDecoder newInstance(java.io.FileDescriptor, boolean) throws java.io.IOException;
method public static android.graphics.BitmapRegionDecoder newInstance(java.io.InputStream, boolean) throws java.io.IOException;
@@ -14107,6 +14107,7 @@ package android.graphics {
method public void endRecording();
method public int getHeight();
method public int getWidth();
+ method public boolean requiresHardwareAcceleration();
method public deprecated void writeToStream(java.io.OutputStream);
}
@@ -14221,22 +14222,22 @@ package android.graphics {
ctor public Rect();
ctor public Rect(int, int, int, int);
ctor public Rect(android.graphics.Rect);
- method public final int centerX();
- method public final int centerY();
+ method public int centerX();
+ method public int centerY();
method public boolean contains(int, int);
method public boolean contains(int, int, int, int);
method public boolean contains(android.graphics.Rect);
method public int describeContents();
- method public final float exactCenterX();
- method public final float exactCenterY();
+ method public float exactCenterX();
+ method public float exactCenterY();
method public java.lang.String flattenToString();
- method public final int height();
+ method public int height();
method public void inset(int, int);
method public boolean intersect(int, int, int, int);
method public boolean intersect(android.graphics.Rect);
method public boolean intersects(int, int, int, int);
method public static boolean intersects(android.graphics.Rect, android.graphics.Rect);
- method public final boolean isEmpty();
+ method public boolean isEmpty();
method public void offset(int, int);
method public void offsetTo(int, int);
method public void readFromParcel(android.os.Parcel);
@@ -14250,7 +14251,7 @@ package android.graphics {
method public void union(int, int, int, int);
method public void union(android.graphics.Rect);
method public void union(int, int);
- method public final int width();
+ method public int width();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.Rect> CREATOR;
field public int bottom;
@@ -15819,9 +15820,7 @@ package android.hardware.camera2 {
}
public static final class CameraCharacteristics.Key<T> {
- method public final boolean equals(java.lang.Object);
method public java.lang.String getName();
- method public final int hashCode();
}
public abstract class CameraConstrainedHighSpeedCaptureSession extends android.hardware.camera2.CameraCaptureSession {
@@ -16183,9 +16182,7 @@ package android.hardware.camera2 {
}
public static final class CaptureRequest.Key<T> {
- method public final boolean equals(java.lang.Object);
method public java.lang.String getName();
- method public final int hashCode();
}
public class CaptureResult extends android.hardware.camera2.CameraMetadata {
@@ -16278,9 +16275,7 @@ package android.hardware.camera2 {
}
public static final class CaptureResult.Key<T> {
- method public final boolean equals(java.lang.Object);
method public java.lang.String getName();
- method public final int hashCode();
}
public final class DngCreator implements java.lang.AutoCloseable {
@@ -16392,7 +16387,7 @@ package android.hardware.camera2.params {
method public float getComponent(int);
method public float getGreenEven();
method public float getGreenOdd();
- method public final float getRed();
+ method public float getRed();
field public static final int BLUE = 3; // 0x3
field public static final int COUNT = 4; // 0x4
field public static final int GREEN_EVEN = 1; // 0x1
@@ -16420,16 +16415,16 @@ package android.hardware.camera2.params {
method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRangesFor(android.util.Size);
method public android.util.Size[] getHighSpeedVideoSizes();
method public android.util.Size[] getHighSpeedVideoSizesFor(android.util.Range<java.lang.Integer>);
- method public final int[] getInputFormats();
+ method public int[] getInputFormats();
method public android.util.Size[] getInputSizes(int);
- method public final int[] getOutputFormats();
+ method public int[] getOutputFormats();
method public long getOutputMinFrameDuration(int, android.util.Size);
method public <T> long getOutputMinFrameDuration(java.lang.Class<T>, android.util.Size);
method public <T> android.util.Size[] getOutputSizes(java.lang.Class<T>);
method public android.util.Size[] getOutputSizes(int);
method public long getOutputStallDuration(int, android.util.Size);
method public <T> long getOutputStallDuration(java.lang.Class<T>, android.util.Size);
- method public final int[] getValidOutputFormatsForInput(int);
+ method public int[] getValidOutputFormatsForInput(int);
method public boolean isOutputSupportedFor(int);
method public static <T> boolean isOutputSupportedFor(java.lang.Class<T>);
method public boolean isOutputSupportedFor(android.view.Surface);
@@ -16494,8 +16489,37 @@ package android.hardware.display {
package android.hardware.fingerprint {
public class FingerprintDialog {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
- method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
+ method public void authenticate(android.hardware.fingerprint.FingerprintDialog.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
+ method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
+ field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+ field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
+ field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+ field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
+ field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
+ field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
+ field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
+ field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
+ field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
+ field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+ field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
+ field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
+ }
+
+ public static abstract class FingerprintDialog.AuthenticationCallback {
+ ctor public FingerprintDialog.AuthenticationCallback();
+ method public void onAuthenticationError(int, java.lang.CharSequence);
+ method public void onAuthenticationFailed();
+ method public void onAuthenticationHelp(int, java.lang.CharSequence);
+ method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintDialog.AuthenticationResult);
+ }
+
+ public static class FingerprintDialog.AuthenticationResult {
+ method public android.hardware.fingerprint.FingerprintDialog.CryptoObject getCryptoObject();
}
public static class FingerprintDialog.Builder {
@@ -16507,10 +16531,19 @@ package android.hardware.fingerprint {
method public android.hardware.fingerprint.FingerprintDialog.Builder setTitle(java.lang.CharSequence);
}
- public class FingerprintManager {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
- method public boolean hasEnrolledFingerprints();
- method public boolean isHardwareDetected();
+ public static final class FingerprintDialog.CryptoObject {
+ ctor public FingerprintDialog.CryptoObject(java.security.Signature);
+ ctor public FingerprintDialog.CryptoObject(javax.crypto.Cipher);
+ ctor public FingerprintDialog.CryptoObject(javax.crypto.Mac);
+ method public javax.crypto.Cipher getCipher();
+ method public javax.crypto.Mac getMac();
+ method public java.security.Signature getSignature();
+ }
+
+ public deprecated class FingerprintManager {
+ method public deprecated void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
+ method public deprecated boolean hasEnrolledFingerprints();
+ method public deprecated boolean isHardwareDetected();
field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -16518,9 +16551,11 @@ package android.hardware.fingerprint {
field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
@@ -16737,12 +16772,12 @@ package android.icu.lang {
public final class UCharacter implements android.icu.lang.UCharacterEnums.ECharacterCategory android.icu.lang.UCharacterEnums.ECharacterDirection {
method public static int charCount(int);
- method public static final int codePointAt(java.lang.CharSequence, int);
- method public static final int codePointAt(char[], int);
- method public static final int codePointAt(char[], int, int);
- method public static final int codePointBefore(java.lang.CharSequence, int);
- method public static final int codePointBefore(char[], int);
- method public static final int codePointBefore(char[], int, int);
+ method public static int codePointAt(java.lang.CharSequence, int);
+ method public static int codePointAt(char[], int);
+ method public static int codePointAt(char[], int, int);
+ method public static int codePointBefore(java.lang.CharSequence, int);
+ method public static int codePointBefore(char[], int);
+ method public static int codePointBefore(char[], int, int);
method public static int codePointCount(java.lang.CharSequence, int, int);
method public static int codePointCount(char[], int, int);
method public static int digit(int, int);
@@ -16750,7 +16785,7 @@ package android.icu.lang {
method public static int foldCase(int, boolean);
method public static java.lang.String foldCase(java.lang.String, boolean);
method public static int foldCase(int, int);
- method public static final java.lang.String foldCase(java.lang.String, int);
+ method public static java.lang.String foldCase(java.lang.String, int);
method public static char forDigit(int, int);
method public static android.icu.util.VersionInfo getAge(int);
method public static int getBidiPairedBracket(int);
@@ -16802,8 +16837,8 @@ package android.icu.lang {
method public static boolean isPrintable(int);
method public static boolean isSpaceChar(int);
method public static boolean isSupplementary(int);
- method public static final boolean isSupplementaryCodePoint(int);
- method public static final boolean isSurrogatePair(char, char);
+ method public static boolean isSupplementaryCodePoint(int);
+ method public static boolean isSurrogatePair(char, char);
method public static boolean isTitleCase(int);
method public static boolean isUAlphabetic(int);
method public static boolean isULowercase(int);
@@ -16812,13 +16847,13 @@ package android.icu.lang {
method public static boolean isUnicodeIdentifierPart(int);
method public static boolean isUnicodeIdentifierStart(int);
method public static boolean isUpperCase(int);
- method public static final boolean isValidCodePoint(int);
+ method public static boolean isValidCodePoint(int);
method public static boolean isWhitespace(int);
method public static int offsetByCodePoints(java.lang.CharSequence, int, int);
method public static int offsetByCodePoints(char[], int, int, int, int);
- method public static final int toChars(int, char[], int);
- method public static final char[] toChars(int);
- method public static final int toCodePoint(char, char);
+ method public static int toChars(int, char[], int);
+ method public static char[] toChars(int);
+ method public static int toCodePoint(char, char);
method public static int toLowerCase(int);
method public static java.lang.String toLowerCase(java.lang.String);
method public static java.lang.String toLowerCase(java.util.Locale, java.lang.String);
@@ -17108,7 +17143,7 @@ package android.icu.lang {
}
public static final class UCharacter.UnicodeBlock extends java.lang.Character.Subset {
- method public static final android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String);
+ method public static android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String);
method public int getID();
method public static android.icu.lang.UCharacter.UnicodeBlock getInstance(int);
method public static android.icu.lang.UCharacter.UnicodeBlock of(int);
@@ -17915,20 +17950,20 @@ package android.icu.lang {
}
public final class UScript {
- method public static final boolean breaksBetweenLetters(int);
- method public static final int[] getCode(java.util.Locale);
- method public static final int[] getCode(android.icu.util.ULocale);
- method public static final int[] getCode(java.lang.String);
- method public static final int getCodeFromName(java.lang.String);
- method public static final java.lang.String getName(int);
- method public static final java.lang.String getSampleString(int);
- method public static final int getScript(int);
- method public static final int getScriptExtensions(int, java.util.BitSet);
- method public static final java.lang.String getShortName(int);
- method public static final android.icu.lang.UScript.ScriptUsage getUsage(int);
- method public static final boolean hasScript(int, int);
- method public static final boolean isCased(int);
- method public static final boolean isRightToLeft(int);
+ method public static boolean breaksBetweenLetters(int);
+ method public static int[] getCode(java.util.Locale);
+ method public static int[] getCode(android.icu.util.ULocale);
+ method public static int[] getCode(java.lang.String);
+ method public static int getCodeFromName(java.lang.String);
+ method public static java.lang.String getName(int);
+ method public static java.lang.String getSampleString(int);
+ method public static int getScript(int);
+ method public static int getScriptExtensions(int, java.util.BitSet);
+ method public static java.lang.String getShortName(int);
+ method public static android.icu.lang.UScript.ScriptUsage getUsage(int);
+ method public static boolean hasScript(int, int);
+ method public static boolean isCased(int);
+ method public static boolean isRightToLeft(int);
field public static final int ADLAM = 167; // 0xa7
field public static final int AFAKA = 147; // 0x93
field public static final int AHOM = 161; // 0xa1
@@ -18343,14 +18378,14 @@ package android.icu.text {
method public deprecated int hashCode();
method public int next();
method public int previous();
- method public static final int primaryOrder(int);
+ method public static int primaryOrder(int);
method public void reset();
- method public static final int secondaryOrder(int);
+ method public static int secondaryOrder(int);
method public void setOffset(int);
method public void setText(java.lang.String);
method public void setText(android.icu.text.UCharacterIterator);
method public void setText(java.text.CharacterIterator);
- method public static final int tertiaryOrder(int);
+ method public static int tertiaryOrder(int);
field public static final int IGNORABLE = 0; // 0x0
field public static final int NULLORDER = -1; // 0xffffffff
}
@@ -19577,7 +19612,7 @@ package android.icu.text {
method public boolean isUpperCaseFirst();
method public void setAlternateHandlingDefault();
method public void setAlternateHandlingShifted(boolean);
- method public final void setCaseFirstDefault();
+ method public void setCaseFirstDefault();
method public void setCaseLevel(boolean);
method public void setCaseLevelDefault();
method public void setDecompositionDefault();
@@ -20550,11 +20585,11 @@ package android.icu.util {
public final class LocaleData {
method public static android.icu.util.VersionInfo getCLDRVersion();
method public java.lang.String getDelimiter(int);
- method public static final android.icu.util.LocaleData getInstance(android.icu.util.ULocale);
- method public static final android.icu.util.LocaleData getInstance();
- method public static final android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale);
+ method public static android.icu.util.LocaleData getInstance(android.icu.util.ULocale);
+ method public static android.icu.util.LocaleData getInstance();
+ method public static android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale);
method public boolean getNoSubstitute();
- method public static final android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale);
+ method public static android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale);
method public void setNoSubstitute(boolean);
field public static final int ALT_QUOTATION_END = 3; // 0x3
field public static final int ALT_QUOTATION_START = 2; // 0x2
@@ -21954,6 +21989,7 @@ package android.media {
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
field public static final int ENCODING_E_AC3 = 6; // 0x6
+ field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
@@ -21984,6 +22020,7 @@ package android.media {
method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
+ method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
method public java.lang.String getProperty(java.lang.String);
@@ -22157,11 +22194,26 @@ package android.media {
field public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
+ public final class AudioPresentation {
+ method public java.util.Map<java.util.Locale, java.lang.String> getLabels();
+ method public java.util.Locale getLocale();
+ method public int getMasteringIndication();
+ method public boolean hasAudioDescription();
+ method public boolean hasDialogueEnhancement();
+ method public boolean hasSpokenSubtitles();
+ field public static final int MASTERED_FOR_3D = 3; // 0x3
+ field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
+ field public static final int MASTERED_FOR_STEREO = 1; // 0x1
+ field public static final int MASTERED_FOR_SURROUND = 2; // 0x2
+ field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
+ }
+
public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
method protected void finalize();
+ method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -22322,6 +22374,7 @@ package android.media {
method public int setPlaybackRate(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
+ method public int setPresentation(android.media.AudioPresentation);
method protected deprecated void setState(int);
method public deprecated int setStereoVolume(float, float);
method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
@@ -22826,40 +22879,40 @@ package android.media {
method public static android.media.MediaCodec createByCodecName(java.lang.String) throws java.io.IOException;
method public static android.media.MediaCodec createDecoderByType(java.lang.String) throws java.io.IOException;
method public static android.media.MediaCodec createEncoderByType(java.lang.String) throws java.io.IOException;
- method public final android.view.Surface createInputSurface();
+ method public android.view.Surface createInputSurface();
method public static android.view.Surface createPersistentInputSurface();
- method public final int dequeueInputBuffer(long);
- method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
+ method public int dequeueInputBuffer(long);
+ method public int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
method protected void finalize();
- method public final void flush();
+ method public void flush();
method public android.media.MediaCodecInfo getCodecInfo();
method public java.nio.ByteBuffer getInputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
- method public final android.media.MediaFormat getInputFormat();
+ method public android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
method public android.os.PersistableBundle getMetrics();
- method public final java.lang.String getName();
+ method public java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
- method public final android.media.MediaFormat getOutputFormat();
- method public final android.media.MediaFormat getOutputFormat(int);
+ method public android.media.MediaFormat getOutputFormat();
+ method public android.media.MediaFormat getOutputFormat(int);
method public android.media.Image getOutputImage(int);
- method public final void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
- method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
- method public final void release();
- method public final void releaseOutputBuffer(int, boolean);
- method public final void releaseOutputBuffer(int, long);
- method public final void reset();
+ method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
+ method public void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
+ method public void release();
+ method public void releaseOutputBuffer(int, boolean);
+ method public void releaseOutputBuffer(int, long);
+ method public void reset();
method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler);
method public void setCallback(android.media.MediaCodec.Callback);
method public void setInputSurface(android.view.Surface);
method public void setOnFrameRenderedListener(android.media.MediaCodec.OnFrameRenderedListener, android.os.Handler);
method public void setOutputSurface(android.view.Surface);
- method public final void setParameters(android.os.Bundle);
- method public final void setVideoScalingMode(int);
- method public final void signalEndOfInputStream();
- method public final void start();
- method public final void stop();
+ method public void setParameters(android.os.Bundle);
+ method public void setVideoScalingMode(int);
+ method public void signalEndOfInputStream();
+ method public void start();
+ method public void stop();
field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
@@ -22953,10 +23006,10 @@ package android.media {
}
public final class MediaCodecInfo {
- method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
- method public final java.lang.String getName();
- method public final java.lang.String[] getSupportedTypes();
- method public final boolean isEncoder();
+ method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
+ method public java.lang.String getName();
+ method public java.lang.String[] getSupportedTypes();
+ method public boolean isEncoder();
}
public static final class MediaCodecInfo.AudioCapabilities {
@@ -22976,9 +23029,9 @@ package android.media {
method public int getMaxSupportedInstances();
method public java.lang.String getMimeType();
method public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
- method public final boolean isFeatureRequired(java.lang.String);
- method public final boolean isFeatureSupported(java.lang.String);
- method public final boolean isFormatSupported(android.media.MediaFormat);
+ method public boolean isFeatureRequired(java.lang.String);
+ method public boolean isFeatureSupported(java.lang.String);
+ method public boolean isFormatSupported(android.media.MediaFormat);
field public static final deprecated int COLOR_Format12bitRGB444 = 3; // 0x3
field public static final deprecated int COLOR_Format16bitARGB1555 = 5; // 0x5
field public static final deprecated int COLOR_Format16bitARGB4444 = 4; // 0x4
@@ -23236,11 +23289,11 @@ package android.media {
public final class MediaCodecList {
ctor public MediaCodecList(int);
- method public final java.lang.String findDecoderForFormat(android.media.MediaFormat);
- method public final java.lang.String findEncoderForFormat(android.media.MediaFormat);
- method public static final deprecated int getCodecCount();
- method public static final deprecated android.media.MediaCodecInfo getCodecInfoAt(int);
- method public final android.media.MediaCodecInfo[] getCodecInfos();
+ method public java.lang.String findDecoderForFormat(android.media.MediaFormat);
+ method public java.lang.String findEncoderForFormat(android.media.MediaFormat);
+ method public static deprecated int getCodecCount();
+ method public static deprecated android.media.MediaCodecInfo getCodecInfoAt(int);
+ method public android.media.MediaCodecInfo[] getCodecInfos();
field public static final int ALL_CODECS = 1; // 0x1
field public static final int REGULAR_CODECS = 0; // 0x0
}
@@ -23248,10 +23301,10 @@ package android.media {
public final class MediaCrypto {
ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
method protected void finalize();
- method public static final boolean isCryptoSchemeSupported(java.util.UUID);
- method public final void release();
- method public final boolean requiresSecureDecoderComponent(java.lang.String);
- method public final void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
+ method public static boolean isCryptoSchemeSupported(java.util.UUID);
+ method public void release();
+ method public boolean requiresSecureDecoderComponent(java.lang.String);
+ method public void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
}
public final class MediaCryptoException extends java.lang.Exception {
@@ -23267,10 +23320,10 @@ package android.media {
public final class MediaDescrambler implements java.lang.AutoCloseable {
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
- method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
+ method public int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
method protected void finalize();
- method public final boolean requiresSecureDecoderComponent(java.lang.String);
- method public final void setMediaCasSession(android.media.MediaCas.Session);
+ method public boolean requiresSecureDecoderComponent(java.lang.String);
+ method public void setMediaCasSession(android.media.MediaCas.Session);
}
public class MediaDescription implements android.os.Parcelable {
@@ -23326,8 +23379,8 @@ package android.media {
method public java.util.List<byte[]> getSecureStopIds();
method public java.util.List<byte[]> getSecureStops();
method public int getSecurityLevel(byte[]);
- method public static final boolean isCryptoSchemeSupported(java.util.UUID);
- method public static final boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
+ method public static boolean isCryptoSchemeSupported(java.util.UUID);
+ method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
@@ -23478,6 +23531,7 @@ package android.media {
ctor public MediaExtractor();
method public boolean advance();
method protected void finalize();
+ method public java.util.List<android.media.AudioPresentation> getAudioPresentations(int);
method public long getCachedDuration();
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
@@ -23488,21 +23542,21 @@ package android.media {
method public long getSampleSize();
method public long getSampleTime();
method public int getSampleTrackIndex();
- method public final int getTrackCount();
+ method public int getTrackCount();
method public android.media.MediaFormat getTrackFormat(int);
method public boolean hasCacheReachedEndOfStream();
method public int readSampleData(java.nio.ByteBuffer, int);
- method public final void release();
+ method public void release();
method public void seekTo(long, int);
method public void selectTrack(int);
- method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
- method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
- method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
- method public final void setDataSource(java.lang.String) throws java.io.IOException;
- method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
- method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
- method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
- method public final void setMediaCas(android.media.MediaCas);
+ method public void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
+ method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+ method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+ method public void setDataSource(java.lang.String) throws java.io.IOException;
+ method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+ method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
+ method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
+ method public void setMediaCas(android.media.MediaCas);
method public void unselectTrack(int);
field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
field public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; // 0x4
@@ -23525,22 +23579,22 @@ package android.media {
public final class MediaFormat {
ctor public MediaFormat();
- method public final boolean containsKey(java.lang.String);
- method public static final android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
- method public static final android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
- method public static final android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
- method public final java.nio.ByteBuffer getByteBuffer(java.lang.String);
+ method public boolean containsKey(java.lang.String);
+ method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
+ method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
+ method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
+ method public java.nio.ByteBuffer getByteBuffer(java.lang.String);
method public boolean getFeatureEnabled(java.lang.String);
- method public final float getFloat(java.lang.String);
- method public final int getInteger(java.lang.String);
- method public final long getLong(java.lang.String);
- method public final java.lang.String getString(java.lang.String);
- method public final void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
+ method public float getFloat(java.lang.String);
+ method public int getInteger(java.lang.String);
+ method public long getLong(java.lang.String);
+ method public java.lang.String getString(java.lang.String);
+ method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
method public void setFeatureEnabled(java.lang.String, boolean);
- method public final void setFloat(java.lang.String, float);
- method public final void setInteger(java.lang.String, int);
- method public final void setLong(java.lang.String, long);
- method public final void setString(java.lang.String, java.lang.String);
+ method public void setFloat(java.lang.String, float);
+ method public void setInteger(java.lang.String, int);
+ method public void setLong(java.lang.String, long);
+ method public void setString(java.lang.String, java.lang.String);
field public static final int COLOR_RANGE_FULL = 1; // 0x1
field public static final int COLOR_RANGE_LIMITED = 2; // 0x2
field public static final int COLOR_STANDARD_BT2020 = 6; // 0x6
@@ -24166,6 +24220,7 @@ package android.media {
ctor public MediaRecorder();
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method protected void finalize();
+ method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
method public android.os.PersistableBundle getMetrics();
@@ -24439,14 +24494,14 @@ package android.media {
public final class MediaSync {
ctor public MediaSync();
- method public final android.view.Surface createInputSurface();
+ method public android.view.Surface createInputSurface();
method protected void finalize();
method public void flush();
method public android.media.PlaybackParams getPlaybackParams();
method public android.media.SyncParams getSyncParams();
method public android.media.MediaTimestamp getTimestamp();
method public void queueAudio(java.nio.ByteBuffer, int, long);
- method public final void release();
+ method public void release();
method public void setAudioTrack(android.media.AudioTrack);
method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler);
@@ -24481,6 +24536,41 @@ package android.media {
method public float getMediaClockRate();
}
+ public final class MicrophoneInfo {
+ method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getChannelMapping();
+ method public java.lang.String getDescription();
+ method public int getDirectionality();
+ method public java.util.List<android.util.Pair<java.lang.Float, java.lang.Float>> getFrequencyResponse();
+ method public int getGroup();
+ method public int getId();
+ method public int getIndexInTheGroup();
+ method public int getLocation();
+ method public float getMaxSpl();
+ method public float getMinSpl();
+ method public android.media.MicrophoneInfo.Coordinate3F getOrientation();
+ method public android.media.MicrophoneInfo.Coordinate3F getPosition();
+ method public float getSensitivity();
+ method public int getType();
+ field public static final int CHANNEL_MAPPING_DIRECT = 1; // 0x1
+ field public static final int CHANNEL_MAPPING_PROCESSED = 2; // 0x2
+ field public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2; // 0x2
+ field public static final int DIRECTIONALITY_CARDIOID = 3; // 0x3
+ field public static final int DIRECTIONALITY_HYPER_CARDIOID = 4; // 0x4
+ field public static final int DIRECTIONALITY_OMNI = 1; // 0x1
+ field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5
+ field public static final int DIRECTIONALITY_UNKNOWN = 0; // 0x0
+ field public static final int LOCATION_MAINBODY = 1; // 0x1
+ field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2
+ field public static final int LOCATION_PERIPHERAL = 3; // 0x3
+ field public static final int LOCATION_UNKNOWN = 0; // 0x0
+ }
+
+ public class MicrophoneInfo.Coordinate3F {
+ field public final float x;
+ field public final float y;
+ field public final float z;
+ }
+
public final class NotProvisionedException extends android.media.MediaDrmException {
ctor public NotProvisionedException(java.lang.String);
}
@@ -25401,7 +25491,7 @@ package android.media.midi {
public final class MidiInputPort extends android.media.midi.MidiReceiver implements java.io.Closeable {
method public void close() throws java.io.IOException;
- method public final int getPortNumber();
+ method public int getPortNumber();
method public void onSend(byte[], int, int, long) throws java.io.IOException;
}
@@ -25426,7 +25516,7 @@ package android.media.midi {
public final class MidiOutputPort extends android.media.midi.MidiSender implements java.io.Closeable {
method public void close() throws java.io.IOException;
- method public final int getPortNumber();
+ method public int getPortNumber();
method public void onConnect(android.media.midi.MidiReceiver);
method public void onDisconnect(android.media.midi.MidiReceiver);
}
@@ -25701,7 +25791,7 @@ package android.media.session {
package android.media.tv {
public final class TvContentRating {
- method public final boolean contains(android.media.tv.TvContentRating);
+ method public boolean contains(android.media.tv.TvContentRating);
method public static android.media.tv.TvContentRating createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...);
method public java.lang.String flattenToString();
method public java.lang.String getDomain();
@@ -25751,7 +25841,7 @@ package android.media.tv {
}
public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
- method public static final java.lang.String getVideoResolution(java.lang.String);
+ method public static java.lang.String getVideoResolution(java.lang.String);
field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
@@ -26276,18 +26366,18 @@ package android.media.tv {
public final class TvTrackInfo implements android.os.Parcelable {
method public int describeContents();
- method public final int getAudioChannelCount();
- method public final int getAudioSampleRate();
- method public final java.lang.CharSequence getDescription();
- method public final android.os.Bundle getExtra();
- method public final java.lang.String getId();
- method public final java.lang.String getLanguage();
- method public final int getType();
- method public final byte getVideoActiveFormatDescription();
- method public final float getVideoFrameRate();
- method public final int getVideoHeight();
- method public final float getVideoPixelAspectRatio();
- method public final int getVideoWidth();
+ method public int getAudioChannelCount();
+ method public int getAudioSampleRate();
+ method public java.lang.CharSequence getDescription();
+ method public android.os.Bundle getExtra();
+ method public java.lang.String getId();
+ method public java.lang.String getLanguage();
+ method public int getType();
+ method public byte getVideoActiveFormatDescription();
+ method public float getVideoFrameRate();
+ method public int getVideoHeight();
+ method public float getVideoPixelAspectRatio();
+ method public int getVideoWidth();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR;
field public static final int TYPE_AUDIO = 0; // 0x0
@@ -26298,16 +26388,16 @@ package android.media.tv {
public static final class TvTrackInfo.Builder {
ctor public TvTrackInfo.Builder(int, java.lang.String);
method public android.media.tv.TvTrackInfo build();
- method public final android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
- method public final android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
- method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
- method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
- method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
- method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
- method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
- method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
- method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
- method public final android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
+ method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
+ method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
+ method public android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
+ method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
+ method public android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
+ method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
+ method public android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
+ method public android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
+ method public android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
}
public class TvView extends android.view.ViewGroup {
@@ -26538,34 +26628,34 @@ package android.mtp {
}
public final class MtpObjectInfo {
- method public final int getAssociationDesc();
- method public final int getAssociationType();
- method public final int getCompressedSize();
- method public final long getCompressedSizeLong();
- method public final long getDateCreated();
- method public final long getDateModified();
- method public final int getFormat();
- method public final int getImagePixDepth();
- method public final long getImagePixDepthLong();
- method public final int getImagePixHeight();
- method public final long getImagePixHeightLong();
- method public final int getImagePixWidth();
- method public final long getImagePixWidthLong();
- method public final java.lang.String getKeywords();
- method public final java.lang.String getName();
- method public final int getObjectHandle();
- method public final int getParent();
- method public final int getProtectionStatus();
- method public final int getSequenceNumber();
- method public final long getSequenceNumberLong();
- method public final int getStorageId();
- method public final int getThumbCompressedSize();
- method public final long getThumbCompressedSizeLong();
- method public final int getThumbFormat();
- method public final int getThumbPixHeight();
- method public final long getThumbPixHeightLong();
- method public final int getThumbPixWidth();
- method public final long getThumbPixWidthLong();
+ method public int getAssociationDesc();
+ method public int getAssociationType();
+ method public int getCompressedSize();
+ method public long getCompressedSizeLong();
+ method public long getDateCreated();
+ method public long getDateModified();
+ method public int getFormat();
+ method public int getImagePixDepth();
+ method public long getImagePixDepthLong();
+ method public int getImagePixHeight();
+ method public long getImagePixHeightLong();
+ method public int getImagePixWidth();
+ method public long getImagePixWidthLong();
+ method public java.lang.String getKeywords();
+ method public java.lang.String getName();
+ method public int getObjectHandle();
+ method public int getParent();
+ method public int getProtectionStatus();
+ method public int getSequenceNumber();
+ method public long getSequenceNumberLong();
+ method public int getStorageId();
+ method public int getThumbCompressedSize();
+ method public long getThumbCompressedSizeLong();
+ method public int getThumbFormat();
+ method public int getThumbPixHeight();
+ method public long getThumbPixHeightLong();
+ method public int getThumbPixWidth();
+ method public long getThumbPixWidthLong();
}
public static class MtpObjectInfo.Builder {
@@ -26595,11 +26685,11 @@ package android.mtp {
}
public final class MtpStorageInfo {
- method public final java.lang.String getDescription();
- method public final long getFreeSpace();
- method public final long getMaxCapacity();
- method public final int getStorageId();
- method public final java.lang.String getVolumeIdentifier();
+ method public java.lang.String getDescription();
+ method public long getFreeSpace();
+ method public long getMaxCapacity();
+ method public int getStorageId();
+ method public java.lang.String getVolumeIdentifier();
}
}
@@ -27032,10 +27122,10 @@ package android.net {
public final class Proxy {
ctor public Proxy();
- method public static final deprecated java.lang.String getDefaultHost();
- method public static final deprecated int getDefaultPort();
- method public static final deprecated java.lang.String getHost(android.content.Context);
- method public static final deprecated int getPort(android.content.Context);
+ method public static deprecated java.lang.String getDefaultHost();
+ method public static deprecated int getDefaultPort();
+ method public static deprecated java.lang.String getHost(android.content.Context);
+ method public static deprecated int getPort(android.content.Context);
field public static final deprecated java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
}
@@ -32062,9 +32152,9 @@ package android.os {
method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
method public static void enableEmulatorTraceOutput();
- method public static final int getBinderDeathObjectCount();
- method public static final int getBinderLocalObjectCount();
- method public static final int getBinderProxyObjectCount();
+ method public static int getBinderDeathObjectCount();
+ method public static int getBinderLocalObjectCount();
+ method public static int getBinderProxyObjectCount();
method public static int getBinderReceivedTransactions();
method public static int getBinderSentTransactions();
method public static deprecated int getGlobalAllocCount();
@@ -32473,114 +32563,114 @@ package android.os {
}
public final class Parcel {
- method public final void appendFrom(android.os.Parcel, int, int);
- method public final android.os.IBinder[] createBinderArray();
- method public final java.util.ArrayList<android.os.IBinder> createBinderArrayList();
- method public final boolean[] createBooleanArray();
- method public final byte[] createByteArray();
- method public final char[] createCharArray();
- method public final double[] createDoubleArray();
- method public final float[] createFloatArray();
- method public final int[] createIntArray();
- method public final long[] createLongArray();
- method public final java.lang.String[] createStringArray();
- method public final java.util.ArrayList<java.lang.String> createStringArrayList();
- method public final <T> T[] createTypedArray(android.os.Parcelable.Creator<T>);
- method public final <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>);
- method public final int dataAvail();
- method public final int dataCapacity();
- method public final int dataPosition();
- method public final int dataSize();
- method public final void enforceInterface(java.lang.String);
- method public final boolean hasFileDescriptors();
- method public final byte[] marshall();
+ method public void appendFrom(android.os.Parcel, int, int);
+ method public android.os.IBinder[] createBinderArray();
+ method public java.util.ArrayList<android.os.IBinder> createBinderArrayList();
+ method public boolean[] createBooleanArray();
+ method public byte[] createByteArray();
+ method public char[] createCharArray();
+ method public double[] createDoubleArray();
+ method public float[] createFloatArray();
+ method public int[] createIntArray();
+ method public long[] createLongArray();
+ method public java.lang.String[] createStringArray();
+ method public java.util.ArrayList<java.lang.String> createStringArrayList();
+ method public <T> T[] createTypedArray(android.os.Parcelable.Creator<T>);
+ method public <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>);
+ method public int dataAvail();
+ method public int dataCapacity();
+ method public int dataPosition();
+ method public int dataSize();
+ method public void enforceInterface(java.lang.String);
+ method public boolean hasFileDescriptors();
+ method public byte[] marshall();
method public static android.os.Parcel obtain();
- method public final java.lang.Object[] readArray(java.lang.ClassLoader);
- method public final java.util.ArrayList readArrayList(java.lang.ClassLoader);
- method public final void readBinderArray(android.os.IBinder[]);
- method public final void readBinderList(java.util.List<android.os.IBinder>);
- method public final void readBooleanArray(boolean[]);
- method public final android.os.Bundle readBundle();
- method public final android.os.Bundle readBundle(java.lang.ClassLoader);
- method public final byte readByte();
- method public final void readByteArray(byte[]);
- method public final void readCharArray(char[]);
- method public final double readDouble();
- method public final void readDoubleArray(double[]);
- method public final void readException();
- method public final void readException(int, java.lang.String);
- method public final android.os.ParcelFileDescriptor readFileDescriptor();
- method public final float readFloat();
- method public final void readFloatArray(float[]);
- method public final java.util.HashMap readHashMap(java.lang.ClassLoader);
- method public final int readInt();
- method public final void readIntArray(int[]);
- method public final void readList(java.util.List, java.lang.ClassLoader);
- method public final long readLong();
- method public final void readLongArray(long[]);
- method public final void readMap(java.util.Map, java.lang.ClassLoader);
- method public final <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
- method public final android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
- method public final android.os.PersistableBundle readPersistableBundle();
- method public final android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
- method public final java.io.Serializable readSerializable();
- method public final android.util.Size readSize();
- method public final android.util.SizeF readSizeF();
- method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
- method public final android.util.SparseBooleanArray readSparseBooleanArray();
- method public final java.lang.String readString();
- method public final void readStringArray(java.lang.String[]);
- method public final void readStringList(java.util.List<java.lang.String>);
- method public final android.os.IBinder readStrongBinder();
- method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
- method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
- method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>);
- method public final java.lang.Object readValue(java.lang.ClassLoader);
- method public final void recycle();
- method public final void setDataCapacity(int);
- method public final void setDataPosition(int);
- method public final void setDataSize(int);
- method public final void unmarshall(byte[], int, int);
- method public final void writeArray(java.lang.Object[]);
- method public final void writeBinderArray(android.os.IBinder[]);
- method public final void writeBinderList(java.util.List<android.os.IBinder>);
- method public final void writeBooleanArray(boolean[]);
- method public final void writeBundle(android.os.Bundle);
- method public final void writeByte(byte);
- method public final void writeByteArray(byte[]);
- method public final void writeByteArray(byte[], int, int);
- method public final void writeCharArray(char[]);
- method public final void writeDouble(double);
- method public final void writeDoubleArray(double[]);
- method public final void writeException(java.lang.Exception);
- method public final void writeFileDescriptor(java.io.FileDescriptor);
- method public final void writeFloat(float);
- method public final void writeFloatArray(float[]);
- method public final void writeInt(int);
- method public final void writeIntArray(int[]);
- method public final void writeInterfaceToken(java.lang.String);
- method public final void writeList(java.util.List);
- method public final void writeLong(long);
- method public final void writeLongArray(long[]);
- method public final void writeMap(java.util.Map);
- method public final void writeNoException();
- method public final void writeParcelable(android.os.Parcelable, int);
- method public final <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
- method public final void writePersistableBundle(android.os.PersistableBundle);
- method public final void writeSerializable(java.io.Serializable);
- method public final void writeSize(android.util.Size);
- method public final void writeSizeF(android.util.SizeF);
- method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
- method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
- method public final void writeString(java.lang.String);
- method public final void writeStringArray(java.lang.String[]);
- method public final void writeStringList(java.util.List<java.lang.String>);
- method public final void writeStrongBinder(android.os.IBinder);
- method public final void writeStrongInterface(android.os.IInterface);
- method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int);
- method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
- method public final <T extends android.os.Parcelable> void writeTypedObject(T, int);
- method public final void writeValue(java.lang.Object);
+ method public java.lang.Object[] readArray(java.lang.ClassLoader);
+ method public java.util.ArrayList readArrayList(java.lang.ClassLoader);
+ method public void readBinderArray(android.os.IBinder[]);
+ method public void readBinderList(java.util.List<android.os.IBinder>);
+ method public void readBooleanArray(boolean[]);
+ method public android.os.Bundle readBundle();
+ method public android.os.Bundle readBundle(java.lang.ClassLoader);
+ method public byte readByte();
+ method public void readByteArray(byte[]);
+ method public void readCharArray(char[]);
+ method public double readDouble();
+ method public void readDoubleArray(double[]);
+ method public void readException();
+ method public void readException(int, java.lang.String);
+ method public android.os.ParcelFileDescriptor readFileDescriptor();
+ method public float readFloat();
+ method public void readFloatArray(float[]);
+ method public java.util.HashMap readHashMap(java.lang.ClassLoader);
+ method public int readInt();
+ method public void readIntArray(int[]);
+ method public void readList(java.util.List, java.lang.ClassLoader);
+ method public long readLong();
+ method public void readLongArray(long[]);
+ method public void readMap(java.util.Map, java.lang.ClassLoader);
+ method public <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
+ method public android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
+ method public android.os.PersistableBundle readPersistableBundle();
+ method public android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
+ method public java.io.Serializable readSerializable();
+ method public android.util.Size readSize();
+ method public android.util.SizeF readSizeF();
+ method public android.util.SparseArray readSparseArray(java.lang.ClassLoader);
+ method public android.util.SparseBooleanArray readSparseBooleanArray();
+ method public java.lang.String readString();
+ method public void readStringArray(java.lang.String[]);
+ method public void readStringList(java.util.List<java.lang.String>);
+ method public android.os.IBinder readStrongBinder();
+ method public <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
+ method public <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
+ method public <T> T readTypedObject(android.os.Parcelable.Creator<T>);
+ method public java.lang.Object readValue(java.lang.ClassLoader);
+ method public void recycle();
+ method public void setDataCapacity(int);
+ method public void setDataPosition(int);
+ method public void setDataSize(int);
+ method public void unmarshall(byte[], int, int);
+ method public void writeArray(java.lang.Object[]);
+ method public void writeBinderArray(android.os.IBinder[]);
+ method public void writeBinderList(java.util.List<android.os.IBinder>);
+ method public void writeBooleanArray(boolean[]);
+ method public void writeBundle(android.os.Bundle);
+ method public void writeByte(byte);
+ method public void writeByteArray(byte[]);
+ method public void writeByteArray(byte[], int, int);
+ method public void writeCharArray(char[]);
+ method public void writeDouble(double);
+ method public void writeDoubleArray(double[]);
+ method public void writeException(java.lang.Exception);
+ method public void writeFileDescriptor(java.io.FileDescriptor);
+ method public void writeFloat(float);
+ method public void writeFloatArray(float[]);
+ method public void writeInt(int);
+ method public void writeIntArray(int[]);
+ method public void writeInterfaceToken(java.lang.String);
+ method public void writeList(java.util.List);
+ method public void writeLong(long);
+ method public void writeLongArray(long[]);
+ method public void writeMap(java.util.Map);
+ method public void writeNoException();
+ method public void writeParcelable(android.os.Parcelable, int);
+ method public <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
+ method public void writePersistableBundle(android.os.PersistableBundle);
+ method public void writeSerializable(java.io.Serializable);
+ method public void writeSize(android.util.Size);
+ method public void writeSizeF(android.util.SizeF);
+ method public void writeSparseArray(android.util.SparseArray<java.lang.Object>);
+ method public void writeSparseBooleanArray(android.util.SparseBooleanArray);
+ method public void writeString(java.lang.String);
+ method public void writeStringArray(java.lang.String[]);
+ method public void writeStringList(java.util.List<java.lang.String>);
+ method public void writeStrongBinder(android.os.IBinder);
+ method public void writeStrongInterface(android.os.IInterface);
+ method public <T extends android.os.Parcelable> void writeTypedArray(T[], int);
+ method public <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
+ method public <T extends android.os.Parcelable> void writeTypedObject(T, int);
+ method public void writeValue(java.lang.Object);
field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
}
@@ -33025,7 +33115,7 @@ package android.os {
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
- field public static final java.lang.String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCATION = "no_config_location";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
@@ -34234,7 +34324,7 @@ package android.provider {
}
public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
field public static final android.net.Uri CONTENT_URI;
}
@@ -34363,7 +34453,7 @@ package android.provider {
}
public static final class CalendarContract.EventDays implements android.provider.CalendarContract.EventDaysColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]);
field public static final android.net.Uri CONTENT_URI;
}
@@ -34456,8 +34546,8 @@ package android.provider {
}
public static final class CalendarContract.Instances implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long);
- method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String);
+ method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long);
+ method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String);
field public static final java.lang.String BEGIN = "begin";
field public static final android.net.Uri CONTENT_BY_DAY_URI;
field public static final android.net.Uri CONTENT_SEARCH_BY_DAY_URI;
@@ -34472,7 +34562,7 @@ package android.provider {
}
public static final class CalendarContract.Reminders implements android.provider.BaseColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.RemindersColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
field public static final android.net.Uri CONTENT_URI;
}
@@ -34581,7 +34671,7 @@ package android.provider {
method public static deprecated java.lang.Object decodeImProtocol(java.lang.String);
method public static deprecated java.lang.String encodeCustomImProtocol(java.lang.String);
method public static deprecated java.lang.String encodePredefinedImProtocol(int);
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence);
field public static final deprecated java.lang.String CONTENT_EMAIL_ITEM_TYPE = "vnd.android.cursor.item/email";
field public static final deprecated java.lang.String CONTENT_EMAIL_TYPE = "vnd.android.cursor.dir/email";
field public static final deprecated android.net.Uri CONTENT_EMAIL_URI;
@@ -34731,7 +34821,7 @@ package android.provider {
}
public static final deprecated class Contacts.Organizations implements android.provider.BaseColumns android.provider.Contacts.OrganizationColumns {
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
field public static final deprecated java.lang.String CONTENT_DIRECTORY = "organizations";
field public static final deprecated android.net.Uri CONTENT_URI;
field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "company, title, isprimary ASC";
@@ -34788,8 +34878,8 @@ package android.provider {
}
public static final deprecated class Contacts.Phones implements android.provider.BaseColumns android.provider.Contacts.PeopleColumns android.provider.Contacts.PhonesColumns {
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]);
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
field public static final deprecated android.net.Uri CONTENT_FILTER_URL;
field public static final deprecated java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone";
field public static final deprecated java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone";
@@ -34929,8 +35019,8 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String ADDRESS = "data1";
field public static final android.net.Uri CONTENT_FILTER_URI;
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2";
@@ -34950,7 +35040,7 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.Event implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
method public static int getTypeResource(java.lang.Integer);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_event";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -34981,10 +35071,10 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getProtocolLabelResource(int);
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getProtocolLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
field public static final java.lang.String CUSTOM_PROTOCOL = "data6";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35029,8 +35119,8 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.Organization implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String COMPANY = "data1";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
field public static final java.lang.String DEPARTMENT = "data5";
@@ -35048,8 +35138,8 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final android.net.Uri CONTENT_FILTER_URI;
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone_v2";
@@ -35094,8 +35184,8 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -35118,8 +35208,8 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -35149,8 +35239,8 @@ package android.provider {
}
public static final class ContactsContract.CommonDataKinds.StructuredPostal implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CITY = "data7";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address_v2";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/postal-address_v2";
@@ -35977,7 +36067,7 @@ package android.provider {
public static final class MediaStore.Audio.Artists.Albums implements android.provider.MediaStore.Audio.AlbumColumns {
ctor public MediaStore.Audio.Artists.Albums();
- method public static final android.net.Uri getContentUri(java.lang.String, long);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
}
public static abstract interface MediaStore.Audio.AudioColumns implements android.provider.MediaStore.MediaColumns {
@@ -36013,7 +36103,7 @@ package android.provider {
public static final class MediaStore.Audio.Genres.Members implements android.provider.MediaStore.Audio.AudioColumns {
ctor public MediaStore.Audio.Genres.Members();
- method public static final android.net.Uri getContentUri(java.lang.String, long);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
field public static final java.lang.String AUDIO_ID = "audio_id";
field public static final java.lang.String CONTENT_DIRECTORY = "members";
field public static final java.lang.String DEFAULT_SORT_ORDER = "title_key";
@@ -36049,8 +36139,8 @@ package android.provider {
public static final class MediaStore.Audio.Playlists.Members implements android.provider.MediaStore.Audio.AudioColumns {
ctor public MediaStore.Audio.Playlists.Members();
- method public static final android.net.Uri getContentUri(java.lang.String, long);
- method public static final boolean moveItem(android.content.ContentResolver, long, int, int);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
+ method public static boolean moveItem(android.content.ContentResolver, long, int, int);
field public static final java.lang.String AUDIO_ID = "audio_id";
field public static final java.lang.String CONTENT_DIRECTORY = "members";
field public static final java.lang.String DEFAULT_SORT_ORDER = "play_order";
@@ -36073,7 +36163,7 @@ package android.provider {
public static final class MediaStore.Files {
ctor public MediaStore.Files();
method public static android.net.Uri getContentUri(java.lang.String);
- method public static final android.net.Uri getContentUri(java.lang.String, long);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
}
public static abstract interface MediaStore.Files.FileColumns implements android.provider.MediaStore.MediaColumns {
@@ -36107,13 +36197,13 @@ package android.provider {
public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
ctor public MediaStore.Images.Media();
- method public static final android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
+ method public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
method public static android.net.Uri getContentUri(java.lang.String);
- method public static final java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
- method public static final java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String);
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String);
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+ method public static java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/image";
field public static final java.lang.String DEFAULT_SORT_ORDER = "bucket_display_name";
field public static final android.net.Uri EXTERNAL_CONTENT_URI;
@@ -36158,7 +36248,7 @@ package android.provider {
public static final class MediaStore.Video {
ctor public MediaStore.Video();
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
field public static final java.lang.String DEFAULT_SORT_ORDER = "_display_name";
}
@@ -36390,12 +36480,12 @@ package android.provider {
method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
method public static android.net.Uri getUriFor(java.lang.String);
- method public static final deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String);
+ method public static deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String);
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
- method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
+ method public static deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled";
field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
field public static final deprecated java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
@@ -38198,6 +38288,37 @@ package android.security {
method public java.security.KeyPair getKeyPair();
}
+ public class ConfirmationAlreadyPresentingException extends java.lang.Exception {
+ ctor public ConfirmationAlreadyPresentingException();
+ ctor public ConfirmationAlreadyPresentingException(java.lang.String);
+ }
+
+ public abstract class ConfirmationCallback {
+ ctor public ConfirmationCallback();
+ method public void onConfirmedByUser(byte[]);
+ method public void onDismissedByApplication();
+ method public void onDismissedByUser();
+ method public void onError(java.lang.Exception);
+ }
+
+ public class ConfirmationDialog {
+ method public void cancelPrompt();
+ method public static boolean isSupported();
+ method public void presentPrompt(java.util.concurrent.Executor, android.security.ConfirmationCallback) throws android.security.ConfirmationAlreadyPresentingException, android.security.ConfirmationNotAvailableException;
+ }
+
+ public static class ConfirmationDialog.Builder {
+ ctor public ConfirmationDialog.Builder();
+ method public android.security.ConfirmationDialog build(android.content.Context);
+ method public android.security.ConfirmationDialog.Builder setExtraData(byte[]);
+ method public android.security.ConfirmationDialog.Builder setPromptText(java.lang.CharSequence);
+ }
+
+ public class ConfirmationNotAvailableException extends java.lang.Exception {
+ ctor public ConfirmationNotAvailableException();
+ ctor public ConfirmationNotAvailableException(java.lang.String);
+ }
+
public final class KeyChain {
ctor public KeyChain();
method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
@@ -38307,6 +38428,7 @@ package android.security.keystore {
method public boolean isTrustedUserPresenceRequired();
method public boolean isUserAuthenticationRequired();
method public boolean isUserAuthenticationValidWhileOnBody();
+ method public boolean isUserConfirmationRequired();
}
public static final class KeyGenParameterSpec.Builder {
@@ -38334,6 +38456,7 @@ package android.security.keystore {
method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
+ method public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean);
}
public class KeyInfo implements java.security.spec.KeySpec {
@@ -38355,6 +38478,7 @@ package android.security.keystore {
method public boolean isUserAuthenticationRequired();
method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
method public boolean isUserAuthenticationValidWhileOnBody();
+ method public boolean isUserConfirmationRequired();
}
public class KeyNotYetValidException extends java.security.InvalidKeyException {
@@ -38422,6 +38546,7 @@ package android.security.keystore {
method public boolean isRandomizedEncryptionRequired();
method public boolean isUserAuthenticationRequired();
method public boolean isUserAuthenticationValidWhileOnBody();
+ method public boolean isUserConfirmationRequired();
}
public static final class KeyProtection.Builder {
@@ -38440,6 +38565,7 @@ package android.security.keystore {
method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
+ method public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean);
}
public class StrongBoxUnavailableException extends java.security.ProviderException {
@@ -40391,12 +40517,12 @@ package android.telecom {
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
method public void pullExternalCall();
- method public final void putExtras(android.os.Bundle);
+ method public void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
- method public final void removeExtras(java.util.List<java.lang.String>);
- method public final void removeExtras(java.lang.String...);
+ method public void removeExtras(java.util.List<java.lang.String>);
+ method public void removeExtras(java.lang.String...);
method public void respondToRttRequest(int, boolean);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void sendRttRequest();
@@ -40965,23 +41091,23 @@ package android.telecom {
public final class RemoteConference {
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
- method public final int getConnectionCapabilities();
- method public final int getConnectionProperties();
- method public final java.util.List<android.telecom.RemoteConnection> getConnections();
+ method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
+ method public java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
- method public final android.os.Bundle getExtras();
- method public final int getState();
+ method public android.os.Bundle getExtras();
+ method public int getState();
method public void hold();
method public void merge();
method public void playDtmfTone(char);
- method public final void registerCallback(android.telecom.RemoteConference.Callback);
- method public final void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
+ method public void registerCallback(android.telecom.RemoteConference.Callback);
+ method public void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
method public void separate(android.telecom.RemoteConnection);
method public void setCallAudioState(android.telecom.CallAudioState);
method public void stopDtmfTone();
method public void swap();
method public void unhold();
- method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
+ method public void unregisterCallback(android.telecom.RemoteConference.Callback);
}
public static abstract class RemoteConference.Callback {
@@ -41010,10 +41136,10 @@ package android.telecom {
method public int getConnectionCapabilities();
method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
- method public final android.os.Bundle getExtras();
+ method public android.os.Bundle getExtras();
method public int getState();
method public android.telecom.StatusHints getStatusHints();
- method public final android.telecom.RemoteConnection.VideoProvider getVideoProvider();
+ method public android.telecom.RemoteConnection.VideoProvider getVideoProvider();
method public int getVideoState();
method public void hold();
method public boolean isRingbackRequested();
@@ -42466,11 +42592,11 @@ package android.telephony.gsm {
}
public final deprecated class SmsManager {
- method public final deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
- method public static final deprecated android.telephony.gsm.SmsManager getDefault();
- method public final deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
- method public final deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
- method public final deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
+ method public deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
+ method public static deprecated android.telephony.gsm.SmsManager getDefault();
+ method public deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
+ method public deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
+ method public deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
field public static final deprecated int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1
field public static final deprecated int RESULT_ERROR_NO_SERVICE = 4; // 0x4
field public static final deprecated int RESULT_ERROR_NULL_PDU = 3; // 0x3
@@ -46529,76 +46655,76 @@ package android.view {
public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
method public static java.lang.String actionToString(int);
- method public final void addBatch(long, float, float, float, float, int);
- method public final void addBatch(long, android.view.MotionEvent.PointerCoords[], int);
+ method public void addBatch(long, float, float, float, float, int);
+ method public void addBatch(long, android.view.MotionEvent.PointerCoords[], int);
method public static int axisFromString(java.lang.String);
method public static java.lang.String axisToString(int);
- method public final int findPointerIndex(int);
- method public final int getAction();
- method public final int getActionButton();
- method public final int getActionIndex();
- method public final int getActionMasked();
- method public final float getAxisValue(int);
- method public final float getAxisValue(int, int);
- method public final int getButtonState();
- method public final int getDeviceId();
- method public final long getDownTime();
- method public final int getEdgeFlags();
- method public final long getEventTime();
- method public final int getFlags();
- method public final float getHistoricalAxisValue(int, int);
- method public final float getHistoricalAxisValue(int, int, int);
- method public final long getHistoricalEventTime(int);
- method public final float getHistoricalOrientation(int);
- method public final float getHistoricalOrientation(int, int);
- method public final void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords);
- method public final float getHistoricalPressure(int);
- method public final float getHistoricalPressure(int, int);
- method public final float getHistoricalSize(int);
- method public final float getHistoricalSize(int, int);
- method public final float getHistoricalToolMajor(int);
- method public final float getHistoricalToolMajor(int, int);
- method public final float getHistoricalToolMinor(int);
- method public final float getHistoricalToolMinor(int, int);
- method public final float getHistoricalTouchMajor(int);
- method public final float getHistoricalTouchMajor(int, int);
- method public final float getHistoricalTouchMinor(int);
- method public final float getHistoricalTouchMinor(int, int);
- method public final float getHistoricalX(int);
- method public final float getHistoricalX(int, int);
- method public final float getHistoricalY(int);
- method public final float getHistoricalY(int, int);
- method public final int getHistorySize();
- method public final int getMetaState();
- method public final float getOrientation();
- method public final float getOrientation(int);
- method public final void getPointerCoords(int, android.view.MotionEvent.PointerCoords);
- method public final int getPointerCount();
- method public final int getPointerId(int);
- method public final void getPointerProperties(int, android.view.MotionEvent.PointerProperties);
- method public final float getPressure();
- method public final float getPressure(int);
- method public final float getRawX();
- method public final float getRawY();
- method public final float getSize();
- method public final float getSize(int);
- method public final int getSource();
- method public final float getToolMajor();
- method public final float getToolMajor(int);
- method public final float getToolMinor();
- method public final float getToolMinor(int);
- method public final int getToolType(int);
- method public final float getTouchMajor();
- method public final float getTouchMajor(int);
- method public final float getTouchMinor();
- method public final float getTouchMinor(int);
- method public final float getX();
- method public final float getX(int);
- method public final float getXPrecision();
- method public final float getY();
- method public final float getY(int);
- method public final float getYPrecision();
- method public final boolean isButtonPressed(int);
+ method public int findPointerIndex(int);
+ method public int getAction();
+ method public int getActionButton();
+ method public int getActionIndex();
+ method public int getActionMasked();
+ method public float getAxisValue(int);
+ method public float getAxisValue(int, int);
+ method public int getButtonState();
+ method public int getDeviceId();
+ method public long getDownTime();
+ method public int getEdgeFlags();
+ method public long getEventTime();
+ method public int getFlags();
+ method public float getHistoricalAxisValue(int, int);
+ method public float getHistoricalAxisValue(int, int, int);
+ method public long getHistoricalEventTime(int);
+ method public float getHistoricalOrientation(int);
+ method public float getHistoricalOrientation(int, int);
+ method public void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords);
+ method public float getHistoricalPressure(int);
+ method public float getHistoricalPressure(int, int);
+ method public float getHistoricalSize(int);
+ method public float getHistoricalSize(int, int);
+ method public float getHistoricalToolMajor(int);
+ method public float getHistoricalToolMajor(int, int);
+ method public float getHistoricalToolMinor(int);
+ method public float getHistoricalToolMinor(int, int);
+ method public float getHistoricalTouchMajor(int);
+ method public float getHistoricalTouchMajor(int, int);
+ method public float getHistoricalTouchMinor(int);
+ method public float getHistoricalTouchMinor(int, int);
+ method public float getHistoricalX(int);
+ method public float getHistoricalX(int, int);
+ method public float getHistoricalY(int);
+ method public float getHistoricalY(int, int);
+ method public int getHistorySize();
+ method public int getMetaState();
+ method public float getOrientation();
+ method public float getOrientation(int);
+ method public void getPointerCoords(int, android.view.MotionEvent.PointerCoords);
+ method public int getPointerCount();
+ method public int getPointerId(int);
+ method public void getPointerProperties(int, android.view.MotionEvent.PointerProperties);
+ method public float getPressure();
+ method public float getPressure(int);
+ method public float getRawX();
+ method public float getRawY();
+ method public float getSize();
+ method public float getSize(int);
+ method public int getSource();
+ method public float getToolMajor();
+ method public float getToolMajor(int);
+ method public float getToolMinor();
+ method public float getToolMinor(int);
+ method public int getToolType(int);
+ method public float getTouchMajor();
+ method public float getTouchMajor(int);
+ method public float getTouchMinor();
+ method public float getTouchMinor(int);
+ method public float getX();
+ method public float getX(int);
+ method public float getXPrecision();
+ method public float getY();
+ method public float getY(int);
+ method public float getYPrecision();
+ method public boolean isButtonPressed(int);
method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
@@ -46606,13 +46732,13 @@ package android.view {
method public static android.view.MotionEvent obtain(long, long, int, float, float, int);
method public static android.view.MotionEvent obtain(android.view.MotionEvent);
method public static android.view.MotionEvent obtainNoHistory(android.view.MotionEvent);
- method public final void offsetLocation(float, float);
- method public final void recycle();
- method public final void setAction(int);
- method public final void setEdgeFlags(int);
- method public final void setLocation(float, float);
- method public final void setSource(int);
- method public final void transform(android.graphics.Matrix);
+ method public void offsetLocation(float, float);
+ method public void recycle();
+ method public void setAction(int);
+ method public void setEdgeFlags(int);
+ method public void setLocation(float, float);
+ method public void setSource(int);
+ method public void transform(android.graphics.Matrix);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_BUTTON_PRESS = 11; // 0xb
field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc
@@ -47173,7 +47299,9 @@ package android.view {
method public int getNextFocusRightId();
method public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
+ method public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
+ method public int getOutlineSpotShadowColor();
method public int getOverScrollMode();
method public android.view.ViewOverlay getOverlay();
method public int getPaddingBottom();
@@ -47495,7 +47623,9 @@ package android.view {
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
+ method public void setOutlineAmbientShadowColor(int);
method public void setOutlineProvider(android.view.ViewOutlineProvider);
+ method public void setOutlineSpotShadowColor(int);
method public void setOverScrollMode(int);
method public void setPadding(int, int, int, int);
method public void setPaddingRelative(int, int, int, int);
@@ -48330,9 +48460,9 @@ package android.view {
method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
- method public final void dispatchOnDraw();
- method public final void dispatchOnGlobalLayout();
- method public final boolean dispatchOnPreDraw();
+ method public void dispatchOnDraw();
+ method public void dispatchOnGlobalLayout();
+ method public boolean dispatchOnPreDraw();
method public boolean isAlive();
method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
@@ -50593,7 +50723,7 @@ package android.webkit {
ctor public URLUtil();
method public static java.lang.String composeSearchUrl(java.lang.String, java.lang.String, java.lang.String);
method public static byte[] decode(byte[]) throws java.lang.IllegalArgumentException;
- method public static final java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String);
+ method public static java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String);
method public static java.lang.String guessUrl(java.lang.String);
method public static boolean isAboutUrl(java.lang.String);
method public static boolean isAssetUrl(java.lang.String);
@@ -55662,7 +55792,7 @@ package java.lang {
}
public static final class Character.UnicodeBlock extends java.lang.Character.Subset {
- method public static final java.lang.Character.UnicodeBlock forName(java.lang.String);
+ method public static java.lang.Character.UnicodeBlock forName(java.lang.String);
method public static java.lang.Character.UnicodeBlock of(char);
method public static java.lang.Character.UnicodeBlock of(int);
field public static final java.lang.Character.UnicodeBlock AEGEAN_NUMBERS;
@@ -55889,7 +56019,7 @@ package java.lang {
}
public static final class Character.UnicodeScript extends java.lang.Enum {
- method public static final java.lang.Character.UnicodeScript forName(java.lang.String);
+ method public static java.lang.Character.UnicodeScript forName(java.lang.String);
method public static java.lang.Character.UnicodeScript of(int);
method public static java.lang.Character.UnicodeScript valueOf(java.lang.String);
method public static final java.lang.Character.UnicodeScript[] values();
@@ -57552,6 +57682,7 @@ package java.lang.ref {
method public boolean enqueue();
method public T get();
method public boolean isEnqueued();
+ method public static void reachabilityFence(java.lang.Object);
}
public class ReferenceQueue<T> {
@@ -58704,8 +58835,8 @@ package java.net {
ctor public URL(java.net.URL, java.lang.String) throws java.net.MalformedURLException;
ctor public URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
method public java.lang.String getAuthority();
- method public final java.lang.Object getContent() throws java.io.IOException;
- method public final java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
+ method public java.lang.Object getContent() throws java.io.IOException;
+ method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
method public int getDefaultPort();
method public java.lang.String getFile();
method public java.lang.String getHost();
@@ -58718,7 +58849,7 @@ package java.net {
method public synchronized int hashCode();
method public java.net.URLConnection openConnection() throws java.io.IOException;
method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
- method public final java.io.InputStream openStream() throws java.io.IOException;
+ method public java.io.InputStream openStream() throws java.io.IOException;
method public boolean sameFile(java.net.URL);
method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
method public java.lang.String toExternalForm();
@@ -63374,13 +63505,13 @@ package java.text {
method public int getOffset();
method public int next();
method public int previous();
- method public static final int primaryOrder(int);
+ method public static int primaryOrder(int);
method public void reset();
- method public static final short secondaryOrder(int);
+ method public static short secondaryOrder(int);
method public void setOffset(int);
method public void setText(java.lang.String);
method public void setText(java.text.CharacterIterator);
- method public static final short tertiaryOrder(int);
+ method public static short tertiaryOrder(int);
field public static final int NULLORDER = -1; // 0xffffffff
}
@@ -64661,7 +64792,7 @@ package java.time.chrono {
}
public final class HijrahDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime);
method public static java.time.chrono.HijrahDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.HijrahChronology getChronology();
method public java.time.chrono.HijrahEra getEra();
@@ -64748,7 +64879,7 @@ package java.time.chrono {
}
public final class JapaneseDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime);
method public static java.time.chrono.JapaneseDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.JapaneseChronology getChronology();
method public java.time.chrono.JapaneseEra getEra();
@@ -64804,7 +64935,7 @@ package java.time.chrono {
}
public final class MinguoDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime);
method public static java.time.chrono.MinguoDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.MinguoChronology getChronology();
method public java.time.chrono.MinguoEra getEra();
@@ -64857,7 +64988,7 @@ package java.time.chrono {
}
public final class ThaiBuddhistDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime);
method public static java.time.chrono.ThaiBuddhistDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.ThaiBuddhistChronology getChronology();
method public java.time.chrono.ThaiBuddhistEra getEra();
@@ -64909,8 +65040,8 @@ package java.time.format {
method public <T> T parse(java.lang.CharSequence, java.time.temporal.TemporalQuery<T>);
method public java.time.temporal.TemporalAccessor parseBest(java.lang.CharSequence, java.time.temporal.TemporalQuery<?>...);
method public java.time.temporal.TemporalAccessor parseUnresolved(java.lang.CharSequence, java.text.ParsePosition);
- method public static final java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays();
- method public static final java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond();
+ method public static java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays();
+ method public static java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond();
method public java.text.Format toFormat();
method public java.text.Format toFormat(java.time.temporal.TemporalQuery<?>);
method public java.time.format.DateTimeFormatter withChronology(java.time.chrono.Chronology);
@@ -66359,15 +66490,15 @@ package java.util {
method public java.lang.String getCountry();
method public static java.util.Locale getDefault();
method public static java.util.Locale getDefault(java.util.Locale.Category);
- method public final java.lang.String getDisplayCountry();
+ method public java.lang.String getDisplayCountry();
method public java.lang.String getDisplayCountry(java.util.Locale);
- method public final java.lang.String getDisplayLanguage();
+ method public java.lang.String getDisplayLanguage();
method public java.lang.String getDisplayLanguage(java.util.Locale);
- method public final java.lang.String getDisplayName();
+ method public java.lang.String getDisplayName();
method public java.lang.String getDisplayName(java.util.Locale);
method public java.lang.String getDisplayScript();
method public java.lang.String getDisplayScript(java.util.Locale);
- method public final java.lang.String getDisplayVariant();
+ method public java.lang.String getDisplayVariant();
method public java.lang.String getDisplayVariant(java.util.Locale);
method public java.lang.String getExtension(char);
method public java.util.Set<java.lang.Character> getExtensionKeys();
@@ -66388,7 +66519,6 @@ package java.util {
method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
method public java.util.Locale stripExtensions();
method public java.lang.String toLanguageTag();
- method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
field public static final java.util.Locale CANADA_FRENCH;
field public static final java.util.Locale CHINA;
@@ -70574,7 +70704,6 @@ package javax.crypto {
public class ExemptionMechanism {
ctor protected ExemptionMechanism(javax.crypto.ExemptionMechanismSpi, java.security.Provider, java.lang.String);
- method protected void finalize();
method public final byte[] genExemptionBlob() throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException;
method public final int genExemptionBlob(byte[]) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
method public final int genExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 034ee3090fc4..2a257b128c09 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19,6 +19,7 @@ package android {
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
+ field public static final java.lang.String BIND_DATA_SERVICE = "android.permission.BIND_DATA_SERVICE";
field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
@@ -311,8 +312,10 @@ package android.app {
ctor public InstantAppResolverService();
method public final void attachBaseContext(android.content.Context);
method public final android.os.IBinder onBind(android.content.Intent);
- method public void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
- method public void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
+ method public deprecated void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
+ method public void onGetInstantAppIntentFilter(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
+ method public deprecated void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
+ method public void onGetInstantAppResolveInfo(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback);
}
public static final class InstantAppResolverService.InstantAppResolutionCallback {
@@ -820,13 +823,24 @@ package android.content {
field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final java.lang.String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
+ field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
field public static final java.lang.String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
+ field public static final java.lang.String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
+ field public static final java.lang.String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES";
+ field public static final java.lang.String EXTRA_INSTANT_APP_EXTRAS = "android.intent.extra.INSTANT_APP_EXTRAS";
+ field public static final java.lang.String EXTRA_INSTANT_APP_FAILURE = "android.intent.extra.INSTANT_APP_FAILURE";
+ field public static final java.lang.String EXTRA_INSTANT_APP_HOSTNAME = "android.intent.extra.INSTANT_APP_HOSTNAME";
+ field public static final java.lang.String EXTRA_INSTANT_APP_SUCCESS = "android.intent.extra.INSTANT_APP_SUCCESS";
+ field public static final java.lang.String EXTRA_INSTANT_APP_TOKEN = "android.intent.extra.INSTANT_APP_TOKEN";
+ field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON";
field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
+ field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
+ field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
}
public class IntentFilter implements android.os.Parcelable {
@@ -839,7 +853,9 @@ package android.content {
package android.content.pm {
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ method public boolean isInstantApp();
field public java.lang.String credentialProtectedDataDir;
+ field public int targetSandboxVersion;
}
public final class InstantAppInfo implements android.os.Parcelable {
@@ -869,6 +885,7 @@ package android.content.pm {
ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, int);
ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, long, android.os.Bundle);
ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>);
+ ctor public InstantAppResolveInfo(android.os.Bundle);
method public int describeContents();
method public byte[] getDigestBytes();
method public int getDigestPrefix();
@@ -877,6 +894,7 @@ package android.content.pm {
method public long getLongVersionCode();
method public java.lang.String getPackageName();
method public deprecated int getVersionCode();
+ method public boolean shouldLetInstallerDecide();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR;
}
@@ -888,6 +906,7 @@ package android.content.pm {
method public int[] getDigestPrefix();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo.InstantAppDigest> CREATOR;
+ field public static final android.content.pm.InstantAppResolveInfo.InstantAppDigest UNDEFINED;
}
public final class IntentFilterVerificationInfo implements android.os.Parcelable {
@@ -1122,6 +1141,15 @@ package android.hardware.camera2.params {
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -1129,11 +1157,14 @@ package android.hardware.display {
field public final float batteryLevel;
field public final float brightness;
field public final int colorTemperature;
+ field public final boolean isDefaultBrightnessConfig;
+ field public final boolean isUserSetBrightness;
field public final float lastBrightness;
field public final long[] luxTimestamps;
field public final float[] luxValues;
field public final boolean nightMode;
field public final java.lang.String packageName;
+ field public final float powerBrightnessFactor;
field public final long timeStamp;
}
@@ -1991,6 +2022,7 @@ package android.hardware.radio {
method public int describeContents();
method public android.hardware.radio.RadioManager.BandDescriptor[] getBands();
method public int getClassId();
+ method public java.util.Map<java.lang.String, java.lang.Integer> getDabFrequencyTable();
method public int getId();
method public java.lang.String getImplementor();
method public int getNumAudioSources();
@@ -2673,10 +2705,10 @@ package android.media.soundtrigger {
package android.media.tv {
public final class TvContentRatingSystemInfo implements android.os.Parcelable {
- method public static final android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
+ method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
method public int describeContents();
- method public final android.net.Uri getXmlUri();
- method public final boolean isSystemDefined();
+ method public android.net.Uri getXmlUri();
+ method public boolean isSystemDefined();
method public void writeToParcel(android.os.Parcel, int);
}
@@ -4657,15 +4689,15 @@ package android.telecom {
}
public final deprecated class Phone {
- method public final void addListener(android.telecom.Phone.Listener);
- method public final boolean canAddCall();
- method public final deprecated android.telecom.AudioState getAudioState();
- method public final android.telecom.CallAudioState getCallAudioState();
- method public final java.util.List<android.telecom.Call> getCalls();
- method public final void removeListener(android.telecom.Phone.Listener);
+ method public void addListener(android.telecom.Phone.Listener);
+ method public boolean canAddCall();
+ method public deprecated android.telecom.AudioState getAudioState();
+ method public android.telecom.CallAudioState getCallAudioState();
+ method public java.util.List<android.telecom.Call> getCalls();
+ method public void removeListener(android.telecom.Phone.Listener);
method public void requestBluetoothAudio(java.lang.String);
- method public final void setAudioRoute(int);
- method public final void setMuted(boolean);
+ method public void setAudioRoute(int);
+ method public void setMuted(boolean);
}
public static abstract class Phone.Listener {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index b63703dc0035..48f43e0880da 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,13 +1,5 @@
package android.app {
- public abstract deprecated class EphemeralResolverService extends android.app.InstantAppResolverService {
- ctor public EphemeralResolverService();
- method public android.os.Looper getLooper();
- method public abstract deprecated java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
- method public android.content.pm.EphemeralResolveInfo onGetEphemeralIntentFilter(java.lang.String);
- method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralResolveInfo(int[]);
- }
-
public class Notification implements android.os.Parcelable {
method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
}
@@ -31,10 +23,7 @@ package android.content {
public class Intent implements java.lang.Cloneable android.os.Parcelable {
field public static final deprecated java.lang.String ACTION_DEVICE_INITIALIZATION_WIZARD = "android.intent.action.DEVICE_INITIALIZATION_WIZARD";
- field public static final deprecated java.lang.String ACTION_EPHEMERAL_RESOLVER_SETTINGS = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS";
- field public static final deprecated java.lang.String ACTION_INSTALL_EPHEMERAL_PACKAGE = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
field public static final deprecated java.lang.String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
- field public static final deprecated java.lang.String ACTION_RESOLVE_EPHEMERAL_PACKAGE = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
field public static final deprecated java.lang.String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
field public static final deprecated java.lang.String EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR = "cdmaDefaultRoamingIndicator";
field public static final deprecated java.lang.String EXTRA_CDMA_ROAMING_INDICATOR = "cdmaRoamingIndicator";
@@ -62,45 +51,6 @@ package android.content {
}
-package android.content.pm {
-
- public final deprecated class EphemeralIntentFilter implements android.os.Parcelable {
- ctor public EphemeralIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>);
- method public int describeContents();
- method public java.util.List<android.content.IntentFilter> getFilters();
- method public java.lang.String getSplitName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralIntentFilter> CREATOR;
- }
-
- public final deprecated class EphemeralResolveInfo implements android.os.Parcelable {
- ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
- ctor public deprecated EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
- ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>, int);
- ctor public EphemeralResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
- method public int describeContents();
- method public byte[] getDigestBytes();
- method public int getDigestPrefix();
- method public deprecated java.util.List<android.content.IntentFilter> getFilters();
- method public java.util.List<android.content.pm.EphemeralIntentFilter> getIntentFilters();
- method public java.lang.String getPackageName();
- method public int getVersionCode();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR;
- field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
- }
-
- public static final class EphemeralResolveInfo.EphemeralDigest implements android.os.Parcelable {
- ctor public EphemeralResolveInfo.EphemeralDigest(java.lang.String);
- method public int describeContents();
- method public byte[][] getDigestBytes();
- method public int[] getDigestPrefix();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo.EphemeralDigest> CREATOR;
- }
-
-}
-
package android.media.tv {
public final class TvInputManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index d834cf7d62ab..b331ba0c7205 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -298,6 +298,15 @@ package android.hardware.camera2 {
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -305,11 +314,14 @@ package android.hardware.display {
field public final float batteryLevel;
field public final float brightness;
field public final int colorTemperature;
+ field public final boolean isDefaultBrightnessConfig;
+ field public final boolean isUserSetBrightness;
field public final float lastBrightness;
field public final long[] luxTimestamps;
field public final float[] luxValues;
field public final boolean nightMode;
field public final java.lang.String packageName;
+ field public final float powerBrightnessFactor;
field public final long timeStamp;
}
@@ -995,8 +1007,8 @@ package android.view {
}
public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
- method public final void setActionButton(int);
- method public final void setButtonState(int);
+ method public void setActionButton(int);
+ method public void setButtonState(int);
}
public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
@@ -1044,6 +1056,14 @@ package android.view.autofill {
}
+package android.view.inputmethod {
+
+ public final class InputMethodManager {
+ method public boolean isInputMethodPickerShown();
+ }
+
+}
+
package android.widget {
public abstract class AbsListView extends android.widget.AdapterView implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index f75678b7fa1e..6e0bd3a81d84 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -26,6 +26,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -34,6 +35,7 @@ import android.text.TextUtils;
import libcore.io.Streams;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -583,7 +585,7 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
- Streams.copy(new FileInputStream(fd.getFileDescriptor()), System.out);
+ FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
}
@@ -596,7 +598,7 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
- Streams.copy(System.in, new FileOutputStream(fd.getFileDescriptor()));
+ FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index eabbb96a392e..b0019ac90708 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -38,8 +38,11 @@ statsd_common_src := \
src/external/StatsPuller.cpp \
src/external/StatsCompanionServicePuller.cpp \
src/external/SubsystemSleepStatePuller.cpp \
+ src/external/ResourceHealthManagerPuller.cpp \
src/external/CpuTimePerUidPuller.cpp \
src/external/CpuTimePerUidFreqPuller.cpp \
+ src/external/KernelUidCpuActiveTimeReader.cpp \
+ src/external/KernelUidCpuClusterTimeReader.cpp \
src/external/StatsPullerManagerImpl.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
@@ -75,7 +78,8 @@ statsd_common_aidl_includes := \
$(LOCAL_PATH)/../../core/java
statsd_common_static_libraries := \
- libplatformprotos
+ libhealthhalutils \
+ libplatformprotos \
statsd_common_shared_libraries := \
libbase \
@@ -93,6 +97,7 @@ statsd_common_shared_libraries := \
libhidlbase \
libhidltransport \
libhwbinder \
+ android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
libmemunreachable
@@ -165,6 +170,7 @@ LOCAL_CFLAGS += \
LOCAL_SRC_FILES := \
$(statsd_common_src) \
+ tests/dimension_test.cpp \
tests/AnomalyMonitor_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
tests/ConfigManager_test.cpp \
@@ -190,7 +196,8 @@ LOCAL_SRC_FILES := \
tests/e2e/WakelockDuration_e2e_test.cpp \
tests/e2e/MetricConditionLink_e2e_test.cpp \
tests/e2e/Attribution_e2e_test.cpp \
- tests/e2e/GaugeMetric_e2e_test.cpp
+ tests/e2e/GaugeMetric_e2e_test.cpp \
+ tests/e2e/DimensionInCondition_e2e_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 857a6ddad0be..f0eaeff88d34 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -67,12 +67,16 @@ android::hash_t hashDimensionsValue(const DimensionsValue& value) {
return hashDimensionsValue(0, value);
}
+android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) {
+ android::hash_t hash = seed;
+ hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey));
+ return JenkinsHashWhiten(hash);
+}
+
using std::string;
string HashableDimensionKey::toString() const {
- string flattened;
- DimensionsValueToString(getDimensionsValue(), &flattened);
- return flattened;
+ return DimensionsValueToString(getDimensionsValue());
}
bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
@@ -162,6 +166,22 @@ bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
return LessThan(getDimensionsValue(), that.getDimensionsValue());
};
+string MetricDimensionKey::toString() const {
+ string flattened = mDimensionKeyInWhat.toString();
+ flattened += mDimensionKeyInCondition.toString();
+ return flattened;
+}
+
+bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
+ return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
+ mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+};
+
+bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
+ return toString().compare(that.toString()) < 0;
+};
+
+
} // namespace statsd
} // namespace os
} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 85c317f8cf1f..a31d7a6d85c6 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -41,6 +41,10 @@ public:
return mDimensionsValue;
}
+ inline DimensionsValue* getMutableDimensionsValue() {
+ return &mDimensionsValue;
+ }
+
bool operator==(const HashableDimensionKey& that) const;
bool operator<(const HashableDimensionKey& that) const;
@@ -53,8 +57,52 @@ private:
DimensionsValue mDimensionsValue;
};
+class MetricDimensionKey {
+ public:
+ explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
+ const HashableDimensionKey& dimensionKeyInCondition)
+ : mDimensionKeyInWhat(dimensionKeyInWhat),
+ mDimensionKeyInCondition(dimensionKeyInCondition) {};
+
+ MetricDimensionKey(){};
+
+ MetricDimensionKey(const MetricDimensionKey& that)
+ : mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
+ mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {};
+
+ MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
+
+ std::string toString() const;
+
+ inline const HashableDimensionKey& getDimensionKeyInWhat() const {
+ return mDimensionKeyInWhat;
+ }
+
+ inline const HashableDimensionKey& getDimensionKeyInCondition() const {
+ return mDimensionKeyInCondition;
+ }
+
+ bool hasDimensionKeyInCondition() const {
+ return mDimensionKeyInCondition.getDimensionsValue().has_field();
+ }
+
+ bool operator==(const MetricDimensionKey& that) const;
+
+ bool operator<(const MetricDimensionKey& that) const;
+
+ inline const char* c_str() const {
+ return toString().c_str();
+ }
+ private:
+ HashableDimensionKey mDimensionKeyInWhat;
+ HashableDimensionKey mDimensionKeyInCondition;
+};
+
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2);
+
android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value);
android::hash_t hashDimensionsValue(const DimensionsValue& value);
+android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey);
} // namespace statsd
} // namespace os
@@ -63,6 +111,7 @@ android::hash_t hashDimensionsValue(const DimensionsValue& value);
namespace std {
using android::os::statsd::HashableDimensionKey;
+using android::os::statsd::MetricDimensionKey;
template <>
struct hash<HashableDimensionKey> {
@@ -71,4 +120,14 @@ struct hash<HashableDimensionKey> {
}
};
-} // namespace std
+template <>
+struct hash<MetricDimensionKey> {
+ std::size_t operator()(const MetricDimensionKey& key) const {
+ android::hash_t hash = hashDimensionsValue(
+ key.getDimensionKeyInWhat().getDimensionsValue());
+ hash = android::JenkinsHashMix(hash,
+ hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue()));
+ return android::JenkinsHashWhiten(hash);
+ }
+};
+} // namespace std \ No newline at end of file
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index c19ff63e2858..7642aafaab43 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -104,7 +104,10 @@ private:
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
-
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index f545bb0738e9..4e4145439e25 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -551,6 +551,12 @@ status_t StatsService::cmd_dump_memory_info(FILE* out) {
return NO_ERROR;
}
+status_t StatsService::cmd_clear_puller_cache(FILE* out) {
+ mStatsPullerManager.ClearPullerCache();
+ fprintf(out, "Puller cached data removed!\n");
+ return NO_ERROR;
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
const vector<String16>& app) {
VLOG("StatsService::informAllUidData was called");
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index be20893994f1..fd3ed1dbed72 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -197,6 +197,11 @@ private:
*/
status_t cmd_dump_memory_info(FILE* out);
+ /*
+ * Clear all puller cached data
+ */
+ status_t cmd_clear_puller_cache(FILE* out);
+
/**
* Update a configuration.
*/
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ded6c4c660be..c84a5b4509b0 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -96,7 +96,7 @@ void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) {
}
}
-void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
const int64_t& bucketNum) {
flushPastBuckets(bucketNum);
@@ -147,7 +147,7 @@ void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
}
}
-int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
const int64_t& bucketNum) const {
const auto& bucket = mPastBuckets[index(bucketNum)];
if (bucket == nullptr) {
@@ -157,7 +157,7 @@ int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
return itr == bucket->end() ? 0 : itr->second;
}
-int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
const auto& itr = mSumOverPastBuckets.find(key);
if (itr != mSumOverPastBuckets.end()) {
return itr->second;
@@ -165,7 +165,7 @@ int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) c
return 0;
}
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
if (currentBucketNum > mMostRecentBucketNum + 1) {
// TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this.
@@ -175,7 +175,7 @@ bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const Hashab
&& getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
}
-void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key) {
// TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
if (isInRefractoryPeriod(timestampNs, key)) {
VLOG("Skipping anomaly declaration since within refractory period");
@@ -199,14 +199,14 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableD
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
- // TODO: This should also take in the const HashableDimensionKey& key?
+ // TODO: This should also take in the const MetricDimensionKey& key?
android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
mConfigKey.GetId(), mAlert.id());
}
void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
const int64_t& currBucketNum,
- const HashableDimensionKey& key,
+ const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
declareAnomaly(timestampNs, key);
@@ -214,7 +214,7 @@ void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
}
bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
- const HashableDimensionKey& key) {
+ const MetricDimensionKey& key) {
const auto& it = mRefractoryPeriodEndsSec.find(key);
if (it != mRefractoryPeriodEndsSec.end()) {
if ((timestampNs / NS_PER_SEC) <= it->second) {
@@ -226,7 +226,7 @@ bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
return false;
}
-void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
VLOG("informSubscribers called.");
if (mSubscriptions.empty()) {
ALOGE("Attempt to call with no subscribers.");
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 472c02c7ee7f..f01a97f86cf6 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -48,19 +48,19 @@ public:
// 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,
+ void addPastBucket(const MetricDimensionKey& 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 HashableDimensionKey& key,
+ bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
const int64_t& currentBucketValue);
// Informs incidentd about the detected alert.
- void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key);
+ void declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key);
// Detects the alert and informs the incidentd when applicable.
void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
- const HashableDimensionKey& key,
+ const MetricDimensionKey& key,
const int64_t& currentBucketValue);
// Init the AnomalyMonitor which is shared across anomaly trackers.
@@ -69,10 +69,10 @@ public:
}
// Helper function to return the sum value of past buckets at given dimension.
- int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+ int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
// Helper function to return the value for a past bucket.
- int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+ int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
// Returns the anomaly threshold.
inline int64_t getAnomalyThreshold() const {
@@ -81,7 +81,7 @@ public:
// Returns the refractory period timestamp (in seconds) for the given key.
// If there is no stored refractory period ending timestamp, returns 0.
- uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const {
+ uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
const auto& it = mRefractoryPeriodEndsSec.find(key);
return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
}
@@ -124,7 +124,7 @@ protected:
// declared for that dimension) ends, in seconds. Only anomalies that occur after this period
// ends will be declared.
// Entries may be, but are not guaranteed to be, removed after the period is finished.
- unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
+ unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
void flushPastBuckets(const int64_t& currBucketNum);
@@ -135,7 +135,7 @@ protected:
// and remove any items with value 0.
void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
- bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key);
+ bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key);
// Calculates the corresponding bucket index within the circular array.
size_t index(int64_t bucketNum) const;
@@ -144,7 +144,7 @@ protected:
virtual void resetStorage();
// Informs the subscribers that an anomaly has occurred.
- void informSubscribers(const HashableDimensionKey& key);
+ void informSubscribers(const MetricDimensionKey& key);
FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 7576a38db51d..bbee9fa5358c 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -37,8 +37,8 @@ void DurationAnomalyTracker::resetStorage() {
if (!mAlarms.empty()) VLOG("AnomalyTracker.resetStorage() called but mAlarms is NOT empty!");
}
-void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
- const uint64_t& timestampNs) {
+void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
+ const uint64_t& timestampNs) {
auto itr = mAlarms.find(dimensionKey);
if (itr == mAlarms.end()) {
return;
@@ -51,7 +51,7 @@ void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensio
}
}
-void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
+void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
const uint64_t& timestampNs) {
uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC);
@@ -66,7 +66,7 @@ void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey
}
}
-void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) {
auto itr = mAlarms.find(dimensionKey);
if (itr != mAlarms.end()) {
mAlarms.erase(dimensionKey);
@@ -77,7 +77,7 @@ void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey)
}
void DurationAnomalyTracker::stopAllAlarms() {
- std::set<HashableDimensionKey> keys;
+ std::set<MetricDimensionKey> keys;
for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
keys.insert(itr->first);
}
@@ -95,7 +95,7 @@ void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs,
// seldomly called. The alternative would be having AnomalyAlarms store information about the
// DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that is
// rarely ever called.
- unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
+ unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
for (const auto& kv : mAlarms) {
if (firedAlarms.count(kv.second) > 0) {
matchedAlarms.insert({kv.first, kv.second});
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 33e55ab850c9..052fdf576289 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -32,10 +32,10 @@ public:
virtual ~DurationAnomalyTracker();
// Starts the alarm at the given timestamp.
- void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+ void startAlarm(const MetricDimensionKey& dimensionKey, const uint64_t& eventTime);
// Stops the alarm.
- void stopAlarm(const HashableDimensionKey& dimensionKey);
+ void stopAlarm(const MetricDimensionKey& dimensionKey);
// Stop all the alarms owned by this tracker.
void stopAllAlarms();
@@ -46,7 +46,7 @@ public:
}
// Declares the anomaly when the alarm expired given the current timestamp.
- void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+ void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
const uint64_t& timestampNs);
// Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
@@ -59,7 +59,7 @@ public:
protected:
// 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;
+ std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms;
// Anomaly alarm monitor.
sp<AnomalyMonitor> mAnomalyMonitor;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4e570a684a06..e64b631b1738 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -95,11 +95,12 @@ message Atom {
AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
ShutdownSequenceReported shutdown_sequence_reported = 56;
BootSequenceReported boot_sequence_reported = 57;
+ DaveyOccurred davey_occurred = 58;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
// Pulled events will start at field 10000.
- // Next: 10019
+ // Next: 10021
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -120,6 +121,8 @@ message Atom {
CpuActiveTime cpu_active_time = 10016;
CpuClusterTime cpu_cluster_time = 10017;
DiskSpace disk_space = 10018;
+ RemainingBatteryCapacity remaining_battery_capacity = 10019;
+ FullBatteryCapacity full_battery_capacity = 10020;
}
}
@@ -721,6 +724,17 @@ message BootSequenceReported {
}
/**
+ * Logs the duration of a davey (jank of >=700ms) when it occurs
+ *
+ * Logged from:
+ * frameworks/base/libs/hwui/JankTracker.cpp
+ */
+message DaveyOccurred {
+ // Amount of time it took to render the frame. Should be >=700ms.
+ optional int64 jank_duration_ms = 1;
+}
+
+/**
* Logs phone signal strength changes.
*
* Logged from:
@@ -757,8 +771,8 @@ message SettingChanged {
// The tag used with the is_default for resetting sets of settings. This is generally null.
optional string tag = 5;
- // 1 indicates that this setting with tag should be resettable.
- optional int32 is_default = 6;
+ // True if this setting with tag should be resettable.
+ optional bool is_default = 6;
// The user ID associated. Defined in android/os/UserHandle.java
optional int32 user = 7;
@@ -1105,9 +1119,12 @@ message IsolatedUidChanged {
optional int32 isolated_uid = 2;
- // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
- // be removed before if it's used for another parent uid.
- optional int32 is_create = 3;
+ // We expect an isolated uid to be removed before if it's used for another parent uid.
+ enum Event {
+ REMOVED = 0;
+ CREATED = 1;
+ }
+ optional Event event = 3;
}
/**
@@ -1410,3 +1427,19 @@ message DiskSpace {
// available bytes in download cache or temp directories
optional uint64 temp_available_bytes = 3;
}
+
+/**
+ * Pulls battery coulomb counter, which is the remaining battery charge in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message RemainingBatteryCapacity {
+ optional int32 charge_uAh = 1;
+}
+
+/**
+ * Pulls battery capacity, which is the battery capacity when full in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message FullBatteryCapacity {
+ optional int32 capacity_uAh = 1;
+}
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index ea6586c25d80..4c20ccb61afe 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -78,6 +78,7 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
return false;
}
+
bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
conditionIdIndexMap, stack);
@@ -88,8 +89,10 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
ALOGW("Child initialization success %lld ", (long long)child);
}
+ if (allConditionTrackers[childIndex]->isSliced()) {
+ setSliced(true);
+ }
mChildren.push_back(childIndex);
-
mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
childTracker->getLogTrackerIndex().end());
}
@@ -105,11 +108,15 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
- vector<ConditionState>& conditionCache) const {
+ const FieldMatcher& dimensionFields,
+ vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ // So far, this is fine as there is at most one child having sliced output.
for (const int childIndex : mChildren) {
if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
- conditionCache);
+ dimensionFields, conditionCache,
+ dimensionsKeySet);
}
}
conditionCache[mIndex] =
@@ -127,6 +134,7 @@ void CombinationConditionTracker::evaluateCondition(
}
for (const int childIndex : mChildren) {
+ // So far, this is fine as there is at most one child having sliced output.
if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
const sp<ConditionTracker>& child = mAllConditions[childIndex];
child->evaluateCondition(event, eventMatcherValues, mAllConditions,
@@ -159,6 +167,24 @@ void CombinationConditionTracker::evaluateCondition(
}
}
+ConditionState CombinationConditionTracker::getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
+ // So far, this is fine as there is at most one child having sliced output.
+ for (const int childIndex : mChildren) {
+ conditionCache[childIndex] = conditionCache[childIndex] |
+ allConditions[childIndex]->getMetConditionDimension(
+ allConditions, dimensionFields, dimensionsKeySet);
+ }
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+ if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
+ dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
+ }
+ return conditionCache[mIndex];
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index dfd3837f31f4..0b7f9492d628 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -41,12 +41,20 @@ public:
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache) override;
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const override;
+ void isConditionMet(
+ const ConditionKey& conditionParameters,
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+ ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
private:
LogicalOperation mLogicalOperation;
+
// Store index of the children Predicates.
// We don't store string name of the Children, because we want to get rid of the hash map to
// map the name to object. We don't want to store smart pointers to children, because it
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 773860f429b1..81abbdb36ee4 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -24,6 +24,7 @@
#include <log/logprint.h>
#include <utils/RefBase.h>
+#include <unordered_set>
#include <unordered_map>
namespace android {
@@ -84,10 +85,19 @@ public:
// [allConditions]: all condition trackers. This is needed because the condition evaluation is
// done recursively
// [conditionCache]: the cache holding the condition evaluation values.
+ // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
+ // condition, it assumes that only one child predicate is sliced.
virtual void isConditionMet(
const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const = 0;
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
+
+ virtual ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
// return the list of LogMatchingTracker index that this ConditionTracker uses.
virtual const std::set<int>& getLogTrackerIndex() const {
@@ -98,6 +108,10 @@ public:
mSliced = mSliced | sliced;
}
+ bool isSliced() const {
+ return mSliced;
+ }
+
protected:
const int64_t mConditionId;
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index d99c2ccd1fda..0427700fec91 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "ConditionWizard.h"
+#include <unordered_set>
namespace android {
namespace os {
@@ -23,14 +24,26 @@ using std::map;
using std::string;
using std::vector;
-ConditionState ConditionWizard::query(const int index,
- const ConditionKey& parameters) {
+ConditionState ConditionWizard::query(
+ const int index, const ConditionKey& parameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet) {
+
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
- mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache);
+ mAllConditions[index]->isConditionMet(
+ parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet);
return cache[index];
}
+ConditionState ConditionWizard::getMetConditionDimension(
+ const int index, const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const {
+
+ return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
+ *dimensionsKeySet);
+}
+
} // namespace statsd
} // namespace os
} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4ff5c07e210f..b38b59ff4cd0 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -41,7 +41,14 @@ public:
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(
const int conditionIndex,
- const ConditionKey& conditionParameters);
+ const ConditionKey& conditionParameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet);
+
+ virtual ConditionState getMetConditionDimension(
+ const int index,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const;
private:
std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 5cfc349ea46a..25265d5dabd7 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -104,6 +104,9 @@ bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
vector<bool>& stack) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
+ if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) {
+ setSliced(true);
+ }
return mInitialized;
}
@@ -234,11 +237,12 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou
conditionChangedCache[mIndex] == true);
}
-void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
+void SimpleConditionTracker::evaluateCondition(
+ const LogEvent& event,
+ const vector<MatchingState>& eventMatcherValues,
+ const vector<sp<ConditionTracker>>& mAllConditions,
+ vector<ConditionState>& conditionCache,
+ vector<bool>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
VLOG("Yes, already evaluated, %lld %d",
@@ -271,7 +275,7 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
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;
+ conditionCache[mIndex] = mInitialValue;
} else {
const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
if (itr == mSlicedConditionState.end()) {
@@ -310,10 +314,8 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
ConditionState::kNotEvaluated);
vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
-
handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
dimensionalConditionCache, dimensionalConditionChangedCache);
-
OrConditionState(dimensionalConditionCache, &conditionCache);
OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
}
@@ -323,49 +325,112 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
void SimpleConditionTracker::isConditionMet(
const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
- vector<ConditionState>& conditionCache) const {
+ const FieldMatcher& dimensionFields,
+ vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ // it has been evaluated.
+ VLOG("Yes, already evaluated, %lld %d",
+ (long long)mConditionId, conditionCache[mIndex]);
+ return;
+ }
const auto pair = conditionParameters.find(mConditionId);
- if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
- ALOGE("Predicate %lld output has dimension, but it's not specified in the query!",
- (long long)mConditionId);
- conditionCache[mIndex] = mInitialValue;
+ if (pair == conditionParameters.end()) {
+ ConditionState conditionState = ConditionState::kNotEvaluated;
+ if (dimensionFields.has_field() && dimensionFields.child_size() > 0 &&
+ dimensionFields.field() == mOutputDimensions.field()) {
+ conditionState = conditionState | getMetConditionDimension(
+ allConditions, dimensionFields, dimensionsKeySet);
+ } else {
+ conditionState = conditionState | mInitialValue;
+ if (!mSliced) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ }
+ }
+ }
+ conditionCache[mIndex] = conditionState;
return;
}
- std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
+ std::vector<HashableDimensionKey> defaultKeys = { DEFAULT_DIMENSION_KEY };
const std::vector<HashableDimensionKey> &keys =
(pair == conditionParameters.end()) ? defaultKeys : pair->second;
ConditionState conditionState = ConditionState::kNotEvaluated;
- for (const auto& key : keys) {
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const HashableDimensionKey& key = keys[i];
auto startedCountIt = mSlicedConditionState.find(key);
if (startedCountIt != mSlicedConditionState.end()) {
- conditionState = conditionState |
- (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+ ConditionState sliceState =
+ startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+ HashableDimensionKey dimensionKey;
+ if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields,
+ dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
} else {
// For unseen key, check whether the require dimensions are subset of sliced condition
// output.
- bool seenDimension = false;
+ conditionState = conditionState | mInitialValue;
for (const auto& slice : mSlicedConditionState) {
- if (IsSubDimension(slice.first.getDimensionsValue(),
- key.getDimensionsValue())) {
- seenDimension = true;
- conditionState = conditionState |
- (slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
- }
- if (conditionState == ConditionState::kTrue) {
- break;
+ ConditionState sliceState =
+ slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) {
+ conditionState = conditionState | sliceState;
+ if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+ HashableDimensionKey dimensionKey;
+ if (getSubDimension(slice.first.getDimensionsValue(),
+ dimensionFields, dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
}
}
- if (!seenDimension) {
- conditionState = conditionState | mInitialValue;
- }
}
}
conditionCache[mIndex] = conditionState;
VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
}
+ConditionState SimpleConditionTracker::getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ ConditionState conditionState = mInitialValue;
+ if (!dimensionFields.has_field() ||
+ !mOutputDimensions.has_field() ||
+ dimensionFields.field() != mOutputDimensions.field()) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ }
+ return conditionState;
+ }
+
+ for (const auto& slice : mSlicedConditionState) {
+ ConditionState sliceState =
+ slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ DimensionsValue dimensionsValue;
+ conditionState = conditionState | sliceState;
+ HashableDimensionKey dimensionKey;
+ if (sliceState == ConditionState::kTrue &&
+ getSubDimension(slice.first.getDimensionsValue(), dimensionFields,
+ dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
+ return conditionState;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 815b445a8c5b..ce9a02d4a795 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -48,7 +48,14 @@ public:
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const override;
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+
+ ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
private:
const ConfigKey mConfigKey;
@@ -73,7 +80,8 @@ private:
void handleStopAll(std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
- void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+ void handleConditionEvent(const HashableDimensionKey& outputKey,
+ bool matchStart,
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 3b2d480b3ebf..0ab33cfbaea1 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -118,6 +118,9 @@ void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored) {
void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField,
std::vector<Field> *allFields) {
+ if (matcher.has_position()) {
+ leafField->set_position_index(0);
+ }
if (matcher.child_size() == 0) {
allFields->push_back(*rootField);
return;
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
index 04445ca0e230..8a2e87128319 100644
--- a/cmds/statsd/src/dimension.cpp
+++ b/cmds/statsd/src/dimension.cpp
@@ -253,6 +253,9 @@ void buildAttributionFieldMatcher(const int tagId, const Position position, Fiel
}
void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
+ if (!value.has_field()) {
+ return;
+ }
*flattened += std::to_string(value.field());
*flattened += ":";
switch (value.value_case()) {
@@ -352,6 +355,46 @@ long getLongFromDimenValue(const DimensionsValue& dimensionValue) {
}
}
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+ DimensionsValue* subDimension) {
+ if (!matcher.has_field()) {
+ return false;
+ }
+ if (matcher.field() != dimension.field()) {
+ return false;
+ }
+ if (matcher.child_size() <= 0) {
+ if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple ||
+ dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) {
+ return false;
+ }
+ *subDimension = dimension;
+ return true;
+ } else {
+ if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) {
+ return false;
+ }
+ bool found_value = true;
+ auto value_tuple = dimension.value_tuple();
+ subDimension->set_field(dimension.field());
+ for (int i = 0; found_value && i < matcher.child_size(); ++i) {
+ int j = 0;
+ for (; j < value_tuple.dimensions_value_size(); ++j) {
+ if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) {
+ break;
+ }
+ }
+ if (j < value_tuple.dimensions_value_size()) {
+ found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i),
+ subDimension->mutable_value_tuple()->add_dimensions_value());
+ } else {
+ found_value = false;
+ }
+ }
+ return found_value;
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index e900c5e87227..138c6e9b0160 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -63,6 +63,9 @@ bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub
// Helper function to get long value from the DimensionsValue proto.
long getLongFromDimenValue(const DimensionsValue& dimensionValue);
+
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+ DimensionsValue* subDimension);
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
new file mode 100644
index 000000000000..72fb5ffd4b90
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include "external/ResourceHealthManagerPuller.h"
+#include "external/StatsPuller.h"
+
+#include "ResourceHealthManagerPuller.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr;
+
+bool getHealthHal() {
+ if (gHealthHal == nullptr) {
+ gHealthHal = get_health_service();
+
+ }
+ return gHealthHal != nullptr;
+}
+
+ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
+}
+
+// TODO: add other health atoms (eg. Temperature).
+bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ if (!getHealthHal()) {
+ ALOGE("Health Hal not loaded");
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+
+ data->clear();
+ bool result_success = true;
+
+ Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
+ if (r != Result::SUCCESS) {
+ result_success = false;
+ return;
+ }
+ if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryChargeCounter);
+ ptr->init();
+ data->push_back(ptr);
+ } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryFullCharge);
+ ptr->init();
+ data->push_back(ptr);
+ } else {
+ ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
+ }
+ });
+ if (!result_success || !ret.isOk()) {
+ ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
+ ret.description().c_str());
+ if (!ret.isOk() && ret.isDeadObject()) {
+ gHealthHal = nullptr;
+ }
+ return false;
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
new file mode 100644
index 000000000000..9b238eaf5f83
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads Ihealth.hal
+ */
+class ResourceHealthManagerPuller : public StatsPuller {
+public:
+ ResourceHealthManagerPuller(int tagId);
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index cadc535f2469..da14434737af 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -59,6 +59,11 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
return ret;
}
+void StatsPuller::ClearCache() {
+ lock_guard<std::mutex> lock(mLock);
+ mCachedData.clear();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 47cc9f01e054..bc7c45f535d1 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -38,6 +38,8 @@ public:
bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ void ClearCache();
+
protected:
// The atom tag id this puller pulls
const int mTagId;
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 00a1475c0829..4826d963a3ed 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -50,10 +50,14 @@ class StatsPullerManager {
return mPullerManager.Pull(tagId, data);
}
- virtual void SetTimeBaseSec(const long timeBaseSec) {
+ void SetTimeBaseSec(const long timeBaseSec) {
mPullerManager.SetTimeBaseSec(timeBaseSec);
}
+ void ClearPullerCache() {
+ mPullerManager.ClearPullerCache();
+ }
+
private:
StatsPullerManagerImpl
& mPullerManager = StatsPullerManagerImpl::GetInstance();
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 148c9ae9b249..71b0abe25d28 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -23,10 +23,11 @@
#include <climits>
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
-#include "SubsystemSleepStatePuller.h"
+#include "ResourceHealthManagerPuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
+#include "SubsystemSleepStatePuller.h"
#include "logd/LogEvent.h"
#include "statslog.h"
@@ -83,7 +84,10 @@ StatsPullerManagerImpl::StatsPullerManagerImpl()
make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
-
+ mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY,
+ make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)});
+ mPullers.insert({android::util::FULL_BATTERY_CAPACITY,
+ make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
@@ -195,6 +199,12 @@ void StatsPullerManagerImpl::OnAlarmFired() {
}
}
+void StatsPullerManagerImpl::ClearPullerCache() {
+ for (auto puller : mPullers) {
+ puller.second->ClearCache();
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 7c59f6659d1f..fba3ade8c1bb 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -49,6 +49,8 @@ public:
void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
+ void ClearPullerCache();
+
private:
StatsPullerManagerImpl();
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 9e72f5bd4b72..1dcd8534db79 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -62,13 +62,14 @@ void LogEvent::init() {
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);
- // destroy the context to save memory.
- if (mContext) {
+ android_log_context contextForRead = create_android_log_parser(buffer, len);
+ if (contextForRead) {
+ init(contextForRead);
+ // destroy the context to save memory.
// android_log_destroy will set mContext to NULL
- android_log_destroy(&mContext);
+ android_log_destroy(&contextForRead);
}
+ android_log_destroy(&mContext);
}
}
@@ -188,6 +189,9 @@ void increaseField(Field *field, bool is_child) {
* of the elements that are written to the log.
*/
void LogEvent::init(android_log_context context) {
+ if (!context) {
+ return;
+ }
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
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 0455f6a210a9..ae4df3ee92f0 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,6 +21,7 @@
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
#include "stats_log_util.h"
+#include "dimension.h"
#include <limits.h>
#include <stdlib.h>
@@ -71,13 +72,15 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -99,7 +102,10 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog
auto count_metrics = report->mutable_count_metrics();
for (const auto& counter : mPastBuckets) {
CountMetricData* metricData = count_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ counter.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ counter.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : counter.second) {
CountBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -123,17 +129,26 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
VLOG("metric %lld dump report now...",(long long)mMetricId);
for (const auto& counter : mPastBuckets) {
- const HashableDimensionKey& hashableKey = counter.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = counter.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
- long long dimensionToken = protoOutput->start(
+ long long dimensionInWhatToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
- protoOutput->end(dimensionToken);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInWhatToken);
+
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
@@ -166,7 +181,7 @@ void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
mCondition = conditionMet;
}
-bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
return false;
}
@@ -187,7 +202,7 @@ bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey)
}
void CountMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 061b7a36817c..8659d4773568 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -50,7 +50,7 @@ public:
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -74,14 +74,14 @@ private:
void flushIfNeededLocked(const uint64_t& newEventTime);
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
// The current bucket.
std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
static const size_t kBucketSize = sizeof(CountBucket{});
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 000874cf8f44..efbdae150792 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -21,6 +21,7 @@
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
#include "stats_log_util.h"
+#include "dimension.h"
#include <limits.h>
#include <stdlib.h>
@@ -81,13 +82,15 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -113,15 +116,17 @@ sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(const Alert &alert)
}
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
- const HashableDimensionKey& eventKey) const {
+ const MetricDimensionKey& eventKey) const {
switch (mAggregationType) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+ mDimensionsInCondition, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+ mDimensionsInCondition, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
}
}
@@ -129,10 +134,34 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
flushIfNeededLocked(eventTime);
+
// Now for each of the on-going event, check if the condition has changed for them.
- for (auto& pair : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->onSlicedConditionMayChange(eventTime);
}
+
+
+ std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
+ ConditionState conditionState = mWizard->getMetConditionDimension(
+ mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet);
+
+ bool condition = (conditionState == ConditionState::kTrue);
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+ conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition());
+ }
+ std::unordered_set<MetricDimensionKey> newKeys;
+ for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+ auto newKey =
+ MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey);
+ if (newKeys.find(newKey) == newKeys.end()) {
+ mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime);
+ mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey);
+ mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime);
+ }
+ newKeys.insert(newKey);
+ }
+ }
}
void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
@@ -142,7 +171,7 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
flushIfNeededLocked(eventTime);
// TODO: need to populate the condition change time from the event which triggers the condition
// change, instead of using current time.
- for (auto& pair : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -155,7 +184,10 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, Stats
auto duration_metrics = report->mutable_duration_metrics();
for (const auto& pair : mPastBuckets) {
DurationMetricData* metricData = duration_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ pair.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ pair.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : pair.second) {
auto bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -179,8 +211,8 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
VLOG("metric %lld dump report now...", (long long)mMetricId);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = pair.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -188,9 +220,18 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
+
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
long long bucketInfoToken = protoOutput->start(
@@ -219,10 +260,11 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) {
return;
}
VLOG("flushing...........");
- for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
+ for (auto it = mCurrentSlicedDurationTrackerMap.begin();
+ it != mCurrentSlicedDurationTrackerMap.end();) {
if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) {
VLOG("erase bucket for key %s", it->first.c_str());
- it = mCurrentSlicedDuration.erase(it);
+ it = mCurrentSlicedDurationTrackerMap.erase(it);
} else {
++it;
}
@@ -234,28 +276,28 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) {
}
void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
- if (mCurrentSlicedDuration.size() == 0) {
+ if (mCurrentSlicedDurationTrackerMap.size() == 0) {
return;
}
fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
- (unsigned long)mCurrentSlicedDuration.size());
+ (unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
- for (const auto& slice : mCurrentSlicedDuration) {
+ for (const auto& slice : mCurrentSlicedDurationTrackerMap) {
fprintf(out, "\t%s\n", slice.first.c_str());
slice.second->dumpStates(out, verbose);
}
}
}
-bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// the key is not new, we are good.
- if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+ if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) {
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+ if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -268,27 +310,26 @@ bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newK
}
void DurationMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) {
flushIfNeededLocked(event.GetTimestampNs());
if (matcherIndex == mStopAllIndex) {
- for (auto& pair : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->noteStopAll(event.GetTimestampNs());
}
return;
}
-
- if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+ if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
- mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey);
+ mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey);
}
- auto it = mCurrentSlicedDuration.find(eventKey);
+ auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
std::vector<DimensionsValue> values;
getDimensionKeys(event, mInternalDimensions, &values);
@@ -302,10 +343,11 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked(
} else {
for (const DimensionsValue& value : values) {
if (matcherIndex == mStartIndex) {
- it->second->noteStart(HashableDimensionKey(value), condition,
- event.GetTimestampNs(), conditionKeys);
+ it->second->noteStart(
+ HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys);
} else if (matcherIndex == mStopIndex) {
- it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false);
+ it->second->noteStop(
+ HashableDimensionKey(value), event.GetTimestampNs(), false);
}
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index d8cab92a2b84..152e570df467 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -50,7 +50,7 @@ public:
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) override;
@@ -92,21 +92,21 @@ private:
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The current bucket.
- std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
- mCurrentSlicedDuration;
+ std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>>
+ mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
std::unique_ptr<DurationTracker> createDurationTracker(
- const HashableDimensionKey& eventKey) const;
+ const MetricDimensionKey& eventKey) const;
// This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(DurationBucket{});
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 25c86d0d46f1..820d5918a92a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -128,7 +128,7 @@ void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
}
void EventMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (!condition) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 9da0dd0569d6..935f206017fa 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -45,7 +45,7 @@ protected:
private:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1072c5aae6e4..d6cb1891288a 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -82,13 +82,15 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric
mFieldFilter = metric.gauge_fields_filter();
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
// Kicks off the puller immediately.
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
@@ -136,18 +138,27 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
+ const MetricDimensionKey& dimensionKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
+
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
long long bucketInfoToken = protoOutput->start(
@@ -248,7 +259,7 @@ void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
}
}
-bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
return false;
}
@@ -268,7 +279,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey)
}
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (condition == false) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6c013477af37..86d0ccd241eb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -44,7 +44,7 @@ struct GaugeBucket {
uint64_t mBucketNum;
};
-typedef std::unordered_map<HashableDimensionKey, std::vector<GaugeAtom>>
+typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
DimToGaugeAtomsMap;
// This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -64,7 +64,7 @@ public:
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -99,7 +99,7 @@ private:
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current bucket.
std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
@@ -119,7 +119,7 @@ private:
std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(GaugeBucket{});
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index e74924a81fbf..85e655b08f4d 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -15,6 +15,8 @@
*/
#include "MetricProducer.h"
+#include "dimension.h"
+
namespace android {
namespace os {
namespace statsd {
@@ -30,29 +32,51 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
bool condition;
ConditionKey conditionKey;
+
+ std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mConditionLinks) {
getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]);
}
- if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) {
- condition = false;
- } else {
- condition = true;
- }
+ auto conditionState =
+ mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+ &dimensionKeysInCondition);
+ condition = (conditionState == ConditionState::kTrue);
} else {
condition = mCondition;
}
- if (mDimensions.has_field() && mDimensions.child_size() > 0) {
- vector<DimensionsValue> dimensionValues;
- getDimensionKeys(event, mDimensions, &dimensionValues);
- for (const DimensionsValue& dimensionValue : dimensionValues) {
+ vector<DimensionsValue> dimensionInWhatValues;
+ if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) {
+ getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues);
+ }
+
+ if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
+ } else if (dimensionKeysInCondition.empty()) {
+ for (const DimensionsValue& whatValue : dimensionInWhatValues) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex,
+ MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY),
+ conditionKey, condition, event);
+ }
+ } else if (dimensionInWhatValues.empty()) {
+ for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
onMatchedLogEventInternalLocked(
- matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event);
+ matcherIndex,
+ MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey),
+ conditionKey, condition, event);
}
} else {
- onMatchedLogEventInternalLocked(
- matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event);
+ for (const DimensionsValue& whatValue : dimensionInWhatValues) {
+ for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex,
+ MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey),
+ conditionKey, condition, event);
+ }
+ }
}
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 6f33073c633c..3b1498f07632 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -91,6 +91,7 @@ public:
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, protoOutput);
}
+
void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) {
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, report);
@@ -156,7 +157,8 @@ protected:
int mConditionTrackerIndex;
- FieldMatcher mDimensions; // The dimension defined in statsd_config
+ FieldMatcher mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
+ FieldMatcher mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config
std::vector<MetricConditionLink> mConditionLinks;
@@ -178,7 +180,7 @@ protected:
* [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
*/
virtual void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index d0737de8acf3..636289522780 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -189,13 +189,7 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
return;
}
- if (event.GetTagId() != android::util::APP_HOOK) {
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
- VLOG("log source %d not on the whitelist", event.GetUid());
- return;
- }
- } else { // Check that app hook fields are valid.
+ if (event.GetTagId() == android::util::APP_HOOK) { // Check that app hook fields are valid.
// TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
// Label is 2nd from last field and must be from [0, 15].
@@ -211,6 +205,21 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
VLOG("App hook does not have valid state %ld", apphookState);
return;
}
+ } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
+ // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
+ // Check that the davey duration is reasonable. Max length check is for privacy.
+ status_t err = NO_ERROR;
+ long duration = event.GetLong(event.size(), &err);
+ if (err != NO_ERROR || duration > 100000) {
+ VLOG("Davey duration is unreasonably long: %ld", duration);
+ return;
+ }
+ } else {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
+ VLOG("log source %d not on the whitelist", event.GetUid());
+ return;
+ }
}
int tagId = event.GetTagId();
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 9cdbafc75fb1..d4b9102d5ddc 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -143,6 +143,10 @@ private:
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index ae0c673e3490..c9cc7bb9c1d4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -81,13 +81,15 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric
}
mBucketSizeNs = bucketSizeMills * 1000000;
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
if (!metric.has_condition() && mPullTagId != -1) {
VLOG("Setting up periodic pulling for %d", mPullTagId);
@@ -124,7 +126,10 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog
auto value_metrics = report->mutable_value_metrics();
for (const auto& pair : mPastBuckets) {
ValueMetricData* metricData = value_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ pair.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ pair.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : pair.second) {
ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -146,16 +151,24 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = pair.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
@@ -239,7 +252,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
}
}
-bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -260,7 +273,7 @@ bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey)
}
void ValueMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 9f750cf419b5..121ec7d18515 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -49,7 +49,7 @@ public:
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -99,16 +99,16 @@ private:
long sum;
} Interval;
- std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
+ std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(ValueBucket{});
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index c2d2cea2a1ff..45735a866978 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -60,8 +60,9 @@ struct DurationBucket {
class DurationTracker {
public:
- DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
: mConfigKey(key),
@@ -70,6 +71,7 @@ public:
mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
mBucketSizeNs(bucketSizeNs),
+ mDimensionInCondition(dimensionInCondition),
mNested(nesting),
mCurrentBucketStartTimeNs(currentBucketStartNs),
mDuration(0),
@@ -79,6 +81,8 @@ public:
virtual ~DurationTracker(){};
+ virtual unique_ptr<DurationTracker> clone(const uint64_t eventTime) = 0;
+
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,
@@ -92,7 +96,7 @@ public:
// events, so that the owner can safely remove the tracker.
virtual bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
// Predict the anomaly timestamp given the current status.
virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
@@ -100,6 +104,10 @@ public:
// Dump internal states for debugging
virtual void dumpStates(FILE* out, bool verbose) const = 0;
+ void setEventKey(const MetricDimensionKey& eventKey) {
+ mEventKey = eventKey;
+ }
+
protected:
// Starts the anomaly alarm.
void startAnomalyAlarm(const uint64_t eventTime) {
@@ -150,7 +158,7 @@ protected:
const int64_t mTrackerId;
- HashableDimensionKey mEventKey;
+ MetricDimensionKey mEventKey;
sp<ConditionWizard> mWizard;
@@ -158,6 +166,8 @@ protected:
const int64_t mBucketSizeNs;
+ const FieldMatcher mDimensionInCondition;
+
const bool mNested;
uint64_t mCurrentBucketStartTimeNs;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 412a0c935766..db7dea4afd04 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -25,13 +25,23 @@ namespace os {
namespace statsd {
MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
bool conditionSliced,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- bucketSizeNs, conditionSliced, anomalyTrackers) {
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+ currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers) {
+}
+
+unique_ptr<DurationTracker> MaxDurationTracker::clone(const uint64_t eventTime) {
+ auto clonedTracker = make_unique<MaxDurationTracker>(*this);
+ for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) {
+ it->second.lastStartTime = eventTime;
+ it->second.lastDuration = 0;
+ }
+ return clonedTracker;
}
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -44,7 +54,7 @@ bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mInfos.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
- mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+ mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -149,7 +159,7 @@ void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
}
bool MaxDurationTracker::flushIfNeeded(
- uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+ uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
return false;
}
@@ -236,8 +246,14 @@ void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
if (pair.second.state == kStopped) {
continue;
}
- bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
- ConditionState::kTrue;
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState = mWizard->query(
+ mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
+ &conditionDimensionKeySet);
+ bool conditionMet = (conditionState == ConditionState::kTrue) &&
+ (!mDimensionInCondition.has_field() ||
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
+ conditionDimensionKeySet.end());
VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
noteConditionChanged(pair.first, conditionMet, timestamp);
}
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 661d1311293a..4d32a0637d56 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -29,10 +29,15 @@ namespace statsd {
class MaxDurationTracker : public DurationTracker {
public:
MaxDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, bool conditionSliced,
+ const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+
+ MaxDurationTracker(const MaxDurationTracker& tracker) = default;
+
+ unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -41,7 +46,7 @@ public:
bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
void onSlicedConditionMayChange(const uint64_t timestamp) override;
void onConditionChanged(bool condition, const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 75d7c0898d78..0feae369b0d5 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -25,17 +25,25 @@ namespace statsd {
using std::pair;
OringDurationTracker::OringDurationTracker(
- const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs,
uint64_t bucketSizeNs, bool conditionSliced,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- bucketSizeNs, conditionSliced, anomalyTrackers),
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+ currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers),
mStarted(),
mPaused() {
mLastStartTime = 0;
}
+unique_ptr<DurationTracker> OringDurationTracker::clone(const uint64_t eventTime) {
+ auto clonedTracker = make_unique<OringDurationTracker>(*this);
+ clonedTracker->mLastStartTime = eventTime;
+ clonedTracker->mDuration = 0;
+ return clonedTracker;
+}
+
bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
@@ -45,7 +53,7 @@ bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mConditionKeyMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
- mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+ mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -76,7 +84,6 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi
if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
mConditionKeyMap[key] = conditionKey;
}
-
VLOG("Oring: %s start, condition %d", key.c_str(), condition);
}
@@ -128,7 +135,7 @@ void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
}
bool OringDurationTracker::flushIfNeeded(
- uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+ uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return false;
}
@@ -184,8 +191,14 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
++it;
continue;
}
- if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
- ConditionState::kTrue) {
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState =
+ mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+ mDimensionInCondition, &conditionDimensionKeySet);
+ if (conditionState != ConditionState::kTrue ||
+ (mDimensionInCondition.has_field() &&
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
+ conditionDimensionKeySet.end())) {
startedToPaused.push_back(*it);
it = mStarted.erase(it);
VLOG("Key %s started -> paused", key.c_str());
@@ -210,8 +223,14 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
++it;
continue;
}
- if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
- ConditionState::kTrue) {
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState =
+ mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+ mDimensionInCondition, &conditionDimensionKeySet);
+ if (conditionState == ConditionState::kTrue &&
+ (!mDimensionInCondition.has_field() ||
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition())
+ != conditionDimensionKeySet.end())) {
pausedToStarted.push_back(*it);
it = mPaused.erase(it);
VLOG("Key %s paused -> started", key.c_str());
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 43469ca9a551..75b5a815de9c 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -28,11 +28,15 @@ namespace statsd {
class OringDurationTracker : public DurationTracker {
public:
OringDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, bool conditionSliced,
+ const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+ OringDurationTracker(const OringDurationTracker& tracker) = default;
+
+ unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -44,7 +48,7 @@ public:
bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
const uint64_t currentTimestamp) const override;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a41f30c2bece..6c6140081bd8 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -54,6 +54,9 @@ const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4;
void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
ProtoOutputStream* protoOutput) {
+ if (!dimensionsValue.has_field()) {
+ return;
+ }
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
switch (dimensionsValue.value_case()) {
case DimensionsValue::ValueCase::kValueStr:
@@ -103,6 +106,9 @@ const int FIELD_CHILD = 3;
void writeFieldProtoToStream(
const Field& field, util::ProtoOutputStream* protoOutput) {
+ if (!field.has_field()) {
+ return;
+ }
protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
if (field.has_position_index()) {
protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 160b1f40243f..31f51a7ac0a4 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -28,13 +28,14 @@ namespace os {
namespace statsd {
const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
+const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey();
// Minimum bucket size in seconds
const long kMinBucketSizeSec = 5 * 60;
typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey;
-typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
+typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap;
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index f912e4b2cd24..3af684fa6069 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -56,7 +56,7 @@ void SubscriberReporter::removeConfig(const ConfigKey& configKey) {
void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const {
+ const MetricDimensionKey& dimKey) const {
// Reminder about ids:
// subscription id - name of the Subscription (that ties the Alert to the broadcast)
// subscription rule_id - the name of the Alert (that triggers the broadcast)
@@ -92,7 +92,7 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const {
+ const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
if (mStatsCompanionService == nullptr) {
ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
@@ -107,8 +107,8 @@ void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
}
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
- const HashableDimensionKey& dimKey) {
- return protoToStatsDimensionsValue(dimKey.getDimensionsValue());
+ const MetricDimensionKey& dimKey) {
+ return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue());
}
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 5bb458a8b1d8..13fc7fd06279 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -80,7 +80,7 @@ public:
*/
void alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const;
+ const MetricDimensionKey& dimKey) const;
private:
SubscriberReporter() {};
@@ -101,7 +101,7 @@ private:
void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const;
+ const MetricDimensionKey& dimKey) const;
/** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
static StatsDimensionsValue protoToStatsDimensionsValue(
@@ -109,7 +109,7 @@ private:
/** Converts a HashableDimensionKey to a StatsDimensionsValue. */
static StatsDimensionsValue protoToStatsDimensionsValue(
- const HashableDimensionKey& dimKey);
+ const MetricDimensionKey& dimKey);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 66bfa68ecc55..a415ea1a3069 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -33,14 +33,14 @@ namespace statsd {
const ConfigKey kConfigKey(0, 12345);
-HashableDimensionKey getMockDimensionKey(int key, string value) {
+MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
DimensionsValue dimensionsValue;
dimensionsValue.set_field(key);
dimensionsValue.set_value_str(value);
- return HashableDimensionKey(dimensionsValue);
+ return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
}
-void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
+void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
std::shared_ptr<DimToValMap> bucket) {
for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
(*bucket)[itr->first] += itr->second;
@@ -48,7 +48,7 @@ void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>&
}
std::shared_ptr<DimToValMap> MockBucket(
- const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list) {
+ const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
AddValueToBucket(key_value_pair_list, bucket);
return bucket;
@@ -56,7 +56,7 @@ std::shared_ptr<DimToValMap> MockBucket(
// Returns the value, for the given key, in that bucket, or 0 if not present.
int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
- const HashableDimensionKey& key) {
+ const MetricDimensionKey& key) {
const auto& itr = bucket->find(key);
if (itr != bucket->end()) {
return itr->second;
@@ -68,14 +68,14 @@ int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
bool detectAnomaliesPass(AnomalyTracker& tracker,
const int64_t& bucketNum,
const std::shared_ptr<DimToValMap>& currentBucket,
- const std::set<const HashableDimensionKey>& trueList,
- const std::set<const HashableDimensionKey>& falseList) {
- for (HashableDimensionKey key : trueList) {
+ const std::set<const MetricDimensionKey>& trueList,
+ const std::set<const MetricDimensionKey>& falseList) {
+ for (MetricDimensionKey key : trueList) {
if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
}
- for (HashableDimensionKey key : falseList) {
+ for (MetricDimensionKey key : falseList) {
if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
@@ -100,7 +100,7 @@ void detectAndDeclareAnomalies(AnomalyTracker& tracker,
void checkRefractoryTimes(AnomalyTracker& tracker,
const int64_t& currTimestampNs,
const int32_t& refractoryPeriodSec,
- const std::unordered_map<HashableDimensionKey, int64_t>& timestamps) {
+ const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
for (const auto& kv : timestamps) {
if (kv.second < 0) {
// Make sure that, if there is a refractory period, it is already past.
@@ -124,9 +124,9 @@ TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
alert.set_trigger_if_sum_gt(2);
AnomalyTracker anomalyTracker(alert, kConfigKey);
- HashableDimensionKey keyA = getMockDimensionKey(1, "a");
- HashableDimensionKey keyB = getMockDimensionKey(1, "b");
- HashableDimensionKey keyC = getMockDimensionKey(1, "c");
+ MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+ MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+ MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
int64_t eventTimestamp0 = 10 * NS_PER_SEC;
int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
@@ -269,11 +269,11 @@ TEST(AnomalyTrackerTest, TestSparseBuckets) {
alert.set_trigger_if_sum_gt(2);
AnomalyTracker anomalyTracker(alert, kConfigKey);
- HashableDimensionKey keyA = getMockDimensionKey(1, "a");
- HashableDimensionKey keyB = getMockDimensionKey(1, "b");
- HashableDimensionKey keyC = getMockDimensionKey(1, "c");
- HashableDimensionKey keyD = getMockDimensionKey(1, "d");
- HashableDimensionKey keyE = getMockDimensionKey(1, "e");
+ MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+ MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+ MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
+ MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
+ MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 819f2bebf327..d1b7b2842fd2 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -78,7 +78,7 @@ void makeWakeLockEvent(
std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
const Position position,
const std::vector<int> &uids, const string& conditionName) {
- std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
+ std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
std::vector<int> uid_indexes;
switch(position) {
case Position::FIRST:
@@ -265,6 +265,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
for (Position position :
{ Position::ANY, Position::FIRST, Position::LAST}) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
+
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
position);
@@ -307,7 +310,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
@@ -361,7 +365,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
@@ -369,6 +374,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
}
TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
+
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
Position::ANY /* position */);
@@ -410,7 +418,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
ConditionKey queryKey;
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
@@ -452,13 +461,17 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ dimensionKeys.clear();
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
TEST(SimpleConditionTrackerTest, TestStopAll) {
for (Position position :
{Position::ANY, Position::FIRST, Position::LAST}) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
position);
@@ -502,7 +515,8 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by uid2
@@ -528,8 +542,9 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
// TEST QUERY
const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -550,15 +565,15 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
// TEST QUERY
const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
// TEST QUERY
const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp
new file mode 100644
index 000000000000..678abaed5fd8
--- /dev/null
+++ b/cmds/statsd/tests/dimension_test.cpp
@@ -0,0 +1,149 @@
+// 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 "dimension.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+
+TEST(DimensionTest, subLeafNodes) {
+ DimensionsValue dimension;
+ int tagId = 100;
+ dimension.set_field(tagId);
+ auto child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(1);
+ child->set_value_int(2000);
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(3);
+ child->set_value_str("test");
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(4);
+ auto grandChild = child->mutable_value_tuple()->add_dimensions_value();
+ grandChild->set_field(1);
+ grandChild->set_value_float(1.3f);
+ grandChild = child->mutable_value_tuple()->add_dimensions_value();
+ grandChild->set_field(3);
+ grandChild->set_value_str("tag");
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(6);
+ child->set_value_bool(false);
+
+ DimensionsValue sub_dimension;
+ FieldMatcher matcher;
+
+ // Tag id not matched.
+ matcher.set_field(tagId + 1);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field not exist.
+ matcher.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(5);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(6);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Not leaf field.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(4);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Grand-child leaf field not exist.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ auto childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(2);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Grand-child leaf field.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Multiple grand-child fields.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Multiple fields.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ matcher.add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Subset of the fields not exist.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ matcher.add_child()->set_field(2);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
new file mode 100644
index 000000000000..b5d48efb55f6
--- /dev/null
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -0,0 +1,725 @@
+// 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateCountMetricWithNoLinkConfig() {
+ StatsdConfig config;
+ auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenIsOffPredicate;
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by any attribution node and both by uid and tag.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidAndTagDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate);
+
+ auto metric = config.add_count_metric();
+ metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
+ metric->set_what(screenBrightnessChangeAtomMatcher.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ metric->set_bucket(ONE_MINUTE);
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) {
+ ConfigKey cfgKey;
+ auto config = CreateCountMetricWithNoLinkConfig();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10));
+
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 200));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
+
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50));
+
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 11));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 101));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 201));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + 203));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs - 99));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs - 2));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + bucketSizeNs - 1));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs + 2));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+ EXPECT_EQ(countMetrics.data_size(), 7);
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs );
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 3);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).count(), 1);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+ data = countMetrics.data(5);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(6);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+}
+
+namespace {
+
+StatsdConfig CreateCountMetricWithLinkConfig() {
+ StatsdConfig config;
+ auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+ *config.add_atom_matcher() = appCrashMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field*/);
+
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_count_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("AppCrashMetric"));
+ metric->set_what(appCrashMatcher.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+ // Links between crash atom and condition of app is in syncing.
+ auto links = metric->add_links();
+ links->set_condition(isSyncingPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) {
+ ConfigKey cfgKey;
+ auto config = CreateCountMetricWithLinkConfig();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11));
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101));
+ events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101));
+
+ events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201));
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211));
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401));
+ events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401));
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301));
+
+ events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 300));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+ bucketStartTimeNs + bucketSizeNs - 1));
+
+ events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 600));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+ EXPECT_EQ(countMetrics.data_size(), 5);
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).count(), 1);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+ *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto inBatterySaverModePredicate = CreateBatterySaverModePredicate();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field */);
+
+ *config.add_predicate() = inBatterySaverModePredicate;
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_duration_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("BatterySaverModeDurationMetric"));
+ metric->set_what(inBatterySaverModePredicate.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
+ for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+ ConfigKey cfgKey;
+ auto config = CreateDurationMetricConfigNoLink(aggregationType);
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1));
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 300));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+ bucketStartTimeNs + bucketSizeNs - 1));
+
+ events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+ events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 700));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::DurationMetricDataWrapper metrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+ EXPECT_EQ(metrics.data_size(), 3);
+ auto data = metrics.data(0);
+ EXPECT_FALSE(data.dimensions_in_what().has_field());
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = metrics.data(1);
+ EXPECT_FALSE(data.dimensions_in_what().has_field());
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = metrics.data(2);
+ EXPECT_FALSE(data.dimensions_in_what().has_field());
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ }
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field */);
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ *config.add_predicate() = isInBackgroundPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_duration_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("AppInBackgroundMetric"));
+ metric->set_what(isInBackgroundPredicate.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+ // Links between crash atom and condition of app is in syncing.
+ auto links = metric->add_links();
+ links->set_condition(isSyncingPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
+ for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+ ConfigKey cfgKey;
+ auto config = CreateDurationMetricConfigWithLink(aggregationType);
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101));
+ events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110));
+
+ events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201));
+ events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100));
+
+ events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
+ events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 300));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+ bucketStartTimeNs + bucketSizeNs - 1));
+
+ events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+ events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 700));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::DurationMetricDataWrapper metrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+ EXPECT_EQ(metrics.data_size(), 3);
+ auto data = metrics.data(0);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = metrics.data(1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = metrics.data(2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 4504a95c8ef0..233031c5c8da 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -44,9 +44,10 @@ StatsdConfig CreateStatsdConfig() {
auto screenIsOffPredicate = CreateScreenIsOffPredicate();
auto isSyncingPredicate = CreateIsSyncingPredicate();
- *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(
- android::util::SYNC_STATE_CHANGED, {1 /* uid field */, 2 /* name field*/});
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field*/);
auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
@@ -78,9 +79,8 @@ StatsdConfig CreateStatsdConfig() {
auto dimensionWhat = links->mutable_fields_in_what();
dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
- auto dimensionCondition = links->mutable_fields_in_condition();
- dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
- dimensionCondition->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
// Links between crash atom and condition of app is in background.
links = countMetric->add_links();
@@ -88,7 +88,7 @@ StatsdConfig CreateStatsdConfig() {
dimensionWhat = links->mutable_fields_in_what();
dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
- dimensionCondition = links->mutable_fields_in_condition();
+ auto dimensionCondition = links->mutable_fields_in_condition();
dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
dimensionCondition->add_child()->set_field(1); // uid field.
return config;
@@ -132,12 +132,14 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) {
CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
bucketStartTimeNs + 2 * bucketSizeNs - 100);
+ std::vector<AttributionNode> attributions =
+ {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
auto syncOnEvent1 =
- CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50);
+ CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
auto syncOffEvent1 =
- CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+ CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
auto syncOnEvent2 =
- CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+ CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
auto moveToBackgroundEvent1 =
CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 4ad209712905..50b3532e827a 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -67,9 +67,9 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
// Flushes.
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -80,10 +80,10 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size());
- const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1];
+ EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
EXPECT_EQ(1LL, bucketInfo2.mCount);
@@ -91,9 +91,9 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
// nothing happens in bucket 3. we should not record anything for bucket 3.
countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(2UL, buckets3.size());
}
@@ -124,10 +124,10 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
{
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -167,9 +167,9 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
{getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+ EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
- EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+ EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
bucketStartTimeNs);
@@ -181,9 +181,9 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -229,13 +229,13 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// Two events in bucket #3.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
@@ -244,13 +244,13 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
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->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
}
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index a59f1fe66354..c9fe2523c577 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -62,9 +62,9 @@ TEST(DurationMetricTrackerTest, TestNoCondition) {
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
- const auto& buckets = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(2UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -107,9 +107,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
- const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets2.size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index da00cae125c7..3deab3710dd1 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -114,9 +114,9 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+ EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
- EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+ EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 4533ac610057..58be5b071b53 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -218,7 +218,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
EXPECT_EQ(13L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
std::shared_ptr<LogEvent> event2 =
std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
@@ -231,7 +231,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
EXPECT_EQ(15L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
std::shared_ptr<LogEvent> event3 =
@@ -245,7 +245,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
EXPECT_EQ(26L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
// The event4 does not have the gauge field. Thus the current bucket value is 0.
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 0772b0d4001d..203f028080d2 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -41,22 +41,24 @@ const ConfigKey kConfigKey(0, 12345);
const int TagId = 1;
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1");
-const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
-const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
-const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
// Event starts again. This would not change anything as it already starts.
@@ -75,16 +77,22 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
}
TEST(MaxDurationTrackerTest, TestStopAll) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
@@ -105,21 +113,26 @@ TEST(MaxDurationTrackerTest, TestStopAll) {
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
// The event starts.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
- // Starts again. Does not change anything.
+ // Starts again. Does not DEFAULT_DIMENSION_KEY anything.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
ConditionKey());
@@ -135,16 +148,21 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
// 2 starts
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -160,7 +178,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
// real stop now.
- tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+ tracker.noteStop(DEFAULT_DIMENSION_KEY,
+ bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
EXPECT_EQ(3u, buckets[eventKey].size());
@@ -170,16 +189,20 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
}
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
- HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
+ MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
- EXPECT_CALL(*wizard, query(_, conditionKey1)) // #4
+ EXPECT_CALL(*wizard, query(_, conditionKey1, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -187,8 +210,8 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
int64_t durationTimeNs = 2 * 1000;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
- bucketSizeNs, true, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, true, {});
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
@@ -204,6 +227,10 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
}
TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
int64_t metricId = 1;
Alert alert;
alert.set_id(101);
@@ -213,7 +240,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
const int32_t refPeriodSec = 1;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -221,8 +248,8 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
- bucketSizeNs, false, {anomalyTracker});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey());
tracker.noteStop(key1, eventStartTimeNs + 10, false);
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 6b8893e5973f..80e16a1f3b55 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -38,24 +38,26 @@ namespace statsd {
const ConfigKey kConfigKey(0, 12345);
const int TagId = 1;
const int64_t metricId = 123;
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
-
-const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
-const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
-const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
TEST(OringDurationTrackerTest, TestDurationOverlap) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, 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(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -71,16 +73,23 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) {
}
TEST(OringDurationTrackerTest, TestDurationNested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -95,16 +104,23 @@ TEST(OringDurationTrackerTest, TestDurationNested) {
}
TEST(OringDurationTrackerTest, TestStopAll) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -118,17 +134,24 @@ TEST(OringDurationTrackerTest, TestStopAll) {
}
TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, 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(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -150,23 +173,30 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
}
TEST(OringDurationTrackerTest, TestDurationConditionChange) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1)) // #4
+ EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, 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(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -181,25 +211,32 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
}
TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1))
+ EXPECT_CALL(*wizard, query(_, key1, _, _))
.Times(2)
.WillOnce(Return(ConditionState::kFalse))
.WillOnce(Return(ConditionState::kTrue));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, 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(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
// condition to false; record duration 5n
@@ -216,22 +253,29 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
}
TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1)) // #4
+ EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
@@ -249,6 +293,13 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
}
TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -256,7 +307,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
alert.set_num_buckets(2);
alert.set_refractory_period_secs(1);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -264,8 +315,8 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, true, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker});
// Nothing in the past bucket.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
@@ -310,6 +361,12 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
}
TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -318,7 +375,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -326,8 +383,8 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
@@ -352,6 +409,13 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
}
TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -360,7 +424,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conkey;
conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
@@ -369,8 +433,9 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false,
+ {anomalyTracker});
tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index fff3dbf5428d..55c078dcbab8 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -299,26 +299,26 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) {
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
// Value sum == 30 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
// Value sum == 130 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// Three events in bucket #3.
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
// Anomaly at event 4 since Value sum == 131 > 130!
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
// Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
// Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
}
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index fc7245ca54e3..ab9345af172e 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -26,6 +26,13 @@ HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) {
return HashableDimensionKey(dimensionsValue);
}
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) {
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_field(tagId);
+ dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
+ dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
+ return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
+}
} // namespace statsd
} // namespace os
} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 23e86f92f844..0a97456c5684 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -25,10 +25,12 @@ namespace statsd {
class MockConditionWizard : public ConditionWizard {
public:
- MOCK_METHOD2(
+ MOCK_METHOD4(
query,
ConditionState(const int conditionIndex,
- const ConditionKey& conditionParameters));
+ const ConditionKey& conditionParameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet));
};
class MockStatsPullerManager : public StatsPullerManager {
@@ -39,6 +41,7 @@ public:
};
HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 9f4582dc6994..13055cb9e7a3 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <gtest/gtest.h>
#include "statsd_test_util.h"
namespace android {
@@ -27,6 +26,22 @@ AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
return atom_matcher;
}
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
+ return atom_matcher;
+}
+
+AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId("UidProcessStateChanged"));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
+ return atom_matcher;
+}
+
AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
WakelockStateChanged::State state) {
AtomMatcher atom_matcher;
@@ -47,6 +62,30 @@ AtomMatcher CreateReleaseWakelockAtomMatcher() {
return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
}
+AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher(
+ const string& name, BatterySaverModeStateChanged::State state) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId(name));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::BATTERY_SAVER_MODE_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(1); // State field.
+ field_value_matcher->set_eq_int(state);
+ return atom_matcher;
+}
+
+AtomMatcher CreateBatterySaverModeStartAtomMatcher() {
+ return CreateBatterySaverModeStateChangedAtomMatcher(
+ "BatterySaverModeStart", BatterySaverModeStateChanged::ON);
+}
+
+
+AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
+ return CreateBatterySaverModeStateChangedAtomMatcher(
+ "BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
+}
+
+
AtomMatcher CreateScreenStateChangedAtomMatcher(
const string& name, android::view::DisplayStateEnum state) {
AtomMatcher atom_matcher;
@@ -59,6 +98,7 @@ AtomMatcher CreateScreenStateChangedAtomMatcher(
return atom_matcher;
}
+
AtomMatcher CreateScreenTurnedOnAtomMatcher() {
return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
@@ -128,6 +168,13 @@ AtomMatcher CreateProcessCrashAtomMatcher() {
"ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
}
+Predicate CreateBatterySaverModePredicate() {
+ Predicate predicate;
+ predicate.set_id(StringToId("BatterySaverIsOn"));
+ predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
+ predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
+ return predicate;
+}
Predicate CreateScreenIsOnPredicate() {
Predicate predicate;
@@ -218,6 +265,31 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
return event;
}
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(
+ android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(
+ android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+ int level, uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(level));
+ event->init();
+ return event;
+
+}
+
std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
const std::vector<AttributionNode>& attributions, const string& wakelockName,
const WakelockStateChanged::State state, uint64_t timestampNs) {
@@ -267,9 +339,10 @@ std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t ti
}
std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
- const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+ const std::vector<AttributionNode>& attributions,
+ const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
- event->write(uid);
+ event->write(attributions);
event->write(name);
event->write(state);
event->init();
@@ -277,13 +350,13 @@ std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
}
std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const int uid, const string& name, uint64_t timestampNs){
- return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs){
+ return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
}
std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const int uid, const string& name, uint64_t timestampNs) {
- return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs) {
+ return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
}
std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index ff8fef0c46b6..6638893f6aeb 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,6 +14,7 @@
#pragma once
+#include <gtest/gtest.h>
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "statslog.h"
#include "src/logd/LogEvent.h"
@@ -26,6 +27,18 @@ namespace statsd {
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
+// Create AtomMatcher proto for screen brightness state changed.
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
+
+// Create AtomMatcher proto for starting battery save mode.
+AtomMatcher CreateBatterySaverModeStartAtomMatcher();
+
+// Create AtomMatcher proto for stopping battery save mode.
+AtomMatcher CreateBatterySaverModeStopAtomMatcher();
+
+// Create AtomMatcher proto for process state changed.
+AtomMatcher CreateUidProcessStateChangedAtomMatcher();
+
// Create AtomMatcher proto for acquiring wakelock.
AtomMatcher CreateAcquireWakelockAtomMatcher();
@@ -59,6 +72,9 @@ Predicate CreateScreenIsOnPredicate();
// Create Predicate proto for screen is off.
Predicate CreateScreenIsOffPredicate();
+// Create Predicate proto for battery saver mode.
+Predicate CreateBatterySaverModePredicate();
+
// Create Predicate proto for holding wakelock.
Predicate CreateHoldingWakelockPredicate();
@@ -86,6 +102,15 @@ FieldMatcher CreateAttributionUidDimensions(const int atomId,
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
const android::view::DisplayStateEnum state, uint64_t timestampNs);
+// Create log event for screen brightness state changed.
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+ int level, uint64_t timestampNs);
+
+// Create log event when battery saver starts.
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
+// Create log event when battery saver stops.
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
+
// Create log event for app moving to background.
std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
@@ -94,11 +119,11 @@ std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t ti
// Create log event when the app sync starts.
std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const int uid, const string& name, uint64_t timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const int uid, const string& name, uint64_t timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateAppCrashEvent(
@@ -136,9 +161,12 @@ void ValidateAttributionUidAndTagDimension(
template <typename T>
void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
- std::map<HashableDimensionKey, int> dimensionIndexMap;
+ std::map<MetricDimensionKey, int> dimensionIndexMap;
for (int i = 0; i < metricData.data_size(); ++i) {
- dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i));
+ dimensionIndexMap.insert(std::make_pair(
+ MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()),
+ HashableDimensionKey(metricData.data(i).dimensions_in_condition())),
+ i));
}
for (const auto& itr : dimensionIndexMap) {
*sortedMetricData->add_data() = metricData.data(itr.second);
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index 32a85b136023..a65095f4188d 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -27,6 +27,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_PRIVILEGED_MODULE := true
LOCAL_DEX_PREOPT := false
+LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml
index 7bfde4050197..cd76c9d379b5 100644
--- a/cmds/statsd/tools/dogfood/AndroidManifest.xml
+++ b/cmds/statsd/tools/dogfood/AndroidManifest.xml
@@ -18,6 +18,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.statsd.dogfood"
+ android:sharedUserId="android.uid.system"
android:versionCode="1"
android:versionName="1.0" >
diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
index c1c391483855..d05006124994 100644
--- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
+++ b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index d39aa1d314e6..57575ae145f6 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -116,28 +116,32 @@ public class MainActivity extends Activity {
findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC);
}
});
findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 0);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE);
}
});
findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 2);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON);
}
});
findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF);
}
});
@@ -255,7 +259,9 @@ public class MainActivity extends Activity {
}
int[] uids = new int[] {mUids[id]};
String[] tags = new String[] {"acquire"};
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 1);");
@@ -269,7 +275,9 @@ public class MainActivity extends Activity {
}
int[] uids = new int[] {mUids[id]};
String[] tags = new String[] {"release"};
- StatsLog.write(10, uids, tags, 0, name, 0);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 0);");
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 34f6d7de0cc9..3893be49e739 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -21,7 +21,6 @@ import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
public class UsbCommand extends Svc.Command {
public UsbCommand() {
@@ -37,41 +36,41 @@ public class UsbCommand extends Svc.Command {
public String longHelp() {
return shortHelp() + "\n"
+ "\n"
- + "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n"
- + " Set the current usb function and optionally the data lock state.\n\n"
+ + "usage: svc usb setFunctions [function]\n"
+ + " Set the current usb function. If function is blank, sets to charging.\n"
+ " svc usb setScreenUnlockedFunctions [function]\n"
- + " Sets the functions which, if the device was charging,"
- + " become current on screen unlock.\n"
- + " svc usb getFunction\n"
- + " Gets the list of currently enabled functions\n";
+ + " Sets the functions which, if the device was charging, become current on"
+ + "screen unlock. If function is blank, turn off this feature.\n"
+ + " svc usb getFunctions\n"
+ + " Gets the list of currently enabled functions\n\n"
+ + "possible values of [function] are any of 'mtp', 'ptp', 'rndis', 'midi'\n";
}
@Override
public void run(String[] args) {
- boolean validCommand = false;
if (args.length >= 2) {
- if ("setFunction".equals(args[1])) {
- IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
- Context.USB_SERVICE));
- boolean unlockData = false;
- if (args.length >= 4) {
- unlockData = Boolean.valueOf(args[3]);
- }
+ IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
+ Context.USB_SERVICE));
+ if ("setFunctions".equals(args[1])) {
try {
- usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), unlockData);
+ usbMgr.setCurrentFunctions(UsbManager.usbFunctionsFromString(
+ args.length >= 3 ? args[2] : ""));
} catch (RemoteException e) {
System.err.println("Error communicating with UsbManager: " + e);
}
return;
- } else if ("getFunction".equals(args[1])) {
- System.err.println(SystemProperties.get("sys.usb.config"));
+ } else if ("getFunctions".equals(args[1])) {
+ try {
+ System.err.println(
+ UsbManager.usbFunctionsToString(usbMgr.getCurrentFunctions()));
+ } catch (RemoteException e) {
+ System.err.println("Error communicating with UsbManager: " + e);
+ }
return;
} else if ("setScreenUnlockedFunctions".equals(args[1])) {
- IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
- Context.USB_SERVICE));
try {
- usbMgr.setScreenUnlockedFunctions((args.length >= 3 ? args[2] :
- UsbManager.USB_FUNCTION_NONE));
+ usbMgr.setScreenUnlockedFunctions(UsbManager.usbFunctionsFromString(
+ args.length >= 3 ? args[2] : ""));
} catch (RemoteException e) {
System.err.println("Error communicating with UsbManager: " + e);
}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
new file mode 100644
index 000000000000..58936fcc4452
--- /dev/null
+++ b/config/hiddenapi-light-greylist.txt
@@ -0,0 +1,1164 @@
+Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
+Landroid/animation/ValueAnimator;->animateValue(F)V
+Landroid/animation/ValueAnimator;->getDurationScale()F
+Landroid/animation/ValueAnimator;->sDurationScale:F
+Landroid/app/Activity;->convertFromTranslucent()V
+Landroid/app/Activity;->convertToTranslucent(Landroid/app/Activity$TranslucentConversionListener;Landroid/app/ActivityOptions;)Z
+Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
+Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder;
+Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo;
+Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z
+Landroid/app/ActivityManager;->forceStopPackage(Ljava/lang/String;)V
+Landroid/app/ActivityManager;->getCurrentUser()I
+Landroid/app/ActivityManager;->isLowRamDeviceStatic()Z
+Landroid/app/ActivityManager;->isUserRunning(I)Z
+Landroid/app/ActivityManager;->mContext:Landroid/content/Context;
+Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager;
+Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I
+Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J
+Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I
+Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I
+Landroid/app/Activity;->mApplication:Landroid/app/Application;
+Landroid/app/Activity;->mHandler:Landroid/os/Handler;
+Landroid/app/Activity;->mResultCode:I
+Landroid/app/Activity;->mResultData:Landroid/content/Intent;
+Landroid/app/Activity;->mToken:Landroid/os/IBinder;
+Landroid/app/Activity;->mWindow:Landroid/view/Window;
+Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
+Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
+Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
+Landroid/app/ActivityThread$ActivityClientRecord;->token:Landroid/os/IBinder;
+Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk;
+Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle;
+Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread;
+Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application;
+Landroid/app/ActivityThread;->currentPackageName()Ljava/lang/String;
+Landroid/app/ActivityThread;->currentProcessName()Ljava/lang/String;
+Landroid/app/ActivityThread;->getApplication()Landroid/app/Application;
+Landroid/app/ActivityThread;->getApplicationThread()Landroid/app/ActivityThread$ApplicationThread;
+Landroid/app/ActivityThread;->getHandler()Landroid/os/Handler;
+Landroid/app/ActivityThread;->getPackageManager()Landroid/content/pm/IPackageManager;
+Landroid/app/ActivityThread;->getProcessName()Ljava/lang/String;
+Landroid/app/ActivityThread$H;->BIND_SERVICE:I
+Landroid/app/ActivityThread$H;->CREATE_SERVICE:I
+Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I
+Landroid/app/ActivityThread$H;->RECEIVER:I
+Landroid/app/ActivityThread$H;->RELAUNCH_ACTIVITY:I
+Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I
+Landroid/app/ActivityThread$H;->SERVICE_ARGS:I
+Landroid/app/ActivityThread$H;->STOP_SERVICE:I
+Landroid/app/ActivityThread$H;->UNBIND_SERVICE:I
+Landroid/app/ActivityThread;->installContentProviders(Landroid/content/Context;Ljava/util/List;)V
+Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList;
+Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData;
+Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application;
+Landroid/app/ActivityThread;->mInstrumentation:Landroid/app/Instrumentation;
+Landroid/app/ActivityThread;->mNumVisibleActivities:I
+Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
+Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager;
+Landroid/app/admin/DevicePolicyManager;->getTrustAgentConfiguration(Landroid/content/ComponentName;Landroid/content/ComponentName;I)Ljava/util/List;
+Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(J)V
+Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(JZ)V
+Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;I)Z
+Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;)Z
+Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;ZI)V
+Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;Z)V
+Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams;
+Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController;
+Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application;
+Landroid/app/AppGlobals;->getPackageManager()Landroid/content/pm/IPackageManager;
+Landroid/app/Application;->attach(Landroid/content/Context;)V
+Landroid/app/ApplicationLoaders;->getDefault()Landroid/app/ApplicationLoaders;
+Landroid/app/ApplicationLoaders;->mLoaders:Landroid/util/ArrayMap;
+Landroid/app/Application;->mComponentCallbacks:Ljava/util/ArrayList;
+Landroid/app/Application;->mLoadedApk:Landroid/app/LoadedApk;
+Landroid/app/ApplicationPackageManager;->deletePackage(Ljava/lang/String;Landroid/content/pm/IPackageDeleteObserver;I)V
+Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;)I
+Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;I)I
+Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->noteOp(I)I
+Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I
+Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I
+Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
+Landroid/app/AppOpsManager;->setMode(IILjava/lang/String;I)V
+Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
+Landroid/app/backup/BackupDataInputStream;->dataSize:I
+Landroid/app/backup/BackupDataInputStream;->key:Ljava/lang/String;
+Landroid/app/backup/FullBackup;->backupToTar(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I
+Landroid/app/ContextImpl;->createActivityContext(Landroid/app/ActivityThread;Landroid/app/LoadedApk;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;ILandroid/content/res/Configuration;)Landroid/app/ContextImpl;
+Landroid/app/ContextImpl;->getActivityToken()Landroid/os/IBinder;
+Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread;
+Landroid/app/ContextImpl;->mPackageInfo:Landroid/app/LoadedApk;
+Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V
+Landroid/app/DatePickerDialog;->mDatePicker:Landroid/widget/DatePicker;
+Landroid/app/Dialog;->CANCEL:I
+Landroid/app/Dialog;->mCancelMessage:Landroid/os/Message;
+Landroid/app/Dialog;->mDismissMessage:Landroid/os/Message;
+Landroid/app/Dialog;->mListenersHandler:Landroid/os/Handler;
+Landroid/app/Dialog;->mOwnerActivity:Landroid/app/Activity;
+Landroid/app/Dialog;->mShowMessage:Landroid/os/Message;
+Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
+Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
+Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
+Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
+Landroid/app/IActivityManager;->resumeAppSwitches()V
+Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
+Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler;
+Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
+Landroid/app/LoadedApk;->mApplication:Landroid/app/Application;
+Landroid/app/LoadedApk;->mReceivers:Landroid/util/ArrayMap;
+Landroid/app/LoadedApk;->mResDir:Ljava/lang/String;
+Landroid/app/LoadedApk;->mResources:Landroid/content/res/Resources;
+Landroid/app/LocalActivityManager;->mActivities:Ljava/util/Map;
+Landroid/app/LocalActivityManager;->mActivityArray:Ljava/util/ArrayList;
+Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList;
+Landroid/app/Notification;->EXTRA_SUBSTITUTE_APP_NAME:Ljava/lang/String;
+Landroid/app/Notification;->isGroupSummary()Z
+Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager;
+Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V
+Landroid/app/Notification;->setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V
+Landroid/app/PendingIntent;->getActivityAsUser(Landroid/content/Context;ILandroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/PendingIntent;
+Landroid/app/PendingIntent;->getIntent()Landroid/content/Intent;
+Landroid/app/PendingIntent;->isActivity()Z
+Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context;
+Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView;
+Landroid/app/Service;->setForeground(Z)V
+Landroid/app/StatusBarManager;->collapsePanels()V
+Landroid/app/StatusBarManager;->disable(I)V
+Landroid/app/StatusBarManager;->expandNotificationsPanel()V
+Landroid/app/StatusBarManager;->expandSettingsPanel(Ljava/lang/String;)V
+Landroid/app/StatusBarManager;->expandSettingsPanel()V
+Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
+Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap;
+Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap;
+Landroid/app/WallpaperManager;->getIWallpaperManager()Landroid/app/IWallpaperManager;
+Landroid/app/WallpaperManager;->openDefaultWallpaper(Landroid/content/Context;I)Ljava/io/InputStream;
+Landroid/app/WallpaperManager;->setBitmap(Landroid/graphics/Bitmap;Landroid/graphics/Rect;ZII)I
+Landroid/app/WallpaperManager;->sGlobals:Landroid/app/WallpaperManager$Globals;
+Landroid/appwidget/AppWidgetManager;->bindAppWidgetIdIfAllowed(IILandroid/content/ComponentName;Landroid/os/Bundle;)Z
+Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;Landroid/os/Bundle;)V
+Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;)V
+Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z
+Landroid/bluetooth/BluetoothAdapter;->enableNoAutoConnect()Z
+Landroid/bluetooth/BluetoothAdapter;->getDiscoverableTimeout()I
+Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z
+Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z
+Landroid/bluetooth/BluetoothDevice;->cancelBondProcess()Z
+Landroid/bluetooth/BluetoothDevice;->createBond(I)Z
+Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String;
+Landroid/bluetooth/BluetoothDevice;->isConnected()Z
+Landroid/bluetooth/BluetoothDevice;->isEncrypted()Z
+Landroid/bluetooth/BluetoothDevice;->removeBond()Z
+Landroid/bluetooth/BluetoothDevice;->setPhonebookAccessPermission(I)Z
+Landroid/bluetooth/BluetoothGattCharacteristic;->mInstance:I
+Landroid/bluetooth/BluetoothGattCharacteristic;->mService:Landroid/bluetooth/BluetoothGattService;
+Landroid/bluetooth/BluetoothGattDescriptor;->mCharacteristic:Landroid/bluetooth/BluetoothGattCharacteristic;
+Landroid/bluetooth/BluetoothGattDescriptor;->mInstance:I
+Landroid/bluetooth/BluetoothGatt;->refresh()Z
+Landroid/bluetooth/BluetoothHeadset;->close()V
+Landroid/bluetooth/BluetoothHeadset;->setPriority(Landroid/bluetooth/BluetoothDevice;I)Z
+Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid;
+Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
+Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor;
+Landroid/content/ContentProviderOperation;->mSelection:Ljava/lang/String;
+Landroid/content/ContentProviderOperation;->mType:I
+Landroid/content/ContentResolver;->getContentService()Landroid/content/IContentService;
+Landroid/content/ContentResolver;->getSyncStatus(Landroid/accounts/Account;Ljava/lang/String;)Landroid/content/SyncStatusInfo;
+Landroid/content/ContentResolver;->mContext:Landroid/content/Context;
+Landroid/content/ContentValues;->mValues:Ljava/util/HashMap;
+Landroid/content/Context;->createPackageContextAsUser(Ljava/lang/String;ILandroid/os/UserHandle;)Landroid/content/Context;
+Landroid/content/Context;->getSharedPrefsFile(Ljava/lang/String;)Ljava/io/File;
+Landroid/content/Context;->getThemeResId()I
+Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;I)V
+Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;Landroid/os/Bundle;)V
+Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V
+Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/os/Bundle;Landroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V
+Landroid/content/ContextWrapper;->mBase:Landroid/content/Context;
+Landroid/content/CursorLoader;->mCancellationSignal:Landroid/os/CancellationSignal;
+Landroid/content/CursorLoader;->mObserver:Landroid/content/Loader$ForceLoadContentObserver;
+Landroid/content/IContentService;->cancelSync(Landroid/accounts/Account;Ljava/lang/String;Landroid/content/ComponentName;)V
+Landroid/content/IContentService;->getMasterSyncAutomatically()Z
+Landroid/content/IContentService;->setMasterSyncAutomatically(Z)V
+Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String;
+Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent;
+Landroid/content/pm/ApplicationInfo;->installLocation:I
+Landroid/content/pm/ApplicationInfo;->isForwardLocked()Z
+Landroid/content/pm/ApplicationInfo;->isInstantApp()Z
+Landroid/content/pm/ApplicationInfo;->isPrivilegedApp()Z
+Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String;
+Landroid/content/pm/ApplicationInfo;->privateFlags:I
+Landroid/content/pm/ApplicationInfo;->targetSandboxVersion:I
+Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
+Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
+Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager;
+Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V
+Landroid/content/pm/PackageInstaller$SessionParams;->setGrantedRuntimePermissions([Ljava/lang/String;)V
+Landroid/content/pm/PackageInstaller$SessionParams;->setInstallAsInstantApp(Z)V
+Landroid/content/pm/PackageManager;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/PackageManager;->freeStorageAndNotify(Ljava/lang/String;JLandroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/PackageManager;->freeStorage(JLandroid/content/IntentSender;)V
+Landroid/content/pm/PackageManager;->freeStorage(Ljava/lang/String;JLandroid/content/IntentSender;)V
+Landroid/content/pm/PackageManager;->getApplicationInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
+Landroid/content/pm/PackageManager;->getPackageCandidateVolumes(Landroid/content/pm/ApplicationInfo;)Ljava/util/List;
+Landroid/content/pm/PackageManager;->getPackageInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageManager;->getPackageSizeInfo(Ljava/lang/String;Landroid/content/pm/IPackageStatsObserver;)V
+Landroid/content/pm/PackageManager;->getResourcesForApplicationAsUser(Ljava/lang/String;I)Landroid/content/res/Resources;
+Landroid/content/pm/PackageManager;->movePackage(Ljava/lang/String;Landroid/os/storage/VolumeInfo;)I
+Landroid/content/pm/PackageManager;->queryBroadcastReceivers(Landroid/content/Intent;II)Ljava/util/List;
+Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
+Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
+Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->parseMonolithicPackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/UserInfo;->id:I
+Landroid/content/pm/UserInfo;->isPrimary()Z
+Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I
+Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I
+Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V
+Landroid/content/res/AssetManager;->getArraySize(I)I
+Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray;
+Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String;
+Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence;
+Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->mObject:J
+Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;
+Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J
+Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J
+Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z
+Landroid/content/res/AssetManager;->retrieveArray(I[I)I
+Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z
+Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I
+Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I
+Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
+Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
+Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
+Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager;
+Landroid/content/res/ResourcesImpl;->mColorDrawableCache:Landroid/content/res/DrawableCache;
+Landroid/content/res/ResourcesImpl;->mConfiguration:Landroid/content/res/Configuration;
+Landroid/content/res/ResourcesImpl;->mDrawableCache:Landroid/content/res/DrawableCache;
+Landroid/content/res/ResourcesImpl;->mPreloading:Z
+Landroid/content/res/ResourcesImpl;->sPreloadedColorDrawables:Landroid/util/LongSparseArray;
+Landroid/content/res/ResourcesImpl;->sPreloadedDrawables:[Landroid/util/LongSparseArray;
+Landroid/content/res/ResourcesImpl;->TRACE_FOR_MISS_PRELOAD:Z
+Landroid/content/res/ResourcesImpl;->TRACE_FOR_PRELOAD:Z
+Landroid/content/res/Resources;->loadXmlResourceParser(ILjava/lang/String;)Landroid/content/res/XmlResourceParser;
+Landroid/content/res/Resources;->loadXmlResourceParser(Ljava/lang/String;IILjava/lang/String;)Landroid/content/res/XmlResourceParser;
+Landroid/content/res/Resources;->mResourcesImpl:Landroid/content/res/ResourcesImpl;
+Landroid/content/res/Resources;->mTmpValue:Landroid/util/TypedValue;
+Landroid/content/res/Resources;->mTypedArrayPool:Landroid/util/Pools$SynchronizedPool;
+Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V
+Landroid/content/res/TypedArray;->getValueAt(ILandroid/util/TypedValue;)Z
+Landroid/content/res/TypedArray;->mAssets:Landroid/content/res/AssetManager;
+Landroid/content/res/TypedArray;->mData:[I
+Landroid/content/res/TypedArray;->mIndices:[I
+Landroid/content/res/TypedArray;->mLength:I
+Landroid/content/res/TypedArray;->mMetrics:Landroid/util/DisplayMetrics;
+Landroid/content/res/TypedArray;->mRecycled:Z
+Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources;
+Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme;
+Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue;
+Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser;
+Landroid/content/res/XmlBlock;->close()V
+Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser;
+Landroid/content/res/XmlBlock$Parser;->mParseState:J
+Landroid/content/SyncStatusInfo;->lastSuccessTime:J
+Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
+Landroid/database/CursorWindow;->mWindowPtr:J
+Landroid/database/CursorWindow;->sCursorWindowSize:I
+Landroid/database/CursorWindow;->sWindowToPidMap:Landroid/util/LongSparseArray;
+Landroid/database/CursorWrapper;->mCursor:Landroid/database/Cursor;
+Landroid/graphics/Bitmap$Config;->nativeInt:I
+Landroid/graphics/Bitmap;->createAshmemBitmap()Landroid/graphics/Bitmap;
+Landroid/graphics/Bitmap;->createAshmemBitmap(Landroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
+Landroid/graphics/Bitmap;->getDefaultDensity()I
+Landroid/graphics/drawable/AnimationDrawable;->mCurFrame:I
+Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorStateList;
+Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
+Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
+Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
+Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets;
+Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;
+Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V
+Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference;
+Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
+Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
+Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
+Landroid/graphics/drawable/StateListDrawable;->getStateCount()I
+Landroid/graphics/drawable/StateListDrawable;->getStateDrawable(I)Landroid/graphics/drawable/Drawable;
+Landroid/graphics/drawable/StateListDrawable;->getStateSet(I)[I
+Landroid/graphics/drawable/StateListDrawable;->mStateListState:Landroid/graphics/drawable/StateListDrawable$StateListState;
+Landroid/graphics/drawable/StateListDrawable;->updateStateFromTypedArray(Landroid/content/res/TypedArray;)V
+Landroid/graphics/LinearGradient;->mColors:[I
+Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap;
+Landroid/graphics/SurfaceTexture;->nativeDetachFromGLContext()I
+Landroid/graphics/Typeface;->mStyle:I
+Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface;
+Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
+Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
+Landroid/hardware/Camera;->addCallbackBuffer([BI)V
+Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera;
+Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
+Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
+Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
+Landroid/hardware/input/InputManager;->getInstance()Landroid/hardware/input/InputManager;
+Landroid/hardware/input/InputManager;->mIm:Landroid/hardware/input/IInputManager;
+Landroid/hardware/usb/UsbManager;->setCurrentFunction(Ljava/lang/String;Z)V
+Landroid/location/LocationRequest;->createFromDeprecatedProvider(Ljava/lang/String;JFZ)Landroid/location/LocationRequest;
+Landroid/location/LocationRequest;->setWorkSource(Landroid/os/WorkSource;)V
+Landroid/location/Location;->setIsFromMockProvider(Z)V
+Landroid/media/AudioAttributes$Builder;->setInternalCapturePreset(I)Landroid/media/AudioAttributes$Builder;
+Landroid/media/AudioManager;->abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;)I
+Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap;
+Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioFocusRequest;Landroid/media/audiopolicy/AudioPolicy;)I
+Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;II)I
+Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;IILandroid/media/audiopolicy/AudioPolicy;)I
+Landroid/media/AudioManager;->setMasterMute(ZI)V
+Landroid/media/AudioManager;->STREAM_BLUETOOTH_SCO:I
+Landroid/media/AudioManager;->STREAM_SYSTEM_ENFORCED:I
+Landroid/media/AudioManager;->STREAM_TTS:I
+Landroid/media/AudioSystem;->setDeviceConnectionState(IILjava/lang/String;Ljava/lang/String;)I
+Landroid/media/AudioTrack;->getLatency()I
+Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
+Landroid/media/MediaCodec;->releaseOutputBuffer(IZZJ)V
+Landroid/media/MediaFile;->getFileType(Ljava/lang/String;)Landroid/media/MediaFile$MediaFileType;
+Landroid/media/MediaFile;->getMimeTypeForFile(Ljava/lang/String;)Ljava/lang/String;
+Landroid/media/MediaFile;->isVideoFileType(I)Z
+Landroid/media/MediaFile$MediaFileType;->fileType:I
+Landroid/media/MediaFile;->sFileTypeMap:Ljava/util/HashMap;
+Landroid/media/MediaMetadataRetriever;->getEmbeddedPicture(I)[B
+Landroid/media/MediaPlayer;->getMetadata(ZZ)Landroid/media/Metadata;
+Landroid/media/MediaPlayer;->invoke(Landroid/os/Parcel;Landroid/os/Parcel;)V
+Landroid/media/MediaPlayer;->newRequest()Landroid/os/Parcel;
+Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/util/List;)V
+Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)V
+Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;)V
+Landroid/media/MediaPlayer;->setRetransmitEndpoint(Ljava/net/InetSocketAddress;)V
+Landroid/media/MediaRecorder$AudioSource;->RADIO_TUNER:I
+Landroid/media/MediaRouter$RouteInfo;->getStatusCode()I
+Landroid/media/MediaRouter$RouteInfo;->STATUS_CONNECTING:I
+Landroid/media/MediaRouter;->selectRouteInt(ILandroid/media/MediaRouter$RouteInfo;Z)V
+Landroid/media/MediaScanner;->mClient:Landroid/media/MediaScanner$MyMediaScannerClient;
+Landroid/media/MediaScanner;->scanSingleFile(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;
+Landroid/media/MiniThumbFile;->reset()V
+Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
+Landroid/media/Ringtone;->setLooping(Z)V
+Landroid/media/Ringtone;->setVolume(F)V
+Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
+Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
+Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
+Landroid/net/ConnectivityManager;->getLinkProperties(I)Landroid/net/LinkProperties;
+Landroid/net/ConnectivityManager;->getMobileDataEnabled()Z
+Landroid/net/ConnectivityManager;->getTetherableUsbRegexs()[Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getTetheredIfaces()[Ljava/lang/String;
+Landroid/net/ConnectivityManager;->isNetworkSupported(I)Z
+Landroid/net/ConnectivityManager;->isNetworkTypeMobile(I)Z
+Landroid/net/ConnectivityManager;->isTetheringSupported()Z
+Landroid/net/ConnectivityManager;->mService:Landroid/net/IConnectivityManager;
+Landroid/net/ConnectivityManager;->requestRouteToHostAddress(ILjava/net/InetAddress;)Z
+Landroid/net/ConnectivityManager;->requestRouteToHost(II)Z
+Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager;
+Landroid/net/NetworkStats;->capacity:I
+Landroid/net/NetworkStats;->defaultNetwork:[I
+Landroid/net/NetworkStats;->iface:[Ljava/lang/String;
+Landroid/net/NetworkStats;->metered:[I
+Landroid/net/NetworkStats;->operations:[J
+Landroid/net/NetworkStats;->roaming:[I
+Landroid/net/NetworkStats;->rxBytes:[J
+Landroid/net/NetworkStats;->rxPackets:[J
+Landroid/net/NetworkStats;->set:[I
+Landroid/net/NetworkStats;->size:I
+Landroid/net/NetworkStats;->tag:[I
+Landroid/net/NetworkStats;->txBytes:[J
+Landroid/net/NetworkStats;->txPackets:[J
+Landroid/net/NetworkStats;->uid:[I
+Landroid/net/SSLCertificateSocketFactory;->getHttpSocketFactory(ILandroid/net/SSLSessionCache;)Lorg/apache/http/conn/ssl/SSLSocketFactory;
+Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService;
+Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I
+Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;
+Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
+Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/WifiConfiguration;->apBand:I
+Landroid/net/wifi/WifiConfiguration;->apChannel:I
+Landroid/net/wifi/WifiConfiguration;->hasNoInternetAccess()Z
+Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration;
+Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z
+Landroid/net/wifi/WifiManager;->connect(ILandroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->connect(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String;
+Landroid/net/wifi/WifiManager;->forget(ILandroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;
+Landroid/net/wifi/WifiManager;->getWifiApState()I
+Landroid/net/wifi/WifiManager;->isDualBandSupported()Z
+Landroid/net/wifi/WifiManager;->isWifiApEnabled()Z
+Landroid/net/wifi/WifiManager;->mService:Landroid/net/wifi/IWifiManager;
+Landroid/net/wifi/WifiManager;->save(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I
+Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I
+Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
+Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
+Landroid/opengl/GLSurfaceView$EglHelper;->mEglContext:Ljavax/microedition/khronos/egl/EGLContext;
+Landroid/opengl/GLSurfaceView$GLThread;->mEglHelper:Landroid/opengl/GLSurfaceView$EglHelper;
+Landroid/opengl/GLSurfaceView;->mGLThread:Landroid/opengl/GLSurfaceView$GLThread;
+Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
+Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
+Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
+Landroid/os/AsyncTask;->mWorker:Landroid/os/AsyncTask$WorkerRunnable;
+Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor;
+Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V
+Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I
+Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
+Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
+Landroid/os/BatteryStats$Uid;->getProcessStats()Landroid/util/ArrayMap;
+Landroid/os/BatteryStats$Uid;->getSensorStats()Landroid/util/SparseArray;
+Landroid/os/BatteryStats$Uid;->getUid()I
+Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
+Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
+Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J
+Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J
+Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
+Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I
+Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
+Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V
+Landroid/os/Debug;->countInstancesOfClass(Ljava/lang/Class;)J
+Landroid/os/Debug;->dumpReferenceTables()V
+Landroid/os/Debug$MemoryInfo;->getOtherLabel(I)Ljava/lang/String;
+Landroid/os/Debug$MemoryInfo;->getOtherPrivateDirty(I)I
+Landroid/os/Debug$MemoryInfo;->getOtherPss(I)I
+Landroid/os/Debug$MemoryInfo;->getOtherSharedDirty(I)I
+Landroid/os/Debug$MemoryInfo;->NUM_DVK_STATS:I
+Landroid/os/Debug$MemoryInfo;->NUM_OTHER_STATS:I
+Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File;
+Landroid/os/FileUtils;->checksumCrc32(Ljava/io/File;)J
+Landroid/os/FileUtils;->copyFile(Ljava/io/File;Ljava/io/File;)Z
+Landroid/os/FileUtils;->copyToFile(Ljava/io/InputStream;Ljava/io/File;)Z
+Landroid/os/FileUtils;->deleteOlderFiles(Ljava/io/File;IJ)Z
+Landroid/os/FileUtils;->readTextFile(Ljava/io/File;ILjava/lang/String;)Ljava/lang/String;
+Landroid/os/FileUtils;->setPermissions(Ljava/io/FileDescriptor;III)I
+Landroid/os/FileUtils;->setPermissions(Ljava/io/File;III)I
+Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I
+Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
+Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
+Landroid/os/Handler;->hasCallbacks(Ljava/lang/Runnable;)Z
+Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
+Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
+Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
+Landroid/os/IPowerManager;->userActivity(JII)V
+Landroid/os/Looper;->mQueue:Landroid/os/MessageQueue;
+Landroid/os/MemoryFile;->getFileDescriptor()Ljava/io/FileDescriptor;
+Landroid/os/Message;->callback:Ljava/lang/Runnable;
+Landroid/os/Message;->flags:I
+Landroid/os/Message;->next:Landroid/os/Message;
+Landroid/os/MessageQueue;->mIdleHandlers:Ljava/util/ArrayList;
+Landroid/os/MessageQueue;->mMessages:Landroid/os/Message;
+Landroid/os/MessageQueue;->mQuitAllowed:Z
+Landroid/os/MessageQueue;->next()Landroid/os/Message;
+Landroid/os/Message;->target:Landroid/os/Handler;
+Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I
+Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I
+Landroid/os/PowerManager;->isLightDeviceIdleMode()Z
+Landroid/os/PowerManager;->userActivity(JII)V
+Landroid/os/PowerManager;->userActivity(JZ)V
+Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V
+Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V
+Landroid/os/PowerManager;->wakeUp(J)V
+Landroid/os/Process;->getParentPid(I)I
+Landroid/os/Process;->getPids(Ljava/lang/String;[I)[I
+Landroid/os/Process;->getUidForPid(I)I
+Landroid/os/Process;->isIsolated(I)Z
+Landroid/os/Process;->isIsolated()Z
+Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
+Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
+Landroid/os/RecoverySystem;->cancelScheduledUpdate(Landroid/content/Context;)V
+Landroid/os/RecoverySystem;->installPackage(Landroid/content/Context;Ljava/io/File;Z)V
+Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;Landroid/os/Handler;)V
+Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;)V
+Landroid/os/RecoverySystem;->rebootWipeAb(Landroid/content/Context;Ljava/io/File;Ljava/lang/String;)V
+Landroid/os/RecoverySystem;->scheduleUpdateOnBoot(Landroid/content/Context;Ljava/io/File;)V
+Landroid/os/SELinux;->isSELinuxEnabled()Z
+Landroid/os/SELinux;->isSELinuxEnforced()Z
+Landroid/os/ServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/ServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/storage/DiskInfo;->getDescription()Ljava/lang/String;
+Landroid/os/storage/StorageManager;->findVolumeByUuid(Ljava/lang/String;)Landroid/os/storage/VolumeInfo;
+Landroid/os/storage/StorageManager;->getBestVolumeDescription(Landroid/os/storage/VolumeInfo;)Ljava/lang/String;
+Landroid/os/storage/StorageManager;->getDisks()Ljava/util/List;
+Landroid/os/storage/StorageManager;->getStorageBytesUntilLow(Ljava/io/File;)J
+Landroid/os/storage/StorageManager;->getVolumeList(II)[Landroid/os/storage/StorageVolume;
+Landroid/os/storage/StorageManager;->getVolumeList()[Landroid/os/storage/StorageVolume;
+Landroid/os/storage/StorageManager;->getVolumePaths()[Ljava/lang/String;
+Landroid/os/storage/StorageManager;->getVolumes()Ljava/util/List;
+Landroid/os/storage/StorageManager;->getVolumeState(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/storage/StorageVolume;->getPathFile()Ljava/io/File;
+Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String;
+Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getDisk()Landroid/os/storage/DiskInfo;
+Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
+Landroid/os/storage/VolumeInfo;->getState()I
+Landroid/os/storage/VolumeInfo;->getType()I
+Landroid/os/storage/VolumeInfo;->isPrimary()Z
+Landroid/os/storage/VolumeInfo;->isVisible()Z
+Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal;
+Landroid/os/SystemProperties;->addChangeCallback(Ljava/lang/Runnable;)V
+Landroid/os/SystemProperties;->getBoolean(Ljava/lang/String;Z)Z
+Landroid/os/SystemProperties;->getInt(Ljava/lang/String;I)I
+Landroid/os/SystemProperties;->get(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/SystemProperties;->getLong(Ljava/lang/String;J)J
+Landroid/os/SystemProperties;->PROP_NAME_MAX:I
+Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
+Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
+Landroid/os/Trace;->isTagEnabled(J)Z
+Landroid/os/Trace;->setAppTracingAllowed(Z)V
+Landroid/os/Trace;->traceBegin(JLjava/lang/String;)V
+Landroid/os/Trace;->traceCounter(JLjava/lang/String;I)V
+Landroid/os/Trace;->traceEnd(J)V
+Landroid/os/Trace;->TRACE_TAG_APP:J
+Landroid/os/Trace;->TRACE_TAG_VIEW:J
+Landroid/os/UpdateEngine;->applyPayload(Ljava/lang/String;JJ[Ljava/lang/String;)V
+Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;Landroid/os/Handler;)Z
+Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;)Z
+Landroid/os/UpdateEngine;->cancel()V
+Landroid/os/UpdateEngine;->resetStatus()V
+Landroid/os/UpdateLock;->acquire()V
+Landroid/os/UpdateLock;->isHeld()Z
+Landroid/os/UpdateLock;->release()V
+Landroid/os/UserHandle;->ALL:Landroid/os/UserHandle;
+Landroid/os/UserHandle;->getIdentifier()I
+Landroid/os/UserHandle;->getUserId(I)I
+Landroid/os/UserHandle;->isOwner()Z
+Landroid/os/UserHandle;->myUserId()I
+Landroid/os/UserHandle;->of(I)Landroid/os/UserHandle;
+Landroid/os/UserManager;->get(Landroid/content/Context;)Landroid/os/UserManager;
+Landroid/os/UserManager;->getMaxSupportedUsers()I
+Landroid/os/UserManager;->getProfiles(I)Ljava/util/List;
+Landroid/os/UserManager;->getUserHandle()I
+Landroid/os/UserManager;->getUserHandle(I)I
+Landroid/os/UserManager;->getUserInfo(I)Landroid/content/pm/UserInfo;
+Landroid/os/UserManager;->getUserSerialNumber(I)I
+Landroid/os/UserManager;->getUsers()Ljava/util/List;
+Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isLinkedUser()Z
+Landroid/os/UserManager;->isUserUnlocked(I)Z
+Landroid/os/WorkSource;->add(ILjava/lang/String;)Z
+Landroid/os/WorkSource;->add(I)Z
+Landroid/os/WorkSource;->get(I)I
+Landroid/os/WorkSource;->getName(I)Ljava/lang/String;
+Landroid/os/WorkSource;->size()I
+Landroid/preference/DialogPreference;->mBuilder:Landroid/app/AlertDialog$Builder;
+Landroid/preference/DialogPreference;->mDialogIcon:Landroid/graphics/drawable/Drawable;
+Landroid/preference/DialogPreference;->mDialog:Landroid/app/Dialog;
+Landroid/preference/DialogPreference;->mDialogMessage:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mDialogTitle:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mNegativeButtonText:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mPositiveButtonText:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mWhichButtonClicked:I
+Landroid/preference/ListPreference;->mClickedDialogEntryIndex:I
+Landroid/preference/PreferenceActivity;->mPreferenceManager:Landroid/preference/PreferenceManager;
+Landroid/preference/PreferenceActivity;->mPrefsContainer:Landroid/view/ViewGroup;
+Landroid/preference/PreferenceManager;->dispatchActivityDestroy()V
+Landroid/preference/PreferenceManager;->dispatchActivityResult(IILandroid/content/Intent;)V
+Landroid/preference/PreferenceManager;->dispatchActivityStop()V
+Landroid/preference/PreferenceManager;->getEditor()Landroid/content/SharedPreferences$Editor;
+Landroid/preference/PreferenceManager;->getPreferenceScreen()Landroid/preference/PreferenceScreen;
+Landroid/preference/PreferenceManager;->inflateFromIntent(Landroid/content/Intent;Landroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen;
+Landroid/preference/PreferenceManager;->inflateFromResource(Landroid/content/Context;ILandroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen;
+Landroid/preference/PreferenceManager;->mActivityDestroyListeners:Ljava/util/List;
+Landroid/preference/PreferenceManager;->mOnPreferenceTreeClickListener:Landroid/preference/PreferenceManager$OnPreferenceTreeClickListener;
+Landroid/preference/PreferenceManager;->mSharedPreferences:Landroid/content/SharedPreferences;
+Landroid/preference/PreferenceManager;->registerOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V
+Landroid/preference/PreferenceManager;->registerOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V
+Landroid/preference/PreferenceManager;->setFragment(Landroid/preference/PreferenceFragment;)V
+Landroid/preference/PreferenceManager;->setPreferences(Landroid/preference/PreferenceScreen;)Z
+Landroid/preference/PreferenceManager;->shouldCommit()Z
+Landroid/preference/PreferenceManager;->unregisterOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V
+Landroid/preference/PreferenceManager;->unregisterOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V
+Landroid/preference/Preference;->onKey(Landroid/view/View;ILandroid/view/KeyEvent;)Z
+Landroid/preference/Preference;->performClick(Landroid/preference/PreferenceScreen;)V
+Landroid/preference/PreferenceScreen;->mRootAdapter:Landroid/widget/ListAdapter;
+Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
+Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
+Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
+Landroid/provider/Browser;->canClearHistory(Landroid/content/ContentResolver;)Z
+Landroid/provider/Browser;->clearHistory(Landroid/content/ContentResolver;)V
+Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V
+Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V
+Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String;
+Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/provider/Settings$Global;->OTA_DISABLE_AUTOMATIC_UPDATE:Ljava/lang/String;
+Landroid/provider/Settings$Global;->PACKAGE_VERIFIER_ENABLE:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->PACKAGE_VERIFIER_USER_CONSENT:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->USER_SETUP_COMPLETE:Ljava/lang/String;
+Landroid/provider/Settings$System;->AIRPLANE_MODE_TOGGLEABLE_RADIOS:Ljava/lang/String;
+Landroid/provider/Settings$System;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
+Landroid/provider/Settings$System;->putStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;I)Z
+Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Inbox;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Inbox;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Sent;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Sent;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+Landroid/renderscript/RenderScript;->create(Landroid/content/Context;I)Landroid/renderscript/RenderScript;
+Landroid/renderscript/RenderScript;->create(Landroid/content/Context;ILandroid/renderscript/RenderScript$ContextType;I)Landroid/renderscript/RenderScript;
+Landroid/renderscript/RenderScript;->getMinorID()J
+Landroid/R$styleable;->TextAppearance:[I
+Landroid/R$styleable;->TextAppearance_textColor:I
+Landroid/R$styleable;->TextAppearance_textSize:I
+Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnectFailed()V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnect(Ljava/lang/String;Landroid/media/session/MediaSession$Token;Landroid/os/Bundle;)V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildren(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;)V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildrenWithOptions(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;Landroid/os/Bundle;)V
+Landroid/service/media/MediaBrowserService;->KEY_MEDIA_ITEM:Ljava/lang/String;
+Landroid/service/notification/NotificationListenerService;->registerAsSystemService(Landroid/content/Context;Landroid/content/ComponentName;I)V
+Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V
+Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
+Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
+Landroid/telephony/PhoneStateListener;->mSubId:Ljava/lang/Integer;
+Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState;
+Landroid/telephony/SignalStrength;->getCdmaLevel()I
+Landroid/telephony/SignalStrength;->getLteDbm()I
+Landroid/telephony/SignalStrength;->getLteRsrp()I
+Landroid/telephony/SignalStrength;->getLteRssnr()I
+Landroid/telephony/SignalStrength;->getLteSignalStrength()I
+Landroid/telephony/SmsManager;->RESULT_ERROR_FDN_CHECK_FAILURE:I
+Landroid/telephony/SmsMessage;->getSubId()I
+Landroid/telephony/SmsMessage;->mWrappedSmsMessage:Lcom/android/internal/telephony/SmsMessageBase;
+Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I
+Landroid/telephony/SubscriptionManager;->getPhoneId(I)I
+Landroid/telephony/SubscriptionManager;->getSubId(I)[I
+Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V
+Landroid/telephony/TelephonyManager;->checkCarrierPrivilegesForPackage(Ljava/lang/String;)I
+Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager;
+Landroid/telephony/TelephonyManager;->getCurrentPhoneType()I
+Landroid/telephony/TelephonyManager;->getCurrentPhoneType(I)I
+Landroid/telephony/TelephonyManager;->getDataEnabled(I)Z
+Landroid/telephony/TelephonyManager;->getDataEnabled()Z
+Landroid/telephony/TelephonyManager;->getDefault()Landroid/telephony/TelephonyManager;
+Landroid/telephony/TelephonyManager;->getITelephony()Lcom/android/internal/telephony/ITelephony;
+Landroid/telephony/TelephonyManager;->getLine1Number(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkClass(I)I
+Landroid/telephony/TelephonyManager;->getNetworkCountryIso(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkOperator(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkOperatorName(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkType(I)I
+Landroid/telephony/TelephonyManager;->getSimOperator(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSimOperatorName(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSimSerialNumber(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSubscriberId(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
+Landroid/telephony/TelephonyManager;->setDataEnabled(IZ)V
+Landroid/text/AndroidBidi;->bidi(I[C[B)I
+Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
+Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
+Landroid/text/Layout;->DIRS_ALL_LEFT_TO_RIGHT:Landroid/text/Layout$Directions;
+Landroid/text/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod;
+Landroid/text/SpannableStringBuilder;->mGapLength:I
+Landroid/text/SpannableStringBuilder;->mGapStart:I
+Landroid/text/SpannableStringBuilder;->mSpanCount:I
+Landroid/text/SpannableStringBuilder;->mSpanEnds:[I
+Landroid/text/SpannableStringBuilder;->mSpanFlags:[I
+Landroid/text/SpannableStringBuilder;->mSpans:[Ljava/lang/Object;
+Landroid/text/SpannableStringBuilder;->mSpanStarts:[I
+Landroid/text/StaticLayout;->mColumns:I
+Landroid/text/StaticLayout;->mLineCount:I
+Landroid/text/StaticLayout;->mLines:[I
+Landroid/text/TextLine;->obtain()Landroid/text/TextLine;
+Landroid/text/TextLine;->sCached:[Landroid/text/TextLine;
+Landroid/text/TextPaint;->setUnderlineText(IF)V
+Landroid/util/Log;->wtf(ILjava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;ZZ)I
+Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object;
+Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager;
+Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z
+Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager;
+Landroid/view/accessibility/AccessibilityManager;->sInstanceSync:Ljava/lang/Object;
+Landroid/view/accessibility/AccessibilityNodeInfo;->refresh(Landroid/os/Bundle;Z)Z
+Landroid/view/ActionMode;->isUiFocusable()Z
+Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
+Landroid/view/Choreographer;->doFrame(JI)V
+Landroid/view/Choreographer;->getFrameTime()J
+Landroid/view/Choreographer;->mCallbackQueues:[Landroid/view/Choreographer$CallbackQueue;
+Landroid/view/Choreographer;->postCallback(ILjava/lang/Runnable;Ljava/lang/Object;)V
+Landroid/view/Choreographer;->removeCallbacks(ILjava/lang/Runnable;Ljava/lang/Object;)V
+Landroid/view/Choreographer;->scheduleVsyncLocked()V
+Landroid/view/ContextThemeWrapper;->mResources:Landroid/content/res/Resources;
+Landroid/view/ContextThemeWrapper;->mTheme:Landroid/content/res/Resources$Theme;
+Landroid/view/ContextThemeWrapper;->mThemeResource:I
+Landroid/view/InputDevice;->isExternal()Z
+Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V
+Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
+Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
+Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String;
+Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mNextServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V
+Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V
+Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
+Landroid/view/IWindowManager;->getAnimationScale(I)F
+Landroid/view/IWindowManager;->hasNavigationBar()Z
+Landroid/view/IWindowManager;->setAnimationScale(IF)V
+Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
+Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
+Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+Landroid/view/LayoutInflater;->mConstructorArgs:[Ljava/lang/Object;
+Landroid/view/LayoutInflater;->mFactory2:Landroid/view/LayoutInflater$Factory2;
+Landroid/view/LayoutInflater;->mFactory:Landroid/view/LayoutInflater$Factory;
+Landroid/view/LayoutInflater;->mFactorySet:Z
+Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap;
+Landroid/view/MotionEvent;->HISTORY_CURRENT:I
+Landroid/view/MotionEvent;->mNativePtr:J
+Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F
+Landroid/view/MotionEvent;->scale(F)V
+Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
+Landroid/view/SurfaceView;->mCallbacks:Ljava/util/ArrayList;
+Landroid/view/SurfaceView;->mFormat:I
+Landroid/view/SurfaceView;->mRequestedFormat:I
+Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker;->logEvent(Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;)V
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(III)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(IIILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(II)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
+Landroid/view/TextureView;->mLayer:Landroid/view/HardwareLayer;
+Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture;
+Landroid/view/TextureView;->mUpdateListener:Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;
+Landroid/view/TouchDelegate;->mDelegateTargeted:Z
+Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker;
+Landroid/view/View;->clearAccessibilityFocus()V
+Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z
+Landroid/view/View;->computeOpaqueFlags()V
+Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z
+Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z
+Landroid/view/ViewConfiguration;->sHasPermanentMenuKey:Z
+Landroid/view/View;->createSnapshot(Landroid/view/ViewDebug$CanvasProvider;Z)Landroid/graphics/Bitmap;
+Landroid/view/ViewDebug;->dispatchCommand(Landroid/view/View;Ljava/lang/String;Ljava/lang/String;Ljava/io/OutputStream;)V
+Landroid/view/ViewDebug;->dump(Landroid/view/View;ZZLjava/io/OutputStream;)V
+Landroid/view/View;->dispatchAttachedToWindow(Landroid/view/View$AttachInfo;I)V
+Landroid/view/View;->dispatchDetachedFromWindow()V
+Landroid/view/View;->fitsSystemWindows()Z
+Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo;
+Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
+Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I
+Landroid/view/ViewGroup;->FLAG_USE_CHILD_DRAWING_ORDER:I
+Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V
+Landroid/view/ViewGroup$MarginLayoutParams;->endMargin:I
+Landroid/view/ViewGroup$MarginLayoutParams;->startMargin:I
+Landroid/view/ViewGroup;->mChildren:[Landroid/view/View;
+Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget;
+Landroid/view/ViewGroup;->mGroupFlags:I
+Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
+Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
+Landroid/view/View;->isPaddingResolved()Z
+Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z
+Landroid/view/View;->isVisibleToUser()Z
+Landroid/view/View$ListenerInfo;->mOnClickListener:Landroid/view/View$OnClickListener;
+Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener;
+Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate;
+Landroid/view/View;->mAttachInfo:Landroid/view/View$AttachInfo;
+Landroid/view/View;->mBottom:I
+Landroid/view/View;->mContext:Landroid/content/Context;
+Landroid/view/View;->mDrawingCache:Landroid/graphics/Bitmap;
+Landroid/view/View;->mLeft:I
+Landroid/view/View;->mListenerInfo:Landroid/view/View$ListenerInfo;
+Landroid/view/View;->mMinHeight:I
+Landroid/view/View;->mMinWidth:I
+Landroid/view/View;->mPaddingLeft:I
+Landroid/view/View;->mPaddingRight:I
+Landroid/view/View;->mParent:Landroid/view/ViewParent;
+Landroid/view/View;->mPrivateFlags3:I
+Landroid/view/View;->mRecreateDisplayList:Z
+Landroid/view/View;->mResources:Landroid/content/res/Resources;
+Landroid/view/View;->mRight:I
+Landroid/view/View;->mScrollCache:Landroid/view/View$ScrollabilityCache;
+Landroid/view/View;->mScrollX:I
+Landroid/view/View;->mScrollY:I
+Landroid/view/View;->mStartActivityRequestWho:Ljava/lang/String;
+Landroid/view/View;->mTop:I
+Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap;
+Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V
+Landroid/view/View;->recomputePadding()V
+Landroid/view/View;->requestAccessibilityFocus()Z
+Landroid/view/View;->resetPaddingToInitialValues()V
+Landroid/view/ViewRootImpl;->detachFunctor(J)V
+Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V
+Landroid/view/ViewRootImpl;->mStopped:Z
+Landroid/view/View;->setAssistBlocked(Z)V
+Landroid/view/View;->setFrame(IIII)Z
+Landroid/view/View;->setIsRootNamespace(Z)V
+Landroid/view/View;->startActivityForResult(Landroid/content/Intent;I)V
+Landroid/view/View;->STATUS_BAR_DISABLE_BACK:I
+Landroid/view/View;->STATUS_BAR_DISABLE_EXPAND:I
+Landroid/view/View;->STATUS_BAR_DISABLE_HOME:I
+Landroid/view/View;->STATUS_BAR_DISABLE_RECENT:I
+Landroid/view/View;->toGlobalMotionEvent(Landroid/view/MotionEvent;)Z
+Landroid/view/View;->toLocalMotionEvent(Landroid/view/MotionEvent;)Z
+Landroid/view/ViewTreeObserver;->addOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->setTouchableInsets(I)V
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->TOUCHABLE_INSETS_REGION:I
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->touchableRegion:Landroid/graphics/Region;
+Landroid/view/ViewTreeObserver;->removeOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V
+Landroid/view/WindowManagerGlobal;->getInstance()Landroid/view/WindowManagerGlobal;
+Landroid/view/WindowManagerGlobal;->getRootView(Ljava/lang/String;)Landroid/view/View;
+Landroid/view/WindowManagerGlobal;->getViewRootNames()[Ljava/lang/String;
+Landroid/view/WindowManagerGlobal;->getWindowManagerService()Landroid/view/IWindowManager;
+Landroid/view/WindowManagerGlobal;->mLock:Ljava/lang/Object;
+Landroid/view/WindowManagerGlobal;->mViews:Ljava/util/ArrayList;
+Landroid/view/WindowManagerGlobal;->trimMemory(I)V
+Landroid/view/WindowManager$LayoutParams;->needsMenuKey:I
+Landroid/view/WindowManager$LayoutParams;->NEEDS_MENU_SET_TRUE:I
+Landroid/view/WindowManager$LayoutParams;->privateFlags:I
+Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J
+Landroid/view/Window;->mAppName:Ljava/lang/String;
+Landroid/view/Window;->mAppToken:Landroid/os/IBinder;
+Landroid/view/Window;->mHardwareAccelerated:Z
+Landroid/webkit/WebSettings;->setNavDump(Z)V
+Landroid/webkit/WebSettings;->setPluginsEnabled(Z)V
+Landroid/webkit/WebView;->debugDump()V
+Landroid/webkit/WebView;->disablePlatformNotifications()V
+Landroid/webkit/WebView;->emulateShiftHeld()V
+Landroid/webkit/WebView;->enablePlatformNotifications()V
+Landroid/webkit/WebViewFactory;->getLoadedPackageInfo()Landroid/content/pm/PackageInfo;
+Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProvider;
+Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo;
+Landroid/webkit/WebView;->getVisibleTitleHeight()I
+Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->isPaused()Z
+Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->notifyFindDialogDismissed()V
+Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z
+Landroid/webkit/WebView;->savePicture(Landroid/os/Bundle;Ljava/io/File;)Z
+Landroid/webkit/WebView;->sEnforceThreadChecking:Z
+Landroid/widget/AbsListView$FlingRunnable;->endFling()V
+Landroid/widget/AbsListView;->invokeOnItemScrollListener()V
+Landroid/widget/AbsListView;->isVerticalScrollBarHidden()Z
+Landroid/widget/AbsListView;->mAdapter:Landroid/widget/ListAdapter;
+Landroid/widget/AbsListView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
+Landroid/widget/AbsListView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
+Landroid/widget/AbsListView;->mFastScroll:Landroid/widget/FastScroller;
+Landroid/widget/AbsListView;->mFlingRunnable:Landroid/widget/AbsListView$FlingRunnable;
+Landroid/widget/AbsListView;->mIsChildViewEnabled:Z
+Landroid/widget/AbsListView;->mMaximumVelocity:I
+Landroid/widget/AbsListView;->mMotionPosition:I
+Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener;
+Landroid/widget/AbsListView;->mRecycler:Landroid/widget/AbsListView$RecycleBin;
+Landroid/widget/AbsListView;->mSelectionTopPadding:I
+Landroid/widget/AbsListView;->mSelectorPosition:I
+Landroid/widget/AbsListView;->mSelectorRect:Landroid/graphics/Rect;
+Landroid/widget/AbsListView;->mTouchMode:I
+Landroid/widget/AbsListView;->mTouchSlop:I
+Landroid/widget/AbsListView;->mVelocityTracker:Landroid/view/VelocityTracker;
+Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJFF)Z
+Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJ)Z
+Landroid/widget/AbsListView;->smoothScrollBy(IIZZ)V
+Landroid/widget/AbsListView;->trackMotionScroll(II)Z
+Landroid/widget/AbsSeekBar;->mIsDragging:Z
+Landroid/widget/AbsSeekBar;->mSplitTrack:Z
+Landroid/widget/AbsSeekBar;->mThumb:Landroid/graphics/drawable/Drawable;
+Landroid/widget/AbsSeekBar;->mTouchProgressOffset:F
+Landroid/widget/ActivityChooserView;->setExpandActivityOverflowButtonDrawable(Landroid/graphics/drawable/Drawable;)V
+Landroid/widget/AdapterView;->mDataChanged:Z
+Landroid/widget/AdapterView;->setNextSelectedPositionInt(I)V
+Landroid/widget/AdapterView;->setSelectedPositionInt(I)V
+Landroid/widget/AutoCompleteTextView;->doAfterTextChanged()V
+Landroid/widget/AutoCompleteTextView;->doBeforeTextChanged()V
+Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V
+Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
+Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V
+Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/DatePicker;->mDelegate:Landroid/widget/DatePicker$DatePickerDelegate;
+Landroid/widget/EdgeEffect;->mPaint:Landroid/graphics/Paint;
+Landroid/widget/Editor;->mShowCursor:J
+Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/Gallery;->mDownTouchView:Landroid/view/View;
+Landroid/widget/GridView;->mColumnWidth:I
+Landroid/widget/GridView;->mHorizontalSpacing:I
+Landroid/widget/GridView;->mNumColumns:I
+Landroid/widget/GridView;->mRequestedNumColumns:I
+Landroid/widget/GridView;->mVerticalSpacing:I
+Landroid/widget/HorizontalScrollView;->mEdgeGlowLeft:Landroid/widget/EdgeEffect;
+Landroid/widget/HorizontalScrollView;->mEdgeGlowRight:Landroid/widget/EdgeEffect;
+Landroid/widget/HorizontalScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/ImageView;->mAdjustViewBounds:Z
+Landroid/widget/ImageView;->mAlpha:I
+Landroid/widget/ImageView;->mMaxHeight:I
+Landroid/widget/ImageView;->mMaxWidth:I
+Landroid/widget/LinearLayout;->mGravity:I
+Landroid/widget/LinearLayout;->mUseLargestChild:Z
+Landroid/widget/ListPopupWindow;->mPopup:Landroid/widget/PopupWindow;
+Landroid/widget/ListPopupWindow;->setForceIgnoreOutsideTouch(Z)V
+Landroid/widget/ListView;->mAreAllItemsSelectable:Z
+Landroid/widget/ListView;->setSelectionInt(I)V
+Landroid/widget/MediaController;->mAnchor:Landroid/view/View;
+Landroid/widget/MediaController;->mDecor:Landroid/view/View;
+Landroid/widget/MediaController;->mDecorLayoutParams:Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/MediaController;->mWindowManager:Landroid/view/WindowManager;
+Landroid/widget/NumberPicker;->mInputText:Landroid/widget/EditText;
+Landroid/widget/NumberPicker;->mSelectionDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/NumberPicker;->mSelectorWheelPaint:Landroid/graphics/Paint;
+Landroid/widget/PopupMenu;->mPopup:Lcom/android/internal/view/menu/MenuPopupHelper;
+Landroid/widget/PopupWindow;->computeAnimationResource()I
+Landroid/widget/PopupWindow;->createPopupLayoutParams(Landroid/os/IBinder;)Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/PopupWindow;->invokePopup(Landroid/view/WindowManager$LayoutParams;)V
+Landroid/widget/PopupWindow;->mAboveAnchor:Z
+Landroid/widget/PopupWindow;->mAnchor:Ljava/lang/ref/WeakReference;
+Landroid/widget/PopupWindow;->mAnimationStyle:I
+Landroid/widget/PopupWindow;->mBackgroundView:Landroid/view/View;
+Landroid/widget/PopupWindow;->mContentView:Landroid/view/View;
+Landroid/widget/PopupWindow;->mHeightMode:I
+Landroid/widget/PopupWindow;->mIsDropdown:Z
+Landroid/widget/PopupWindow;->mIsShowing:Z
+Landroid/widget/PopupWindow;->mLastHeight:I
+Landroid/widget/PopupWindow;->mLastWidth:I
+Landroid/widget/PopupWindow;->mOnScrollChangedListener:Landroid/view/ViewTreeObserver$OnScrollChangedListener;
+Landroid/widget/PopupWindow;->mWidthMode:I
+Landroid/widget/PopupWindow;->preparePopup(Landroid/view/WindowManager$LayoutParams;)V
+Landroid/widget/PopupWindow;->setLayoutInScreenEnabled(Z)V
+Landroid/widget/PopupWindow;->setLayoutInsetDecor(Z)V
+Landroid/widget/PopupWindow;->setTouchModal(Z)V
+Landroid/widget/ProgressBar;->mCurrentDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/ProgressBar;->mDuration:I
+Landroid/widget/ProgressBar;->mIndeterminate:Z
+Landroid/widget/ProgressBar;->mMaxHeight:I
+Landroid/widget/ProgressBar;->mMinHeight:I
+Landroid/widget/ProgressBar;->mOnlyIndeterminate:Z
+Landroid/widget/RelativeLayout$LayoutParams;->mBottom:I
+Landroid/widget/RelativeLayout$LayoutParams;->mLeft:I
+Landroid/widget/RelativeLayout$LayoutParams;->mRight:I
+Landroid/widget/RelativeLayout$LayoutParams;->mTop:I
+Landroid/widget/RelativeLayout;->mGravity:I
+Landroid/widget/RemoteViews$Action;->mergeBehavior()I
+Landroid/widget/RemoteViews$Action;->viewId:I
+Landroid/widget/RemoteViews;->mActions:Ljava/util/ArrayList;
+Landroid/widget/RemoteViews;->mApplication:Landroid/content/pm/ApplicationInfo;
+Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String;
+Landroid/widget/ScrollView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
+Landroid/widget/ScrollView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
+Landroid/widget/ScrollView;->mIsBeingDragged:Z
+Landroid/widget/ScrollView;->mOverflingDistance:I
+Landroid/widget/ScrollView;->mOverscrollDistance:I
+Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/SearchView;->mCloseButton:Landroid/widget/ImageView;
+Landroid/widget/SearchView;->mSearchButton:Landroid/widget/ImageView;
+Landroid/widget/SearchView;->mSearchPlate:Landroid/view/View;
+Landroid/widget/SearchView;->onCloseClicked()V
+Landroid/widget/SearchView;->setQuery(Ljava/lang/CharSequence;)V
+Landroid/widget/SlidingDrawer;->mTopOffset:I
+Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup;
+Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
+Landroid/widget/TextView;->assumeLayout()V
+Landroid/widget/TextView;->createEditorIfNeeded()V
+Landroid/widget/TextView;->mCursorDrawableRes:I
+Landroid/widget/TextView;->mCurTextColor:I
+Landroid/widget/TextView;->mEditor:Landroid/widget/Editor;
+Landroid/widget/TextView;->mListeners:Ljava/util/ArrayList;
+Landroid/widget/TextView;->mMarquee:Landroid/widget/TextView$Marquee;
+Landroid/widget/TextView;->mMaximum:I
+Landroid/widget/TextView;->mMaxMode:I
+Landroid/widget/TextView;->mSingleLine:Z
+Landroid/widget/VideoView;->mCurrentBufferPercentage:I
+Landroid/widget/VideoView;->mMediaController:Landroid/widget/MediaController;
+Landroid/widget/VideoView;->mSHCallback:Landroid/view/SurfaceHolder$Callback;
+Landroid/widget/VideoView;->mUri:Landroid/net/Uri;
+Landroid/widget/VideoView;->mVideoHeight:I
+Landroid/widget/VideoView;->mVideoWidth:I
+Lcom/android/internal/app/IBatteryStats;->getStatistics()[B
+Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats;
+Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/os/BatteryStatsImpl;->getGlobalWifiRunningTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->getScreenOnTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->getUidStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl$Timer;->getCountLocked(I)I
+Lcom/android/internal/os/BatteryStatsImpl$Timer;->getTotalTimeLocked(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getProcessStats()Landroid/util/ArrayMap;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getSensorStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getUid()I
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWakelockStats()Landroid/util/ArrayMap;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiRunningTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiScanTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getForegroundTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getStarts(I)I
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getSystemTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getUserTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getHandle()I
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getSensorTime()Lcom/android/internal/os/BatteryStatsImpl$Timer;
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Wakelock;->getWakeTime(I)Lcom/android/internal/os/BatteryStatsImpl$Timer;
+Lcom/android/internal/os/FuseAppLoop;->onCommand(IJJJI[B)V
+Lcom/android/internal/os/FuseAppLoop;->onOpen(JJ)[B
+Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;)D
+Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;I)D
+Lcom/android/internal/os/PowerProfile;->getBatteryCapacity()D
+Lcom/android/internal/R$array;->config_mobile_hotspot_provision_app:I
+Lcom/android/internal/R$array;->config_tether_wifi_regexs:I
+Lcom/android/internal/R$attr;->switchStyle:I
+Lcom/android/internal/R$bool;->config_mms_content_disposition_support:I
+Lcom/android/internal/R$bool;->config_showNavigationBar:I
+Lcom/android/internal/R$dimen;->navigation_bar_height:I
+Lcom/android/internal/R$dimen;->navigation_bar_height_landscape:I
+Lcom/android/internal/R$dimen;->status_bar_height:I
+Lcom/android/internal/R$drawable;->ic_menu_close_clear_cancel:I
+Lcom/android/internal/R$id;->amPm:I
+Lcom/android/internal/R$id;->edittext_container:I
+Lcom/android/internal/R$id;->icon:I
+Lcom/android/internal/R$id;->message:I
+Lcom/android/internal/R$id;->minute:I
+Lcom/android/internal/R$id;->shortcut:I
+Lcom/android/internal/R$id;->text:I
+Lcom/android/internal/R$id;->time:I
+Lcom/android/internal/R$id;->timePicker:I
+Lcom/android/internal/R$id;->title_container:I
+Lcom/android/internal/R$id;->title:I
+Lcom/android/internal/R$integer;->config_screenBrightnessDim:I
+Lcom/android/internal/R$layout;->screen_title:I
+Lcom/android/internal/R$styleable;->CompoundButton_button:I
+Lcom/android/internal/R$styleable;->CompoundButton:[I
+Lcom/android/internal/R$styleable;->IconMenuView:[I
+Lcom/android/internal/R$styleable;->ImageView:[I
+Lcom/android/internal/R$styleable;->ImageView_src:I
+Lcom/android/internal/R$styleable;->ScrollView:[I
+Lcom/android/internal/R$styleable;->TabWidget:[I
+Lcom/android/internal/R$styleable;->TextView_drawableBottom:I
+Lcom/android/internal/R$styleable;->TextView_drawableLeft:I
+Lcom/android/internal/R$styleable;->TextView_drawableRight:I
+Lcom/android/internal/R$styleable;->TextView_drawableTop:I
+Lcom/android/internal/R$styleable;->TextView:[I
+Lcom/android/internal/R$styleable;->View_background:I
+Lcom/android/internal/R$styleable;->View:[I
+Lcom/android/internal/R$styleable;->View_id:I
+Lcom/android/internal/R$styleable;->ViewStub:[I
+Lcom/android/internal/R$styleable;->ViewStub_inflatedId:I
+Lcom/android/internal/R$styleable;->ViewStub_layout:I
+Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
+Lcom/android/internal/R$xml;->power_profile:I
+Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
+Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
+Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/ITelephony;->endCall()Z
+Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
+Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
+Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
+Lcom/android/internal/view/InputBindResult;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context;
+Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
+Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V
+Lcom/android/internal/view/menu/MenuItemImpl;->mIconResId:I
+Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
+Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
+Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V
+Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
+Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
+Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
+Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
+Ldalvik/system/CloseGuard;->warnIfOpen()V
+Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
+Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
+Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
+Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
+Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
+Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
+Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
+Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
+Ldalvik/system/VMDebug;->dumpReferenceTables()V
+Ldalvik/system/VMRuntime;->clearGrowthLimit()V
+Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
+Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
+Ldalvik/system/VMRuntime;->is64Bit()Z
+Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
+Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
+Ldalvik/system/VMRuntime;->registerNativeFree(I)V
+Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
+Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
+Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
+Ldalvik/system/VMRuntime;->trackExternalFree(J)V
+Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
+Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
+Ljava/io/FileDescriptor;->descriptor:I
+Ljava/io/FileDescriptor;->getInt$()I
+Ljava/io/FileDescriptor;->setInt$(I)V
+Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J
+Ljava/io/ObjectStreamClass;->newInstance(Ljava/lang/Class;J)Ljava/lang/Object;
+Ljava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object;
+Ljava/lang/Class;->dexCache:Ljava/lang/Object;
+Ljava/lang/Class;->dexClassDefIndex:I
+Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
+Ljava/lang/Daemons$Daemon;->stop()V
+Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread;
+Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
+Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
+Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
+Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
+Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
+Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
+Ljava/lang/ThreadGroup;->parent:Ljava/lang/ThreadGroup;
+Ljava/lang/ThreadGroup;->systemThreadGroup:Ljava/lang/ThreadGroup;
+Ljava/lang/Thread;->inheritableThreadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap;
+Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
+Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
+Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
+Ljava/net/InetAddress;->clearDnsCache()V
+Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z
+Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;
+Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
+Ljava/net/URI;->host:Ljava/lang/String;
+Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
+Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String;
+Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V
+Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
+Ljava/util/ArrayList$SubList;->parentOffset:I
+Ljava/util/ArrayList$SubList;->size:I
+Ljava/util/Arrays$ArrayList;->a:[Ljava/lang/Object;
+Ljava/util/Calendar;->zone:Ljava/util/TimeZone;
+Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
+Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
+Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
+Lorg/json/JSONArray;->values:Ljava/util/List;
+Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7bfb20f8cc35..04c44a3818a3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -425,6 +425,12 @@ import java.util.List;
* safely called after {@link #onPause()} and allows and application to safely
* wait until {@link #onStop()} to save persistent state.</p>
*
+ * <p class="note">For applications targeting platforms starting with
+ * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
+ * will always be called after {@link #onStop}, so an application may safely
+ * perform fragment transactions in {@link #onStop} and will be able to save
+ * persistent state later.</p>
+ *
* <p>For those methods that are not marked as being killable, the activity's
* process will not be killed by the system starting from the time the method
* is called and continuing after it returns. Thus an activity is in the killable
@@ -1577,8 +1583,11 @@ public class Activity extends ContextThemeWrapper
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
- * <p>If called, this method will occur before {@link #onStop}. There are
- * no guarantees about whether it will occur before or after {@link #onPause}.
+ * <p>If called, this method will occur after {@link #onStop} for applications
+ * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
+ * For applications targeting earlier platform versions this method will occur
+ * before {@link #onStop} and there are no guarantees about whether it will
+ * occur before or after {@link #onPause}.
*
* @param outState Bundle in which to place your saved state.
*
@@ -7143,7 +7152,10 @@ public class Activity extends ContextThemeWrapper
boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
if (isAppDebuggable || isApiWarningEnabled) {
- if (VMRuntime.getRuntime().hasUsedHiddenApi()) {
+ if (!mMainThread.mHiddenApiWarningShown && VMRuntime.getRuntime().hasUsedHiddenApi()) {
+ // Only show the warning once per process.
+ mMainThread.mHiddenApiWarningShown = true;
+
String appName = getApplicationInfo().loadLabel(getPackageManager())
.toString();
String warning = "Detected problems with API compatiblity\n"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e8535cd1bca2..42825f0ad8d4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -291,6 +291,7 @@ public final class ActivityThread extends ClientTransactionHandler {
boolean mJitEnabled = false;
boolean mSomeActivitiesChanged = false;
boolean mUpdatingSystemConfig = false;
+ /* package */ boolean mHiddenApiWarningShown = false;
// These can be accessed by multiple threads; mResourcesManager is the lock.
// XXX For now we keep around information about all packages we have
@@ -487,12 +488,14 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- public boolean isPreHoneycomb() {
- if (activity != null) {
- return activity.getApplicationInfo().targetSdkVersion
- < android.os.Build.VERSION_CODES.HONEYCOMB;
- }
- return false;
+ private boolean isPreHoneycomb() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.HONEYCOMB;
+ }
+
+ private boolean isPreP() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.P;
}
public boolean isPersistable() {
@@ -4164,9 +4167,12 @@ public final class ActivityThread extends ClientTransactionHandler {
* {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
*/
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
+ // Before P onSaveInstanceState was called before onStop, starting with P it's
+ // called after. Before Honeycomb state was always saved before onPause.
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
- if (shouldSaveState) {
+ final boolean isPreP = r.isPreP();
+ if (shouldSaveState && isPreP) {
callActivityOnSaveInstanceState(r);
}
@@ -4185,6 +4191,10 @@ public final class ActivityThread extends ClientTransactionHandler {
r.setState(ON_STOP);
EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
+
+ if (shouldSaveState && !isPreP) {
+ callActivityOnSaveInstanceState(r);
+ }
}
private void updateVisibility(ActivityClientRecord r, boolean show) {
diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java
deleted file mode 100644
index 427a0386e87c..000000000000
--- a/core/java/android/app/EphemeralResolverService.java
+++ /dev/null
@@ -1,116 +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.app;
-
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.app.InstantAppResolverService.InstantAppResolutionCallback;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.EphemeralResolveInfo;
-import android.content.pm.InstantAppResolveInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Base class for implementing the resolver service.
- * @hide
- * @removed
- * @deprecated use InstantAppResolverService instead
- */
-@Deprecated
-@SystemApi
-public abstract class EphemeralResolverService extends InstantAppResolverService {
- private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
- private static final String TAG = "PackageManager";
-
- /**
- * Called to retrieve resolve info for ephemeral applications.
- *
- * @param digestPrefix The hash prefix of the ephemeral's domain.
- * @param prefixMask A mask that was applied to each digest prefix. This should
- * be used when comparing against the digest prefixes as all bits might
- * not be set.
- * @deprecated use {@link #onGetEphemeralResolveInfo(int[])} instead
- */
- @Deprecated
- public abstract List<EphemeralResolveInfo> onEphemeralResolveInfoList(
- int digestPrefix[], int prefix);
-
- /**
- * Called to retrieve resolve info for ephemeral applications.
- *
- * @param digestPrefix The hash prefix of the ephemeral's domain.
- */
- public List<EphemeralResolveInfo> onGetEphemeralResolveInfo(int digestPrefix[]) {
- return onEphemeralResolveInfoList(digestPrefix, 0xFFFFF000);
- }
-
- /**
- * Called to retrieve intent filters for ephemeral applications.
- *
- * @param hostName The name of the host to get intent filters for.
- */
- public EphemeralResolveInfo onGetEphemeralIntentFilter(String hostName) {
- throw new IllegalStateException("Must define");
- }
-
- @Override
- public Looper getLooper() {
- return super.getLooper();
- }
-
- @Override
- void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
- InstantAppResolutionCallback callback) {
- if (DEBUG_EPHEMERAL) {
- Log.d(TAG, "Legacy resolver; getInstantAppResolveInfo;"
- + " prefix: " + Arrays.toString(digestPrefix));
- }
- final List<EphemeralResolveInfo> response = onGetEphemeralResolveInfo(digestPrefix);
- final int responseSize = response == null ? 0 : response.size();
- final List<InstantAppResolveInfo> resultList = new ArrayList<>(responseSize);
- for (int i = 0; i < responseSize; i++) {
- resultList.add(response.get(i).getInstantAppResolveInfo());
- }
- callback.onInstantAppResolveInfo(resultList);
- }
-
- @Override
- void _onGetInstantAppIntentFilter(int[] digestPrefix, String token,
- String hostName, InstantAppResolutionCallback callback) {
- if (DEBUG_EPHEMERAL) {
- Log.d(TAG, "Legacy resolver; getInstantAppIntentFilter;"
- + " prefix: " + Arrays.toString(digestPrefix));
- }
- final EphemeralResolveInfo response = onGetEphemeralIntentFilter(hostName);
- final List<InstantAppResolveInfo> resultList = new ArrayList<>(1);
- resultList.add(response.getInstantAppResolveInfo());
- callback.onInstantAppResolveInfo(resultList);
- }
-}
diff --git a/core/java/android/app/IInstantAppResolver.aidl b/core/java/android/app/IInstantAppResolver.aidl
index 805d8c057d27..ae200578d829 100644
--- a/core/java/android/app/IInstantAppResolver.aidl
+++ b/core/java/android/app/IInstantAppResolver.aidl
@@ -16,13 +16,15 @@
package android.app;
+import android.content.Intent;
import android.os.IRemoteCallback;
/** @hide */
oneway interface IInstantAppResolver {
- void getInstantAppResolveInfoList(in int[] digestPrefix,
+ void getInstantAppResolveInfoList(in Intent sanitizedIntent, in int[] hostDigestPrefix,
String token, int sequence, IRemoteCallback callback);
- void getInstantAppIntentFilterList(in int[] digestPrefix,
- String token, String hostName, IRemoteCallback callback);
+ void getInstantAppIntentFilterList(in Intent sanitizedIntent, in int[] hostDigestPrefix,
+ String token, IRemoteCallback callback);
+
}
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index c5dc86c79ef9..76a36820ed83 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -17,7 +17,6 @@
package android.app;
import android.annotation.SystemApi;
-import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.InstantAppResolveInfo;
@@ -35,6 +34,7 @@ import android.util.Slog;
import com.android.internal.os.SomeArgs;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
/**
@@ -43,7 +43,7 @@ import java.util.List;
*/
@SystemApi
public abstract class InstantAppResolverService extends Service {
- private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+ private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
private static final String TAG = "PackageManager";
/** @hide */
@@ -53,23 +53,65 @@ public abstract class InstantAppResolverService extends Service {
Handler mHandler;
/**
- * Called to retrieve resolve info for instant applications.
+ * Called to retrieve resolve info for instant applications immediately.
*
* @param digestPrefix The hash prefix of the instant app's domain.
+ * @deprecated should implement {@link #onGetInstantAppResolveInfo(Intent, int[], String,
+ * InstantAppResolutionCallback)}
*/
+ @Deprecated
public void onGetInstantAppResolveInfo(
int digestPrefix[], String token, InstantAppResolutionCallback callback) {
throw new IllegalStateException("Must define");
}
/**
- * Called to retrieve intent filters for instant applications.
+ * Called to retrieve intent filters for instant applications from potentially expensive
+ * sources.
*
* @param digestPrefix The hash prefix of the instant app's domain.
+ * @deprecated should implement {@link #onGetInstantAppIntentFilter(Intent, int[], String,
+ * InstantAppResolutionCallback)}
*/
+ @Deprecated
public void onGetInstantAppIntentFilter(
int digestPrefix[], String token, InstantAppResolutionCallback callback) {
- throw new IllegalStateException("Must define");
+ throw new IllegalStateException("Must define onGetInstantAppIntentFilter");
+ }
+
+ /**
+ * Called to retrieve resolve info for instant applications immediately.
+ *
+ * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
+ * @param hostDigestPrefix The hash prefix of the instant app's domain.
+ */
+ public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix,
+ String token, InstantAppResolutionCallback callback) {
+ // if not overridden, forward to old methods and filter out non-web intents
+ if (sanitizedIntent.isBrowsableWebIntent()) {
+ onGetInstantAppResolveInfo(hostDigestPrefix, token, callback);
+ } else {
+ callback.onInstantAppResolveInfo(Collections.emptyList());
+ }
+ }
+
+ /**
+ * Called to retrieve intent filters for instant applications from potentially expensive
+ * sources.
+ *
+ * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
+ * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is
+ * defined.
+ */
+ public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix,
+ String token, InstantAppResolutionCallback callback) {
+ Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden");
+ // if not overridden, forward to old methods and filter out non-web intents
+ if (sanitizedIntent.isBrowsableWebIntent()) {
+ onGetInstantAppIntentFilter(hostDigestPrefix, token, callback);
+ } else {
+ callback.onInstantAppResolveInfo(Collections.emptyList());
+ }
}
/**
@@ -89,33 +131,33 @@ public abstract class InstantAppResolverService extends Service {
public final IBinder onBind(Intent intent) {
return new IInstantAppResolver.Stub() {
@Override
- public void getInstantAppResolveInfoList(
- int digestPrefix[], String token, int sequence, IRemoteCallback callback) {
- if (DEBUG_EPHEMERAL) {
+ public void getInstantAppResolveInfoList(Intent sanitizedIntent, int[] digestPrefix,
+ String token, int sequence, IRemoteCallback callback) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "[" + token + "] Phase1 called; posting");
}
final SomeArgs args = SomeArgs.obtain();
args.arg1 = callback;
args.arg2 = digestPrefix;
args.arg3 = token;
- mHandler.obtainMessage(
- ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, args)
- .sendToTarget();
+ args.arg4 = sanitizedIntent;
+ mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO,
+ sequence, 0, args).sendToTarget();
}
@Override
- public void getInstantAppIntentFilterList(
- int digestPrefix[], String token, String hostName, IRemoteCallback callback) {
- if (DEBUG_EPHEMERAL) {
+ public void getInstantAppIntentFilterList(Intent sanitizedIntent,
+ int[] digestPrefix, String token, IRemoteCallback callback) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "[" + token + "] Phase2 called; posting");
}
final SomeArgs args = SomeArgs.obtain();
args.arg1 = callback;
args.arg2 = digestPrefix;
args.arg3 = token;
- args.arg4 = hostName;
- mHandler.obtainMessage(
- ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, callback).sendToTarget();
+ args.arg4 = sanitizedIntent;
+ mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER,
+ callback).sendToTarget();
}
};
}
@@ -142,29 +184,9 @@ public abstract class InstantAppResolverService extends Service {
}
}
- @Deprecated
- void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
- InstantAppResolutionCallback callback) {
- if (DEBUG_EPHEMERAL) {
- Slog.d(TAG, "[" + token + "] Phase1 request;"
- + " prefix: " + Arrays.toString(digestPrefix));
- }
- onGetInstantAppResolveInfo(digestPrefix, token, callback);
- }
- @Deprecated
- void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName,
- InstantAppResolutionCallback callback) {
- if (DEBUG_EPHEMERAL) {
- Slog.d(TAG, "[" + token + "] Phase2 request;"
- + " prefix: " + Arrays.toString(digestPrefix));
- }
- onGetInstantAppIntentFilter(digestPrefix, token, callback);
- }
-
private final class ServiceHandler extends Handler {
public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1;
public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2;
-
public ServiceHandler(Looper looper) {
super(looper, null /*callback*/, true /*async*/);
}
@@ -179,9 +201,13 @@ public abstract class InstantAppResolverService extends Service {
final IRemoteCallback callback = (IRemoteCallback) args.arg1;
final int[] digestPrefix = (int[]) args.arg2;
final String token = (String) args.arg3;
+ final Intent intent = (Intent) args.arg4;
final int sequence = message.arg1;
- _onGetInstantAppResolveInfo(
- digestPrefix, token,
+ if (DEBUG_INSTANT) {
+ Slog.d(TAG, "[" + token + "] Phase1 request;"
+ + " prefix: " + Arrays.toString(digestPrefix));
+ }
+ onGetInstantAppResolveInfo(intent, digestPrefix, token,
new InstantAppResolutionCallback(sequence, callback));
} break;
@@ -190,9 +216,12 @@ public abstract class InstantAppResolverService extends Service {
final IRemoteCallback callback = (IRemoteCallback) args.arg1;
final int[] digestPrefix = (int[]) args.arg2;
final String token = (String) args.arg3;
- final String hostName = (String) args.arg4;
- _onGetInstantAppIntentFilter(
- digestPrefix, token, hostName,
+ final Intent intent = (Intent) args.arg4;
+ if (DEBUG_INSTANT) {
+ Slog.d(TAG, "[" + token + "] Phase2 request;"
+ + " prefix: " + Arrays.toString(digestPrefix));
+ }
+ onGetInstantAppIntentFilter(intent, digestPrefix, token,
new InstantAppResolutionCallback(-1 /*sequence*/, callback));
} break;
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index f21746cdd275..39bccc37d2b1 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -51,6 +51,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadSystemException;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -1329,11 +1330,7 @@ public class WallpaperManager {
private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos)
throws IOException {
- byte[] buffer = new byte[32768];
- int amt;
- while ((amt=data.read(buffer)) > 0) {
- fos.write(buffer, 0, amt);
- }
+ FileUtils.copy(data, fos);
}
/**
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 085fc79f58e4..46566e79d99c 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -146,6 +146,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
})
public @interface WindowConfig {}
+ /** @hide */
+ public static final int PINNED_WINDOWING_MODE_ELEVATION_IN_DIP = 5;
+
public WindowConfiguration() {
unset();
}
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 4461b16fe15c..38d9025cc82f 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -31,4 +31,7 @@ interface ISliceManager {
SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
+
+ byte[] getBackupPayload(int user);
+ void applyRestore(in byte[] payload, int user);
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 2fa9d8e0882f..3f13fffb3857 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -20,9 +20,9 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -193,10 +193,15 @@ public class SliceManager {
* <p>
* Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
* they still care about after a reboot.
+ * <p>
+ * This may only be called by apps that are the default launcher for the device
+ * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
*
* @param uri The uri of the slice being pinned.
* @param specs The list of supported {@link SliceSpec}s of the callback.
* @see SliceProvider#onSlicePinned(Uri)
+ * @see Intent#ACTION_ASSIST
+ * @see Intent#CATEGORY_HOME
*/
public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
try {
@@ -211,10 +216,15 @@ public class SliceManager {
* Remove a pin for a slice.
* <p>
* If the slice has no other pins/callbacks then the slice will be unpinned.
+ * <p>
+ * This may only be called by apps that are the default launcher for the device
+ * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
*
* @param uri The uri of the slice being unpinned.
* @see #pinSlice
* @see SliceProvider#onSliceUnpinned(Uri)
+ * @see Intent#ACTION_ASSIST
+ * @see Intent#CATEGORY_HOME
*/
public void unpinSlice(@NonNull Uri uri) {
try {
@@ -262,17 +272,13 @@ public class SliceManager {
*/
public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
ContentResolver resolver = mContext.getContentResolver();
- IContentProvider provider = resolver.acquireProvider(uri);
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
- final Bundle res = provider.call(resolver.getPackageName(),
- SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
} catch (RemoteException e) {
Log.e(TAG, "Unable to get slice descendants", e);
- } finally {
- resolver.releaseProvider(provider);
}
return Collections.emptyList();
}
@@ -288,17 +294,15 @@ public class SliceManager {
public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
ContentResolver resolver = mContext.getContentResolver();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
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(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
- null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras);
Bundle.setDefusable(res, true);
if (res == null) {
return null;
@@ -308,8 +312,55 @@ public class SliceManager {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Turns a slice intent into a slice uri. Expects an explicit intent. If there is no
+ * {@link android.content.ContentProvider} associated with the given intent this will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param intent The intent associated with a slice.
+ * @return The Slice Uri provided by the app or null if none is given.
+ * @see Slice
+ * @see SliceProvider#onMapIntentToUri(Intent)
+ * @see Intent
+ */
+ public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
+ Preconditions.checkNotNull(intent, "intent");
+ Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+ "Slice intent must be explicit %s", intent);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // 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 intentData;
+ }
+ // Otherwise ask the app
+ List<ResolveInfo> providers =
+ mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+ if (providers == null || providers.isEmpty()) {
+ throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ }
+ String authority = providers.get(0).providerInfo.authority;
+ Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).build();
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
}
}
@@ -329,7 +380,7 @@ public class SliceManager {
@NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(intent, "intent");
Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
- "Slice intent must be explicit " + intent);
+ "Slice intent must be explicit %s", intent);
ContentResolver resolver = mContext.getContentResolver();
// Check if the intent has data for the slice uri on it and use that
@@ -340,23 +391,21 @@ public class SliceManager {
// Otherwise ask the app
List<ResolveInfo> providers =
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
- if (providers == null) {
+ if (providers == null || providers.isEmpty()) {
throw new IllegalArgumentException("Unable to resolve intent " + intent);
}
String authority = providers.get(0).providerInfo.authority;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(mContext.getPackageName(),
- SliceProvider.METHOD_MAP_INTENT, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
}
@@ -365,8 +414,6 @@ public class SliceManager {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
}
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 336bd4782156..af4303263da5 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -114,6 +114,10 @@ public abstract class SliceProvider extends ContentProvider {
/**
* @hide
*/
+ public static final String METHOD_MAP_ONLY_INTENT = "map_only";
+ /**
+ * @hide
+ */
public static final String METHOD_PIN = "pin";
/**
* @hide
@@ -341,6 +345,13 @@ public abstract class SliceProvider extends ContentProvider {
b.putParcelable(EXTRA_SLICE, null);
}
return b;
+ } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
+ Intent intent = extras.getParcelable(EXTRA_INTENT);
+ if (intent == null) return null;
+ Uri uri = onMapIntentToUri(intent);
+ Bundle b = new Bundle();
+ b.putParcelable(EXTRA_SLICE, uri);
+ return b;
} else if (method.equals(METHOD_PIN)) {
Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e02a29494296..9b62f192ae62 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -50,6 +50,7 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
@@ -1553,16 +1554,6 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_INSTALL_FAILURE = "android.intent.action.INSTALL_FAILURE";
/**
- * @hide
- * @removed
- * @deprecated Do not use. This will go away.
- * Replace with {@link #ACTION_INSTALL_INSTANT_APP_PACKAGE}.
- */
- @SystemApi
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE
- = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
- /**
* Activity Action: Launch instant application installer.
* <p class="note">
* This is a protected intent that can only be sent by the system.
@@ -1576,16 +1567,6 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
/**
- * @hide
- * @removed
- * @deprecated Do not use. This will go away.
- * Replace with {@link #ACTION_RESOLVE_INSTANT_APP_PACKAGE}.
- */
- @SystemApi
- @SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE
- = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
- /**
* Service Action: Resolve instant application.
* <p>
* The system will have a persistent connection to this service.
@@ -1600,16 +1581,6 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
/**
- * @hide
- * @removed
- * @deprecated Do not use. This will go away.
- * Replace with {@link #ACTION_INSTANT_APP_RESOLVER_SETTINGS}.
- */
- @SystemApi
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_EPHEMERAL_RESOLVER_SETTINGS
- = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS";
- /**
* Activity Action: Launch instant app settings.
*
* <p class="note">
@@ -4443,45 +4414,109 @@ public class Intent implements Parcelable, Cloneable {
/**
* A {@link IntentSender} to start after ephemeral installation success.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_SUCCESS).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
/**
+ * A {@link IntentSender} to start after instant app installation success.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_SUCCESS =
+ "android.intent.extra.INSTANT_APP_SUCCESS";
+
+ /**
* A {@link IntentSender} to start after ephemeral installation failure.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_FAILURE).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
/**
+ * A {@link IntentSender} to start after instant app installation failure.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_FAILURE =
+ "android.intent.extra.INSTANT_APP_FAILURE";
+
+ /**
* The host name that triggered an ephemeral resolution.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_HOSTNAME).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_HOSTNAME = "android.intent.extra.EPHEMERAL_HOSTNAME";
/**
+ * The host name that triggered an instant app resolution.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_HOSTNAME =
+ "android.intent.extra.INSTANT_APP_HOSTNAME";
+
+ /**
* An opaque token to track ephemeral resolution.
+ * @deprecated Use {@link #EXTRA_INSTANT_APP_TOKEN).
+ * @removed
* @hide
*/
+ @Deprecated
public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
/**
+ * An opaque token to track instant app resolution.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_TOKEN =
+ "android.intent.extra.INSTANT_APP_TOKEN";
+
+ /**
* The action that triggered an instant application resolution.
* @hide
*/
+ @SystemApi
public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
/**
- * A {@link Bundle} of metadata that describes the instanta application that needs to be
+ * An array of {@link Bundle}s containing details about resolved instant apps..
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_INSTANT_APP_BUNDLES =
+ "android.intent.extra.INSTANT_APP_BUNDLES";
+
+ /**
+ * A {@link Bundle} of metadata that describes the instant application that needs to be
* installed. This data is populated from the response to
* {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered
* instant application resolver.
* @hide
*/
+ @SystemApi
public static final String EXTRA_INSTANT_APP_EXTRAS =
"android.intent.extra.INSTANT_APP_EXTRAS";
/**
+ * A boolean value indicating that the instant app resolver was unable to state with certainty
+ * that it did or did not have an app for the sanitized {@link Intent} defined at
+ * {@link #EXTRA_INTENT}.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_UNKNOWN_INSTANT_APP =
+ "android.intent.extra.UNKNOWN_INSTANT_APP";
+
+ /**
* The version code of the app to install components from.
* @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
* @hide
@@ -4493,12 +4528,14 @@ public class Intent implements Parcelable, Cloneable {
* The version code of the app to install components from.
* @hide
*/
+ @SystemApi
public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
/**
- * The app that triggered the ephemeral installation.
+ * The app that triggered the instant app installation.
* @hide
*/
+ @SystemApi
public static final String EXTRA_CALLING_PACKAGE
= "android.intent.extra.CALLING_PACKAGE";
@@ -4507,6 +4544,7 @@ public class Intent implements Parcelable, Cloneable {
* installer may use.
* @hide
*/
+ @SystemApi
public static final String EXTRA_VERIFICATION_BUNDLE
= "android.intent.extra.VERIFICATION_BUNDLE";
@@ -5029,6 +5067,7 @@ public class Intent implements Parcelable, Cloneable {
FLAG_GRANT_PREFIX_URI_PERMISSION,
FLAG_DEBUG_TRIAGED_MISSING,
FLAG_IGNORE_EPHEMERAL,
+ FLAG_ACTIVITY_MATCH_EXTERNAL,
FLAG_ACTIVITY_NO_HISTORY,
FLAG_ACTIVITY_SINGLE_TOP,
FLAG_ACTIVITY_NEW_TASK,
@@ -5072,6 +5111,7 @@ public class Intent implements Parcelable, Cloneable {
FLAG_INCLUDE_STOPPED_PACKAGES,
FLAG_DEBUG_TRIAGED_MISSING,
FLAG_IGNORE_EPHEMERAL,
+ FLAG_ACTIVITY_MATCH_EXTERNAL,
FLAG_ACTIVITY_NO_HISTORY,
FLAG_ACTIVITY_SINGLE_TOP,
FLAG_ACTIVITY_NEW_TASK,
@@ -5475,6 +5515,14 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000;
+
+ /**
+ * If set, resolution of this intent may take place via an instant app not
+ * yet on the device if there does not yet exist an app on device to
+ * resolve it.
+ */
+ public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800;
+
/**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
@@ -10028,6 +10076,25 @@ public class Intent implements Parcelable, Cloneable {
}
}
+ /** @hide */
+ public boolean hasWebURI() {
+ if (getData() == null) {
+ return false;
+ }
+ final String scheme = getScheme();
+ if (TextUtils.isEmpty(scheme)) {
+ return false;
+ }
+ return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
+ }
+
+ /** @hide */
+ public boolean isBrowsableWebIntent() {
+ return ACTION_VIEW.equals(mAction)
+ && hasCategory(CATEGORY_BROWSABLE)
+ && hasWebURI();
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index f6697e8148a0..b61a6d997b14 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -958,6 +958,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* Version of the sandbox the application wants to run in.
* @hide
*/
+ @SystemApi
public int targetSandboxVersion;
/**
@@ -1600,7 +1601,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* @hide
*/
public boolean isAllowedToUseHiddenApi() {
- return isSystemApp();
+ return false;
}
/**
@@ -1655,7 +1656,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
}
- /** @hide */
+ /**
+ * True if the application is installed as an instant app.
+ * @hide
+ */
+ @SystemApi
public boolean isInstantApp() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
}
diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java
index 6bdcefbe974e..202df50dda6f 100644
--- a/core/java/android/content/pm/AuxiliaryResolveInfo.java
+++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java
@@ -21,6 +21,10 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
/**
* Auxiliary application resolution response.
@@ -31,56 +35,95 @@ import android.content.IntentFilter;
* hasn't been installed.
* @hide
*/
-public final class AuxiliaryResolveInfo extends IntentFilter {
- /** Resolved information returned from the external instant resolver */
- public final InstantAppResolveInfo resolveInfo;
- /** The resolved package. Copied from {@link #resolveInfo}. */
- public final String packageName;
+public final class AuxiliaryResolveInfo {
/** The activity to launch if there's an installation failure. */
public final ComponentName installFailureActivity;
- /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
- public final String splitName;
/** Whether or not instant resolution needs the second phase */
public final boolean needsPhaseTwo;
/** Opaque token to track the instant application resolution */
public final String token;
- /** The version code of the package */
- public final long versionCode;
/** An intent to start upon failure to install */
public final Intent failureIntent;
+ /** The matching filters for this resolve info. */
+ public final List<AuxiliaryFilter> filters;
/** Create a response for installing an instant application. */
- public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo,
- @NonNull IntentFilter orig,
- @Nullable String splitName,
- @NonNull String token,
+ public AuxiliaryResolveInfo(@NonNull String token,
boolean needsPhase2,
- @Nullable Intent failureIntent) {
- super(orig);
- this.resolveInfo = resolveInfo;
- this.packageName = resolveInfo.getPackageName();
- this.splitName = splitName;
+ @Nullable Intent failureIntent,
+ @Nullable List<AuxiliaryFilter> filters) {
this.token = token;
this.needsPhaseTwo = needsPhase2;
- this.versionCode = resolveInfo.getVersionCode();
this.failureIntent = failureIntent;
+ this.filters = filters;
this.installFailureActivity = null;
}
/** Create a response for installing a split on demand. */
- public AuxiliaryResolveInfo(@NonNull String packageName,
- @Nullable String splitName,
- @Nullable ComponentName failureActivity,
- long versionCode,
- @Nullable Intent failureIntent) {
+ public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+ @Nullable Intent failureIntent,
+ @Nullable List<AuxiliaryFilter> filters) {
super();
- this.packageName = packageName;
this.installFailureActivity = failureActivity;
- this.splitName = splitName;
- this.versionCode = versionCode;
- this.resolveInfo = null;
+ this.filters = filters;
this.token = null;
this.needsPhaseTwo = false;
this.failureIntent = failureIntent;
}
+
+ /** Create a response for installing a split on demand. */
+ public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+ String packageName, long versionCode, String splitName) {
+ this(failureActivity, null, Collections.singletonList(
+ new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName)));
+ }
+
+ /** @hide */
+ public static final class AuxiliaryFilter extends IntentFilter {
+ /** Resolved information returned from the external instant resolver */
+ public final InstantAppResolveInfo resolveInfo;
+ /** The resolved package. Copied from {@link #resolveInfo}. */
+ public final String packageName;
+ /** The version code of the package */
+ public final long versionCode;
+ /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+ public final String splitName;
+ /** The extras to pass on to the installer for this filter. */
+ public final Bundle extras;
+
+ public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo,
+ String splitName, Bundle extras) {
+ super(orig);
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.versionCode = resolveInfo.getLongVersionCode();
+ this.splitName = splitName;
+ this.extras = extras;
+ }
+
+ public AuxiliaryFilter(InstantAppResolveInfo resolveInfo,
+ String splitName, Bundle extras) {
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.versionCode = resolveInfo.getLongVersionCode();
+ this.splitName = splitName;
+ this.extras = extras;
+ }
+
+ public AuxiliaryFilter(String packageName, long versionCode, String splitName) {
+ this.resolveInfo = null;
+ this.packageName = packageName;
+ this.versionCode = versionCode;
+ this.splitName = splitName;
+ this.extras = null;
+ }
+
+ @Override
+ public String toString() {
+ return "AuxiliaryFilter{"
+ + "packageName='" + packageName + '\''
+ + ", versionCode=" + versionCode
+ + ", splitName='" + splitName + '\'' + '}';
+ }
+ }
} \ No newline at end of file
diff --git a/core/java/android/content/pm/EphemeralIntentFilter.java b/core/java/android/content/pm/EphemeralIntentFilter.java
deleted file mode 100644
index 1dbbf816ed93..000000000000
--- a/core/java/android/content/pm/EphemeralIntentFilter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information about an ephemeral application intent filter.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralIntentFilter implements Parcelable {
- private final InstantAppIntentFilter mInstantAppIntentFilter;
-
- public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
- mInstantAppIntentFilter = new InstantAppIntentFilter(splitName, filters);
- }
-
- EphemeralIntentFilter(@NonNull InstantAppIntentFilter intentFilter) {
- mInstantAppIntentFilter = intentFilter;
- }
-
- EphemeralIntentFilter(Parcel in) {
- mInstantAppIntentFilter = in.readParcelable(null /*loader*/);
- }
-
- public String getSplitName() {
- return mInstantAppIntentFilter.getSplitName();
- }
-
- public List<IntentFilter> getFilters() {
- return mInstantAppIntentFilter.getFilters();
- }
-
- /** @hide */
- InstantAppIntentFilter getInstantAppIntentFilter() {
- return mInstantAppIntentFilter;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppIntentFilter, flags);
- }
-
- public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR
- = new Parcelable.Creator<EphemeralIntentFilter>() {
- @Override
- public EphemeralIntentFilter createFromParcel(Parcel in) {
- return new EphemeralIntentFilter(in);
- }
- @Override
- public EphemeralIntentFilter[] newArray(int size) {
- return new EphemeralIntentFilter[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
deleted file mode 100644
index 12131a3ebc98..000000000000
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Information about an ephemeral application.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralResolveInfo implements Parcelable {
- /** Algorithm that will be used to generate the domain digest */
- public static final String SHA_ALGORITHM = "SHA-256";
-
- private final InstantAppResolveInfo mInstantAppResolveInfo;
- @Deprecated
- private final List<IntentFilter> mLegacyFilters;
-
- @Deprecated
- public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
- @NonNull List<IntentFilter> filters) {
- if (uri == null || packageName == null || filters == null || filters.isEmpty()) {
- throw new IllegalArgumentException();
- }
- final List<EphemeralIntentFilter> ephemeralFilters = new ArrayList<>(1);
- ephemeralFilters.add(new EphemeralIntentFilter(packageName, filters));
- mInstantAppResolveInfo = new InstantAppResolveInfo(uri.getHost(), packageName,
- createInstantAppIntentFilterList(ephemeralFilters));
- mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
- mLegacyFilters.addAll(filters);
- }
-
- @Deprecated
- public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters) {
- this(digest, packageName, filters, -1 /*versionCode*/);
- }
-
- public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters, int versionCode) {
- mInstantAppResolveInfo = new InstantAppResolveInfo(
- digest.getInstantAppDigest(), packageName,
- createInstantAppIntentFilterList(filters), versionCode);
- mLegacyFilters = null;
- }
-
- public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters) {
- this(new EphemeralDigest(hostName), packageName, filters);
- }
-
- EphemeralResolveInfo(Parcel in) {
- mInstantAppResolveInfo = in.readParcelable(null /*loader*/);
- mLegacyFilters = new ArrayList<IntentFilter>();
- in.readList(mLegacyFilters, null /*loader*/);
- }
-
- /** @hide */
- public InstantAppResolveInfo getInstantAppResolveInfo() {
- return mInstantAppResolveInfo;
- }
-
- private static List<InstantAppIntentFilter> createInstantAppIntentFilterList(
- List<EphemeralIntentFilter> filters) {
- if (filters == null) {
- return null;
- }
- final int filterCount = filters.size();
- final List<InstantAppIntentFilter> returnList = new ArrayList<>(filterCount);
- for (int i = 0; i < filterCount; i++) {
- returnList.add(filters.get(i).getInstantAppIntentFilter());
- }
- return returnList;
- }
-
- public byte[] getDigestBytes() {
- return mInstantAppResolveInfo.getDigestBytes();
- }
-
- public int getDigestPrefix() {
- return mInstantAppResolveInfo.getDigestPrefix();
- }
-
- public String getPackageName() {
- return mInstantAppResolveInfo.getPackageName();
- }
-
- public List<EphemeralIntentFilter> getIntentFilters() {
- final List<InstantAppIntentFilter> filters = mInstantAppResolveInfo.getIntentFilters();
- final int filterCount = filters.size();
- final List<EphemeralIntentFilter> returnList = new ArrayList<>(filterCount);
- for (int i = 0; i < filterCount; i++) {
- returnList.add(new EphemeralIntentFilter(filters.get(i)));
- }
- return returnList;
- }
-
- public int getVersionCode() {
- return mInstantAppResolveInfo.getVersionCode();
- }
-
- @Deprecated
- public List<IntentFilter> getFilters() {
- return mLegacyFilters;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppResolveInfo, flags);
- out.writeList(mLegacyFilters);
- }
-
- public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
- = new Parcelable.Creator<EphemeralResolveInfo>() {
- @Override
- public EphemeralResolveInfo createFromParcel(Parcel in) {
- return new EphemeralResolveInfo(in);
- }
- @Override
- public EphemeralResolveInfo[] newArray(int size) {
- return new EphemeralResolveInfo[size];
- }
- };
-
- /**
- * Helper class to generate and store each of the digests and prefixes
- * sent to the Ephemeral Resolver.
- * <p>
- * Since intent filters may want to handle multiple hosts within a
- * domain [eg “*.google.com”], the resolver is presented with multiple
- * hash prefixes. For example, "a.b.c.d.e" generates digests for
- * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
- *
- * @hide
- */
- @SystemApi
- public static final class EphemeralDigest implements Parcelable {
- private final InstantAppDigest mInstantAppDigest;
-
- public EphemeralDigest(@NonNull String hostName) {
- this(hostName, -1 /*maxDigests*/);
- }
-
- /** @hide */
- public EphemeralDigest(@NonNull String hostName, int maxDigests) {
- mInstantAppDigest = new InstantAppDigest(hostName, maxDigests);
- }
-
- EphemeralDigest(Parcel in) {
- mInstantAppDigest = in.readParcelable(null /*loader*/);
- }
-
- /** @hide */
- InstantAppDigest getInstantAppDigest() {
- return mInstantAppDigest;
- }
-
- public byte[][] getDigestBytes() {
- return mInstantAppDigest.getDigestBytes();
- }
-
- public int[] getDigestPrefix() {
- return mInstantAppDigest.getDigestPrefix();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppDigest, flags);
- }
-
- @SuppressWarnings("hiding")
- public static final Parcelable.Creator<EphemeralDigest> CREATOR =
- new Parcelable.Creator<EphemeralDigest>() {
- @Override
- public EphemeralDigest createFromParcel(Parcel in) {
- return new EphemeralDigest(in);
- }
- @Override
- public EphemeralDigest[] newArray(int size) {
- return new EphemeralDigest[size];
- }
- };
- }
-}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 0b16852246f8..8fddb99b35a8 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -26,9 +26,12 @@ interface IPackageInstallerSession {
void addClientProgress(float progress);
String[] getNames();
+
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
ParcelFileDescriptor openRead(String name);
+ void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
+
void removeSplit(String splitName);
void close();
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 19cb9323ba93..112c5dae6731 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -19,6 +19,7 @@ package android.content.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,11 +27,35 @@ import android.os.Parcelable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
/**
- * Information about an instant application.
+ * Describes an externally resolvable instant application. There are three states that this class
+ * can represent: <p/>
+ * <ul>
+ * <li>
+ * The first, usable only for non http/s intents, implies that the resolver cannot
+ * immediately resolve this intent and would prefer that resolution be deferred to the
+ * instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}.
+ * If the {@link android.content.Intent} has the scheme set to http/s and a set of digest
+ * prefixes were passed into one of the resolve methods in
+ * {@link android.app.InstantAppResolverService}, this state cannot be used.
+ * </li>
+ * <li>
+ * The second represents a partial match and is constructed with any of the other
+ * constructors. By setting one or more of the {@link Nullable}arguments to null, you
+ * communicate to the resolver in response to
+ * {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[],
+ * String, InstantAppResolverService.InstantAppResolutionCallback)}
+ * that you need a 2nd round of resolution to complete the request.
+ * </li>
+ * <li>
+ * The third represents a complete match and is constructed with all @Nullable parameters
+ * populated.
+ * </li>
+ * </ul>
* @hide
*/
@SystemApi
@@ -38,6 +63,8 @@ public final class InstantAppResolveInfo implements Parcelable {
/** Algorithm that will be used to generate the domain digest */
private static final String SHA_ALGORITHM = "SHA-256";
+ private static final byte[] EMPTY_DIGEST = new byte[0];
+
private final InstantAppDigest mDigest;
private final String mPackageName;
/** The filters used to match domain */
@@ -46,15 +73,30 @@ public final class InstantAppResolveInfo implements Parcelable {
private final long mVersionCode;
/** Data about the app that should be passed along to the Instant App installer on resolve */
private final Bundle mExtras;
+ /**
+ * A flag that indicates that the resolver is aware that an app may match, but would prefer
+ * that the installer get the sanitized intent to decide. This should not be used for
+ * resolutions that include a host and will be ignored in such cases.
+ */
+ private final boolean mShouldLetInstallerDecide;
+ /** Constructor for intent-based InstantApp resolution results. */
public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
@Nullable List<InstantAppIntentFilter> filters, int versionCode) {
this(digest, packageName, filters, (long) versionCode, null /* extras */);
}
+ /** Constructor for intent-based InstantApp resolution results with extras. */
public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
@Nullable List<InstantAppIntentFilter> filters, long versionCode,
@Nullable Bundle extras) {
+ this(digest, packageName, filters, versionCode, extras, false);
+ }
+
+ /** Constructor for intent-based InstantApp resolution results with extras. */
+ private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+ @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+ @Nullable Bundle extras, boolean shouldLetInstallerDecide) {
// validate arguments
if ((packageName == null && (filters != null && filters.size() != 0))
|| (packageName != null && (filters == null || filters.size() == 0))) {
@@ -62,7 +104,7 @@ public final class InstantAppResolveInfo implements Parcelable {
}
mDigest = digest;
if (filters != null) {
- mFilters = new ArrayList<InstantAppIntentFilter>(filters.size());
+ mFilters = new ArrayList<>(filters.size());
mFilters.addAll(filters);
} else {
mFilters = null;
@@ -70,25 +112,48 @@ public final class InstantAppResolveInfo implements Parcelable {
mPackageName = packageName;
mVersionCode = versionCode;
mExtras = extras;
+ mShouldLetInstallerDecide = shouldLetInstallerDecide;
}
+ /** Constructor for intent-based InstantApp resolution results by hostname. */
public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
@Nullable List<InstantAppIntentFilter> filters) {
this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
null /* extras */);
}
+ /**
+ * Constructor that creates a "let the installer decide" response with optional included
+ * extras.
+ */
+ public InstantAppResolveInfo(@Nullable Bundle extras) {
+ this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
+ }
+
InstantAppResolveInfo(Parcel in) {
- mDigest = in.readParcelable(null /*loader*/);
- mPackageName = in.readString();
- mFilters = new ArrayList<InstantAppIntentFilter>();
- in.readList(mFilters, null /*loader*/);
- mVersionCode = in.readLong();
+ mShouldLetInstallerDecide = in.readBoolean();
mExtras = in.readBundle();
+ if (mShouldLetInstallerDecide) {
+ mDigest = InstantAppDigest.UNDEFINED;
+ mPackageName = null;
+ mFilters = Collections.emptyList();
+ mVersionCode = -1;
+ } else {
+ mDigest = in.readParcelable(null /*loader*/);
+ mPackageName = in.readString();
+ mFilters = new ArrayList<>();
+ in.readList(mFilters, null /*loader*/);
+ mVersionCode = in.readLong();
+ }
+ }
+
+ /** Returns true if the installer should be notified that it should query for packages. */
+ public boolean shouldLetInstallerDecide() {
+ return mShouldLetInstallerDecide;
}
public byte[] getDigestBytes() {
- return mDigest.getDigestBytes()[0];
+ return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST;
}
public int getDigestPrefix() {
@@ -127,11 +192,15 @@ public final class InstantAppResolveInfo implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
+ out.writeBoolean(mShouldLetInstallerDecide);
+ out.writeBundle(mExtras);
+ if (mShouldLetInstallerDecide) {
+ return;
+ }
out.writeParcelable(mDigest, flags);
out.writeString(mPackageName);
out.writeList(mFilters);
out.writeLong(mVersionCode);
- out.writeBundle(mExtras);
}
public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
@@ -159,7 +228,9 @@ public final class InstantAppResolveInfo implements Parcelable {
@SystemApi
public static final class InstantAppDigest implements Parcelable {
private static final int DIGEST_MASK = 0xfffff000;
- private static final int DIGEST_PREFIX_COUNT = 5;
+
+ public static final InstantAppDigest UNDEFINED =
+ new InstantAppDigest(new byte[][]{}, new int[]{});
/** Full digest of the domain hashes */
private final byte[][] mDigestBytes;
/** The first 4 bytes of the domain hashes */
@@ -186,6 +257,11 @@ public final class InstantAppResolveInfo implements Parcelable {
}
}
+ private InstantAppDigest(byte[][] digestBytes, int[] prefix) {
+ this.mDigestPrefix = prefix;
+ this.mDigestBytes = digestBytes;
+ }
+
private static byte[][] generateDigest(String hostName, int maxDigests) {
ArrayList<byte[]> digests = new ArrayList<>();
try {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index df677d208d36..d0be6c854020 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -829,7 +829,19 @@ public class PackageInstaller {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ }
+ /** {@hide} */
+ public void write(@NonNull String name, long offsetBytes, long lengthBytes,
+ @NonNull ParcelFileDescriptor fd) throws IOException {
+ try {
+ mSession.write(name, offsetBytes, lengthBytes, fd);
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
new file mode 100644
index 000000000000..c811999ce304
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.CancellationSignal;
+import android.os.Parcelable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This is the common interface that all biometric authentication classes should implement.
+ * @hide
+ */
+public interface BiometricAuthenticator {
+
+ /**
+ * Container for biometric data
+ * @hide
+ */
+ abstract class BiometricIdentifier implements Parcelable {}
+
+ /**
+ * Container for callback data from {@link BiometricAuthenticator#authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} and
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ class AuthenticationResult {
+ private BiometricIdentifier mIdentifier;
+ private CryptoObject mCryptoObject;
+ private int mUserId;
+
+ /**
+ * @hide
+ */
+ public AuthenticationResult() { }
+
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ mCryptoObject = crypto;
+ mIdentifier = identifier;
+ mUserId = userId;
+ }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return mCryptoObject;
+ }
+
+ /**
+ * Obtain the biometric identifier associated with this operation. Applications are strongly
+ * discouraged from associating specific identifiers with specific applications or
+ * operations.
+ * @hide
+ */
+ public BiometricIdentifier getId() {
+ return mIdentifier;
+ }
+
+ /**
+ * Obtain the userId for which this biometric was authenticated.
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+ };
+
+ /**
+ * Callback structure provided to {@link BiometricAuthenticator#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}. Users must provide
+ * an implementation of this for listening to biometric events.
+ */
+ abstract class AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ public void onAuthenticationAcquired(int acquireInfo) {}
+ };
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called, at which point the crypto object becomes invalid. This
+ * operation can be canceled by using the provided cancel object. The application wil receive
+ * authentication errors through {@link AuthenticationCallback}. Calling
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is occurring will stop
+ * the previous client and start a new authentication. The interrupted client will receive a
+ * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called. This operation can be canceled by using the provided cancel
+ * object. The application wil receive authentication errors through
+ * {@link AuthenticationCallback}. Calling {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} while an existing
+ * authentication attempt is occurring will stop the previous client and start a new
+ * authentication. The interrupted client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
new file mode 100644
index 000000000000..638f525bfb10
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.hardware.fingerprint.FingerprintManager;
+
+/**
+ * Interface containing all of the fingerprint-specific constants.
+ * @hide
+ */
+public interface BiometricFingerprintConstants {
+ //
+ // Error messages from fingerprint hardware during initilization, enrollment, authentication or
+ // removal. Must agree with the list in fingerprint.h
+ //
+
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
+ * platform and sensor-specific, but is generally on the order of 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
+ public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+ /**
+ * The operation was canceled because the fingerprint sensor is unavailable. For example,
+ * this may happen when the user is switched, the device is locked or another pending operation
+ * prevents or disables it.
+ */
+ public static final int FINGERPRINT_ERROR_CANCELED = 5;
+
+ /**
+ * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
+ * provided fingerprint id was incorrect.
+ *
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * These messages are typically reserved for internal operations such as enrollment, but may be
+ * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
+ * expected to show the error message string if they happen, but are advised not to rely on the
+ * message id since they will be device and vendor-specific
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR = 8;
+
+ /**
+ * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
+ * Fingerprint authentication is disabled until the user unlocks with strong authentication
+ * (PIN/Pattern/Password)
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
+
+ /**
+ * The user canceled the operation. Upon receiving this, applications should use alternate
+ * authentication (e.g. a password). The application should also provide the means to return
+ * to fingerprint authentication, such as a "use fingerprint" button.
+ */
+ public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
+
+ /**
+ * The user does not have any fingerprints enrolled.
+ */
+ public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
+
+ /**
+ * The device does not have a fingerprint sensor.
+ */
+ public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
+
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+
+ //
+ // Image acquisition messages. Must agree with those in fingerprint.h
+ //
+
+ /**
+ * The image acquired was good.
+ */
+ public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+
+ /**
+ * Only a partial fingerprint image was detected. During enrollment, the user should be
+ * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
+ */
+ public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+
+ /**
+ * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
+ * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
+ */
+ public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+
+ /**
+ * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
+ * For example, it's reasonable return this after multiple
+ * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
+ * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
+ * when this is returned.
+ */
+ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+
+ /**
+ * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
+ * linear array sensors that require a swipe motion.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+
+ /**
+ * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
+ * linear array sensors, this could also happen if the finger was moved during acquisition.
+ * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
+ * longer.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
new file mode 100644
index 000000000000..496d9c57f252
--- /dev/null
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.security.keystore.AndroidKeyStoreProvider;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * @hide
+ */
+public class CryptoObject {
+ private final Object mCrypto;
+
+ public CryptoObject(@NonNull Signature signature) {
+ mCrypto = signature;
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ mCrypto = cipher;
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ mCrypto = mac;
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ }
+
+ /**
+ * @hide
+ * @return the opId associated with this object or 0 if none
+ */
+ public final long getOpId() {
+ return mCrypto != null
+ ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+ }
+};
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl
new file mode 100644
index 000000000000..9070777bab63
--- /dev/null
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+parcelable AmbientBrightnessDayStats;
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
new file mode 100644
index 000000000000..00f3c36d0361
--- /dev/null
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+/**
+ * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
+ * {@see DisplayManager.getAmbientBrightnessStats()}
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class AmbientBrightnessDayStats implements Parcelable {
+
+ /** The localdate for which brightness stats are being tracked */
+ private final LocalDate mLocalDate;
+
+ /** Ambient brightness values for creating bucket boundaries from */
+ private final float[] mBucketBoundaries;
+
+ /** Stats of how much time (in seconds) was spent in each of the buckets */
+ private final float[] mStats;
+
+ /**
+ * @hide
+ */
+ public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+ @NonNull float[] bucketBoundaries) {
+ this(localDate, bucketBoundaries, null);
+ }
+
+ /**
+ * @hide
+ */
+ public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+ @NonNull float[] bucketBoundaries, float[] stats) {
+ Preconditions.checkNotNull(localDate);
+ Preconditions.checkNotNull(bucketBoundaries);
+ Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
+ "bucketBoundaries");
+ if (bucketBoundaries.length < 1) {
+ throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
+ }
+ checkSorted(bucketBoundaries);
+ if (stats == null) {
+ stats = new float[bucketBoundaries.length];
+ } else {
+ Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
+ if (bucketBoundaries.length != stats.length) {
+ throw new IllegalArgumentException(
+ "Bucket boundaries and stats must be of same size.");
+ }
+ }
+ mLocalDate = localDate;
+ mBucketBoundaries = bucketBoundaries;
+ mStats = stats;
+ }
+
+ public LocalDate getLocalDate() {
+ return mLocalDate;
+ }
+
+ public float[] getStats() {
+ return mStats;
+ }
+
+ public float[] getBucketBoundaries() {
+ return mBucketBoundaries;
+ }
+
+ private AmbientBrightnessDayStats(Parcel source) {
+ mLocalDate = LocalDate.parse(source.readString());
+ mBucketBoundaries = source.createFloatArray();
+ mStats = source.createFloatArray();
+ }
+
+ public static final Creator<AmbientBrightnessDayStats> CREATOR =
+ new Creator<AmbientBrightnessDayStats>() {
+
+ @Override
+ public AmbientBrightnessDayStats createFromParcel(Parcel source) {
+ return new AmbientBrightnessDayStats(source);
+ }
+
+ @Override
+ public AmbientBrightnessDayStats[] newArray(int size) {
+ return new AmbientBrightnessDayStats[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
+ return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
+ other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = result * prime + mLocalDate.hashCode();
+ result = result * prime + Arrays.hashCode(mBucketBoundaries);
+ result = result * prime + Arrays.hashCode(mStats);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bucketBoundariesString = new StringBuilder();
+ StringBuilder statsString = new StringBuilder();
+ for (int i = 0; i < mBucketBoundaries.length; i++) {
+ if (i != 0) {
+ bucketBoundariesString.append(", ");
+ statsString.append(", ");
+ }
+ bucketBoundariesString.append(mBucketBoundaries[i]);
+ statsString.append(mStats[i]);
+ }
+ return new StringBuilder()
+ .append(mLocalDate).append(" ")
+ .append("{").append(bucketBoundariesString).append("} ")
+ .append("{").append(statsString).append("}").toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mLocalDate.toString());
+ dest.writeFloatArray(mBucketBoundaries);
+ dest.writeFloatArray(mStats);
+ }
+
+ /** @hide */
+ public void log(float ambientBrightness, float durationSec) {
+ int bucketIndex = getBucketIndex(ambientBrightness);
+ if (bucketIndex >= 0) {
+ mStats[bucketIndex] += durationSec;
+ }
+ }
+
+ private int getBucketIndex(float ambientBrightness) {
+ if (ambientBrightness < mBucketBoundaries[0]) {
+ return -1;
+ }
+ int low = 0;
+ int high = mBucketBoundaries.length - 1;
+ while (low < high) {
+ int mid = (low + high) / 2;
+ if (mBucketBoundaries[mid] <= ambientBrightness
+ && ambientBrightness < mBucketBoundaries[mid + 1]) {
+ return mid;
+ } else if (mBucketBoundaries[mid] < ambientBrightness) {
+ low = mid + 1;
+ } else if (mBucketBoundaries[mid] > ambientBrightness) {
+ high = mid - 1;
+ }
+ }
+ return low;
+ }
+
+ private static void checkSorted(float[] values) {
+ if (values.length <= 1) {
+ return;
+ }
+ float prevValue = values[0];
+ for (int i = 1; i < values.length; i++) {
+ Preconditions.checkState(prevValue < values[i]);
+ prevValue = values[i];
+ }
+ return;
+ }
+}
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 2301824cb9dc..02eb28ceb4b9 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -54,19 +54,30 @@ public final class BrightnessChangeEvent implements Parcelable {
/** Most recent battery level when brightness was changed or Float.NaN */
public final float batteryLevel;
+ /** Factor applied to brightness due to battery level, 0.0-1.0 */
+ public final float powerBrightnessFactor;
+
/** Color filter active to provide night mode */
public final boolean nightMode;
/** If night mode color filter is active this will be the temperature in kelvin */
public final int colorTemperature;
- /** Brightness le vel before slider adjustment */
+ /** Brightness level before slider adjustment */
public final float lastBrightness;
+ /** Whether brightness configuration is default version */
+ public final boolean isDefaultBrightnessConfig;
+
+ /** Whether brightness curve includes a user brightness point */
+ public final boolean isUserSetBrightness;
+
+
/** @hide */
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
- boolean nightMode, int colorTemperature, float lastBrightness) {
+ float powerBrightnessFactor, boolean nightMode, int colorTemperature,
+ float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
@@ -74,9 +85,12 @@ public final class BrightnessChangeEvent implements Parcelable {
this.luxValues = luxValues;
this.luxTimestamps = luxTimestamps;
this.batteryLevel = batteryLevel;
+ this.powerBrightnessFactor = powerBrightnessFactor;
this.nightMode = nightMode;
this.colorTemperature = colorTemperature;
this.lastBrightness = lastBrightness;
+ this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ this.isUserSetBrightness = isUserSetBrightness;
}
/** @hide */
@@ -88,9 +102,12 @@ public final class BrightnessChangeEvent implements Parcelable {
this.luxValues = other.luxValues;
this.luxTimestamps = other.luxTimestamps;
this.batteryLevel = other.batteryLevel;
+ this.powerBrightnessFactor = other.powerBrightnessFactor;
this.nightMode = other.nightMode;
this.colorTemperature = other.colorTemperature;
this.lastBrightness = other.lastBrightness;
+ this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
+ this.isUserSetBrightness = other.isUserSetBrightness;
}
private BrightnessChangeEvent(Parcel source) {
@@ -101,9 +118,12 @@ public final class BrightnessChangeEvent implements Parcelable {
luxValues = source.createFloatArray();
luxTimestamps = source.createLongArray();
batteryLevel = source.readFloat();
+ powerBrightnessFactor = source.readFloat();
nightMode = source.readBoolean();
colorTemperature = source.readInt();
lastBrightness = source.readFloat();
+ isDefaultBrightnessConfig = source.readBoolean();
+ isUserSetBrightness = source.readBoolean();
}
public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -130,9 +150,12 @@ public final class BrightnessChangeEvent implements Parcelable {
dest.writeFloatArray(luxValues);
dest.writeLongArray(luxTimestamps);
dest.writeFloat(batteryLevel);
+ dest.writeFloat(powerBrightnessFactor);
dest.writeBoolean(nightMode);
dest.writeInt(colorTemperature);
dest.writeFloat(lastBrightness);
+ dest.writeBoolean(isDefaultBrightnessConfig);
+ dest.writeBoolean(isUserSetBrightness);
}
/** @hide */
@@ -144,9 +167,12 @@ public final class BrightnessChangeEvent implements Parcelable {
private float[] mLuxValues;
private long[] mLuxTimestamps;
private float mBatteryLevel;
+ private float mPowerBrightnessFactor;
private boolean mNightMode;
private int mColorTemperature;
private float mLastBrightness;
+ private boolean mIsDefaultBrightnessConfig;
+ private boolean mIsUserSetBrightness;
/** {@see BrightnessChangeEvent#brightness} */
public Builder setBrightness(float brightness) {
@@ -190,6 +216,12 @@ public final class BrightnessChangeEvent implements Parcelable {
return this;
}
+ /** {@see BrightnessChangeEvent#powerSaveBrightness} */
+ public Builder setPowerBrightnessFactor(float powerBrightnessFactor) {
+ mPowerBrightnessFactor = powerBrightnessFactor;
+ return this;
+ }
+
/** {@see BrightnessChangeEvent#nightMode} */
public Builder setNightMode(boolean nightMode) {
mNightMode = nightMode;
@@ -208,11 +240,24 @@ public final class BrightnessChangeEvent implements Parcelable {
return this;
}
+ /** {@see BrightnessChangeEvent#isDefaultBrightnessConfig} */
+ public Builder setIsDefaultBrightnessConfig(boolean isDefaultBrightnessConfig) {
+ mIsDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#userBrightnessPoint} */
+ public Builder setUserBrightnessPoint(boolean isUserSetBrightness) {
+ mIsUserSetBrightness = isUserSetBrightness;
+ return this;
+ }
+
/** Builds a BrightnessChangeEvent */
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
- mNightMode, mColorTemperature, mLastBrightness);
+ mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+ mIsDefaultBrightnessConfig, mIsUserSetBrightness);
}
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4de4880b7c17..22fb8e75289a 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -631,6 +631,16 @@ public final class DisplayManager {
}
/**
+ * Fetch {@link AmbientBrightnessDayStats}s.
+ *
+ * @hide until we make it a system api
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
+ public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ return mGlobal.getAmbientBrightnessStats();
+ }
+
+ /**
* Sets the global display brightness configuration.
*
* @hide
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 2d5f5e041486..d7f7c865b8fb 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -525,6 +525,21 @@ public final class DisplayManagerGlobal {
}
}
+ /**
+ * Retrieves ambient brightness stats.
+ */
+ public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ try {
+ ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
+ if (stats == null) {
+ return Collections.emptyList();
+ }
+ return stats.getList();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 1cfad4f0168f..f468942cc951 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -174,9 +174,9 @@ public abstract class DisplayManagerInternal {
public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
/**
- * Persist brightness slider events.
+ * Persist brightness slider events and ambient brightness stats.
*/
- public abstract void persistBrightnessSliderEvents();
+ public abstract void persistBrightnessTrackerState();
/**
* Notifies the display manager that resource overlays have changed.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 13599cfa0b7d..0571ae1fe825 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -87,6 +87,9 @@ interface IDisplayManager {
// Requires BRIGHTNESS_SLIDER_USAGE permission.
ParceledListSlice getBrightnessEvents(String callingPackage);
+ // Requires ACCESS_AMBIENT_LIGHT_STATS permission.
+ ParceledListSlice getAmbientBrightnessStats();
+
// Sets the global brightness configuration for a given user. Requires
// CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not
// the same as the calling user.
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index c30763475fa8..c7ce8fad2543 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -15,6 +15,7 @@
*/
package android.hardware.fingerprint;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -22,7 +23,7 @@ import android.os.Parcelable;
* Container for fingerprint metadata.
* @hide
*/
-public final class Fingerprint implements Parcelable {
+public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier {
private CharSequence mName;
private int mGroupId;
private int mFingerId;
diff --git a/core/java/android/hardware/fingerprint/FingerprintDialog.java b/core/java/android/hardware/fingerprint/FingerprintDialog.java
index 6b7fab773b43..49835963a3b1 100644
--- a/core/java/android/hardware/fingerprint/FingerprintDialog.java
+++ b/core/java/android/hardware/fingerprint/FingerprintDialog.java
@@ -23,19 +23,25 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.DialogInterface;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.CryptoObject;
import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.text.TextUtils;
+import java.security.Signature;
import java.util.concurrent.Executor;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
/**
* A class that manages a system-provided fingerprint dialog.
*/
-public class FingerprintDialog {
+public class FingerprintDialog implements BiometricAuthenticator, BiometricFingerprintConstants {
/**
* @hide
@@ -200,6 +206,7 @@ public class FingerprintDialog {
}
}
+ private PackageManager mPackageManager;
private FingerprintManager mFingerprintManager;
private Bundle mBundle;
private ButtonInfo mPositiveButtonInfo;
@@ -227,37 +234,209 @@ public class FingerprintDialog {
mPositiveButtonInfo = positiveButtonInfo;
mNegativeButtonInfo = negativeButtonInfo;
mFingerprintManager = context.getSystemService(FingerprintManager.class);
+ mPackageManager = context.getPackageManager();
+ }
+
+ /**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ */
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
+ public CryptoObject(@NonNull Signature signature) {
+ super(signature);
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ super(cipher);
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ super(mac);
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return super.getSignature();
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return super.getCipher();
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return super.getMac();
+ }
+ }
+
+ /**
+ * Container for callback data from {@link #authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} and
+ * {@link #authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ super(crypto, identifier, userId);
+ }
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link #authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return (CryptoObject) super.getCryptoObject();
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link FingerprintDialog#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link FingerprintDialog#authenticate(CryptoObject,
+ * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
+ * of this for listening to authentication events.
+ */
+ public static abstract class AuthenticationCallback extends
+ BiometricAuthenticator.AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ @Override
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @param result An object containing authentication-related data
+ * @hide
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ result.getId(),
+ result.getUserId()));
+ }
+ }
+
+
+ /**
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
}
/**
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(cancel, executor, (AuthenticationCallback) callback);
+ }
+
+
+ /**
* This call warms up the fingerprint hardware, displays a system-provided dialog,
* and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog, at which point the crypto object becomes invalid.
- * This operation can be canceled by using the provided cancel object. The application will
- * receive authentication errors through {@link AuthenticationCallback}, and button events
- * through the corresponding callback set in
- * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
- * It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)}, or when the user dismisses the system-provided dialog, at which point
+ * the crypto object becomes invalid. This operation can be canceled by using the provided
+ * cancel object. The application will receive authentication errors through
+ * {@link AuthenticationCallback}, and button events through the
+ * corresponding callback set in {@link Builder#setNegativeButton(CharSequence,
+ * Executor, DialogInterface.OnClickListener)}. It is safe to reuse the
+ * {@link FingerprintDialog} object, and calling {@link FingerprintDialog#authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} while an
+ * existing authentication attempt is occurring will stop the previous client and start a
+ * new authentication. The interrupted client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
*
- * @throws IllegalArgumentException if any of the arguments are null
+ * @throws IllegalArgumentException If any of the arguments are null
*
- * @param crypto object associated with the call
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
*/
@RequiresPermission(USE_FINGERPRINT)
- public void authenticate(@NonNull FingerprintManager.CryptoObject crypto,
+ public void authenticate(@NonNull CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
callback);
}
@@ -265,29 +444,57 @@ public class FingerprintDialog {
/**
* This call warms up the fingerprint hardware, displays a system-provided dialog,
* and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog. This operation can be canceled by using the provided
- * cancel object. The application will receive authentication errors through
- * {@link AuthenticationCallback}, and button events through the corresponding callback set in
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called, or when the user dismisses the system-provided dialog.
+ * This operation can be canceled by using the provided cancel object. The application will
+ * receive authentication errors through {@link AuthenticationCallback},
+ * and button events through the corresponding callback set in
* {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
* It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ * {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is
+ * occurring will stop the previous client and start a new authentication. The interrupted
+ * client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
*
- * @throws IllegalArgumentException if any of the arguments are null
+ * @throws IllegalArgumentException If any of the arguments are null
*
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
*/
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
}
+
+ private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
+ Executor executor) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ sendError(FINGERPRINT_ERROR_HW_NOT_PRESENT, callback, executor);
+ return true;
+ } else if (!mFingerprintManager.isHardwareDetected()) {
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, callback, executor);
+ return true;
+ } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+ sendError(FINGERPRINT_ERROR_NO_FINGERPRINTS, callback, executor);
+ return true;
+ }
+ return false;
+ }
+
+ private void sendError(int error, AuthenticationCallback callback, Executor executor) {
+ executor.execute(() -> {
+ callback.onAuthenticationError(error, mFingerprintManager.getErrorString(
+ error, 0 /* vendorCode */));
+ });
+ }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 62d92c4ac385..2dfe673a1c6c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -27,6 +27,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -38,7 +40,6 @@ import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;
@@ -51,9 +52,14 @@ import javax.crypto.Mac;
/**
* A class that coordinates access to the fingerprint hardware.
+ * @deprecated See {@link FingerprintDialog} which shows a system-provided dialog upon starting
+ * authentication. In a world where devices may have in-display fingerprint sensors, it's much
+ * more realistic to have a system-provided authentication dialog since the in-display sensor
+ * location may vary by vendor/device.
*/
+@Deprecated
@SystemService(Context.FINGERPRINT_SERVICE)
-public class FingerprintManager {
+public class FingerprintManager implements BiometricFingerprintConstants {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
@@ -64,147 +70,14 @@ public class FingerprintManager {
private static final int MSG_REMOVED = 105;
private static final int MSG_ENUMERATED = 106;
- //
- // Error messages from fingerprint hardware during initilization, enrollment, authentication or
- // removal. Must agree with the list in fingerprint.h
- //
-
- /**
- * The hardware is unavailable. Try again later.
- */
- public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
-
- /**
- * Error state returned when the sensor was unable to process the current image.
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
-
- /**
- * Error state returned when the current request has been running too long. This is intended to
- * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
- * platform and sensor-specific, but is generally on the order of 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
-
- /**
- * Error state returned for operations like enrollment; the operation cannot be completed
- * because there's not enough storage remaining to complete the operation.
- */
- public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
-
- /**
- * The operation was canceled because the fingerprint sensor is unavailable. For example,
- * this may happen when the user is switched, the device is locked or another pending operation
- * prevents or disables it.
- */
- public static final int FINGERPRINT_ERROR_CANCELED = 5;
-
- /**
- * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
- * provided fingerprint id was incorrect.
- *
- * @hide
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
-
- /**
- * The operation was canceled because the API is locked out due to too many attempts.
- * This occurs after 5 failed attempts, and lasts for 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * These messages are typically reserved for internal operations such as enrollment, but may be
- * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
- * expected to show the error message string if they happen, but are advised not to rely on the
- * message id since they will be device and vendor-specific
- */
- public static final int FINGERPRINT_ERROR_VENDOR = 8;
-
- /**
- * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
- * Fingerprint authentication is disabled until the user unlocks with strong authentication
- * (PIN/Pattern/Password)
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
-
- /**
- * The user canceled the operation. Upon receiving this, applications should use alternate
- * authentication (e.g. a password). The application should also provide the means to return
- * to fingerprint authentication, such as a "use fingerprint" button.
- */
- public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
-
- /**
- * @hide
- */
- public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
-
- //
- // Image acquisition messages. Must agree with those in fingerprint.h
- //
-
- /**
- * The image acquired was good.
- */
- public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
-
- /**
- * Only a partial fingerprint image was detected. During enrollment, the user should be
- * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
- */
- public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
-
- /**
- * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
- * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
- */
- public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
-
- /**
- * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
- * For example, it's reasonable return this after multiple
- * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
- * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
- * when this is returned.
- */
- public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
-
- /**
- * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
- * linear array sensors that require a swipe motion.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
-
- /**
- * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
- * linear array sensors, this could also happen if the finger was moved during acquisition.
- * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
- * longer.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
- /**
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
-
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
- private AuthenticationCallback mAuthenticationCallback;
+ private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
- private CryptoObject mCryptoObject;
+ private android.hardware.biometrics.CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
private Executor mExecutor;
@@ -217,9 +90,9 @@ public class FingerprintManager {
}
private class OnAuthenticationCancelListener implements OnCancelListener {
- private CryptoObject mCrypto;
+ private android.hardware.biometrics.CryptoObject mCrypto;
- public OnAuthenticationCancelListener(CryptoObject crypto) {
+ public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
mCrypto = crypto;
}
@@ -233,18 +106,17 @@ public class FingerprintManager {
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
*/
- public static final class CryptoObject {
-
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
public CryptoObject(@NonNull Signature signature) {
- mCrypto = signature;
+ super(signature);
}
public CryptoObject(@NonNull Cipher cipher) {
- mCrypto = cipher;
+ super(cipher);
}
public CryptoObject(@NonNull Mac mac) {
- mCrypto = mac;
+ super(mac);
}
/**
@@ -252,7 +124,7 @@ public class FingerprintManager {
* @return {@link Signature} object or null if this doesn't contain one.
*/
public Signature getSignature() {
- return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ return super.getSignature();
}
/**
@@ -260,7 +132,7 @@ public class FingerprintManager {
* @return {@link Cipher} object or null if this doesn't contain one.
*/
public Cipher getCipher() {
- return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ return super.getCipher();
}
/**
@@ -268,20 +140,9 @@ public class FingerprintManager {
* @return {@link Mac} object or null if this doesn't contain one.
*/
public Mac getMac() {
- return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ return super.getMac();
}
-
- /**
- * @hide
- * @return the opId associated with this object or 0 if none
- */
- public long getOpId() {
- return mCrypto != null ?
- AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
- }
-
- private final Object mCrypto;
- };
+ }
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
@@ -334,13 +195,15 @@ public class FingerprintManager {
* int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
*/
- public static abstract class AuthenticationCallback {
+ public static abstract class AuthenticationCallback
+ extends BiometricAuthenticator.AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
* @param errorCode An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
+ @Override
public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
@@ -350,6 +213,7 @@ public class FingerprintManager {
* @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
+ @Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
@@ -361,6 +225,7 @@ public class FingerprintManager {
/**
* Called when a fingerprint is valid but not recognized.
*/
+ @Override
public void onAuthenticationFailed() { }
/**
@@ -369,7 +234,19 @@ public class FingerprintManager {
* @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
* @hide
*/
+ @Override
public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @hide
+ * @param result
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ (Fingerprint) result.getId(), result.getUserId()));
+ }
};
/**
@@ -489,7 +366,12 @@ public class FingerprintManager {
* by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
* facility</a>.
* @throws IllegalStateException if the crypto primitive is not initialized.
+ * @deprecated See {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
+ * FingerprintDialog.AuthenticationCallback)} and {@link FingerprintDialog#authenticate(
+ * FingerprintDialog.CryptoObject, CancellationSignal, Executor,
+ * FingerprintDialog.AuthenticationCallback)}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
@@ -554,12 +436,12 @@ public class FingerprintManager {
* @param userId the user ID that the fingerprint hardware will authenticate for.
*/
private void authenticate(int userId,
- @Nullable CryptoObject crypto,
+ @Nullable android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
mCryptoObject = crypto;
if (cancel.isCanceled()) {
Log.w(TAG, "authentication already canceled");
@@ -598,7 +480,7 @@ public class FingerprintManager {
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -626,12 +508,12 @@ public class FingerprintManager {
* @param callback
* @hide
*/
- public void authenticate(@NonNull CryptoObject crypto,
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (crypto == null) {
throw new IllegalArgumentException("Must supply a crypto object");
}
@@ -648,9 +530,10 @@ public class FingerprintManager {
throw new IllegalArgumentException("Must supply a receiver");
}
if (callback == null) {
- throw new IllegalArgumentException("Must supply a calback");
+ throw new IllegalArgumentException("Must supply a callback");
}
- authenticate(UserHandle.myUserId(), crypto, cancel, bundle, executor, receiver, callback);
+ authenticate(UserHandle.myUserId(), crypto, cancel,
+ bundle, executor, receiver, callback);
}
/**
@@ -848,7 +731,10 @@ public class FingerprintManager {
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
+ * @deprecated See {@link FingerprintDialog} and
+ * {@link FingerprintDialog#FINGERPRINT_ERROR_NO_FINGERPRINTS}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean hasEnrolledFingerprints() {
if (mService != null) try {
@@ -879,7 +765,10 @@ public class FingerprintManager {
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
+ * @deprecated See {@link FingerprintDialog} and
+ * {@link FingerprintDialog#FINGERPRINT_ERROR_HW_UNAVAILABLE}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
if (mService != null) {
@@ -1049,8 +938,8 @@ public class FingerprintManager {
private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
- final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, fp, userId);
+ final BiometricAuthenticator.AuthenticationResult result =
+ new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -1126,7 +1015,7 @@ public class FingerprintManager {
}
}
- private void cancelAuthentication(CryptoObject cryptoObject) {
+ private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
if (mService != null) try {
mService.cancelAuthentication(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1160,6 +1049,12 @@ public class FingerprintManager {
case FINGERPRINT_ERROR_USER_CANCELED:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_user_canceled);
+ case FINGERPRINT_ERROR_NO_FINGERPRINTS:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_no_fingerprints);
+ case FINGERPRINT_ERROR_HW_NOT_PRESENT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_hw_not_present);
case FINGERPRINT_ERROR_VENDOR: {
String[] msgArray = mContext.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index b00f6033976e..e1d7edfa7d9c 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -213,6 +213,7 @@ public class RadioManager {
private final boolean mIsBgScanSupported;
private final Set<Integer> mSupportedProgramTypes;
private final Set<Integer> mSupportedIdentifierTypes;
+ @Nullable private final Map<String, Integer> mDabFrequencyTable;
@NonNull private final Map<String, String> mVendorInfo;
/** @hide */
@@ -221,6 +222,7 @@ public class RadioManager {
boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
@ProgramSelector.ProgramType int[] supportedProgramTypes,
@ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
+ @Nullable Map<String, Integer> dabFrequencyTable,
Map<String, String> vendorInfo) {
mId = id;
mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
@@ -236,6 +238,13 @@ public class RadioManager {
mIsBgScanSupported = isBgScanSupported;
mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
+ if (dabFrequencyTable != null) {
+ for (Map.Entry<String, Integer> entry : dabFrequencyTable.entrySet()) {
+ Objects.requireNonNull(entry.getKey());
+ Objects.requireNonNull(entry.getValue());
+ }
+ }
+ mDabFrequencyTable = dabFrequencyTable;
mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
}
@@ -363,6 +372,19 @@ public class RadioManager {
}
/**
+ * A frequency table for Digital Audio Broadcasting (DAB).
+ *
+ * The key is a channel name, i.e. 5A, 7B.
+ *
+ * The value is a frequency, in kHz.
+ *
+ * @return a frequency table, or {@code null} if the module doesn't support DAB
+ */
+ public @Nullable Map<String, Integer> getDabFrequencyTable() {
+ return mDabFrequencyTable;
+ }
+
+ /**
* A map of vendor-specific opaque strings, passed from HAL without changes.
* Format of these strings can vary across vendors.
*
@@ -403,6 +425,7 @@ public class RadioManager {
mIsBgScanSupported = in.readInt() == 1;
mSupportedProgramTypes = arrayToSet(in.createIntArray());
mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
+ mDabFrequencyTable = Utils.readStringIntMap(in);
mVendorInfo = Utils.readStringMap(in);
}
@@ -433,6 +456,7 @@ public class RadioManager {
dest.writeInt(mIsBgScanSupported ? 1 : 0);
dest.writeIntArray(setToArray(mSupportedProgramTypes));
dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
+ Utils.writeStringIntMap(dest, mDabFrequencyTable);
Utils.writeStringMap(dest, mVendorInfo);
}
@@ -456,67 +480,31 @@ public class RadioManager {
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + mId;
- result = prime * result + mServiceName.hashCode();
- result = prime * result + mClassId;
- result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode());
- result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode());
- result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());
- result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode());
- result = prime * result + mNumTuners;
- result = prime * result + mNumAudioSources;
- result = prime * result + (mIsCaptureSupported ? 1 : 0);
- result = prime * result + Arrays.hashCode(mBands);
- result = prime * result + (mIsBgScanSupported ? 1 : 0);
- result = prime * result + mVendorInfo.hashCode();
- return result;
+ return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
+ mSerial, mNumTuners, mNumAudioSources, mIsCaptureSupported, mBands,
+ mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
}
@Override
public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (!(obj instanceof ModuleProperties))
- return false;
+ if (this == obj) return true;
+ if (!(obj instanceof ModuleProperties)) return false;
ModuleProperties other = (ModuleProperties) obj;
- if (mId != other.getId())
- return false;
+
+ if (mId != other.getId()) return false;
if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
- if (mClassId != other.getClassId())
- return false;
- if (mImplementor == null) {
- if (other.getImplementor() != null)
- return false;
- } else if (!mImplementor.equals(other.getImplementor()))
- return false;
- if (mProduct == null) {
- if (other.getProduct() != null)
- return false;
- } else if (!mProduct.equals(other.getProduct()))
- return false;
- if (mVersion == null) {
- if (other.getVersion() != null)
- return false;
- } else if (!mVersion.equals(other.getVersion()))
- return false;
- if (mSerial == null) {
- if (other.getSerial() != null)
- return false;
- } else if (!mSerial.equals(other.getSerial()))
- return false;
- if (mNumTuners != other.getNumTuners())
- return false;
- if (mNumAudioSources != other.getNumAudioSources())
- return false;
- if (mIsCaptureSupported != other.isCaptureSupported())
- return false;
- if (!Arrays.equals(mBands, other.getBands()))
- return false;
- if (mIsBgScanSupported != other.isBackgroundScanningSupported())
- return false;
- if (!mVendorInfo.equals(other.mVendorInfo)) return false;
+ if (mClassId != other.mClassId) return false;
+ if (!Objects.equals(mImplementor, other.mImplementor)) return false;
+ if (!Objects.equals(mProduct, other.mProduct)) return false;
+ if (!Objects.equals(mVersion, other.mVersion)) return false;
+ if (!Objects.equals(mSerial, other.mSerial)) return false;
+ if (mNumTuners != other.mNumTuners) return false;
+ if (mNumAudioSources != other.mNumAudioSources) return false;
+ if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
+ if (!Objects.equals(mBands, other.mBands)) return false;
+ if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
+ if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false;
+ if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
return true;
}
}
diff --git a/core/java/android/hardware/radio/Utils.java b/core/java/android/hardware/radio/Utils.java
index f1b589746a14..9887f7823269 100644
--- a/core/java/android/hardware/radio/Utils.java
+++ b/core/java/android/hardware/radio/Utils.java
@@ -56,6 +56,29 @@ final class Utils {
return map;
}
+ static void writeStringIntMap(@NonNull Parcel dest, @Nullable Map<String, Integer> map) {
+ if (map == null) {
+ dest.writeInt(0);
+ return;
+ }
+ dest.writeInt(map.size());
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeInt(entry.getValue());
+ }
+ }
+
+ static @NonNull Map<String, Integer> readStringIntMap(@NonNull Parcel in) {
+ int size = in.readInt();
+ Map<String, Integer> map = new HashMap<>();
+ while (size-- > 0) {
+ String key = in.readString();
+ int value = in.readInt();
+ map.put(key, value);
+ }
+ return map;
+ }
+
static <T extends Parcelable> void writeSet(@NonNull Parcel dest, @Nullable Set<T> set) {
if (set == null) {
dest.writeInt(0);
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 398dda174ba1..91bbdc7dde80 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -88,18 +88,22 @@ interface IUsbManager
/* Returns true if the specified USB function is enabled. */
boolean isFunctionEnabled(String function);
- /* Sets the current USB function as well as whether USB data
- * (for example, MTP exposed pictures) should be made available
- * on the USB connection. Unlocking data should only be done with
- * user involvement, since exposing pictures or other data could
- * leak sensitive user information.
- */
+ /* Sets the current USB function. */
+ void setCurrentFunctions(long functions);
+
+ /* Compatibility version of setCurrentFunctions(long). */
void setCurrentFunction(String function, boolean usbDataUnlocked);
+ /* Gets the current USB functions. */
+ long getCurrentFunctions();
+
/* Sets the screen unlocked USB function(s), which will be set automatically
* when the screen is unlocked.
*/
- void setScreenUnlockedFunctions(String function);
+ void setScreenUnlockedFunctions(long functions);
+
+ /* Gets the current screen unlocked functions. */
+ long getScreenUnlockedFunctions();
/* Allow USB debugging from the attached host. If alwaysAllow is true, add the
* the public key to list of host keys that the user has approved.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 7617c2bd196f..8daecac5a109 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -28,6 +28,7 @@ import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -37,6 +38,8 @@ import android.util.Log;
import com.android.internal.util.Preconditions;
import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
/**
* This class allows you to access the state of USB and communicate with USB devices.
@@ -70,7 +73,7 @@ public class UsbManager {
* MTP function is enabled
* <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
* PTP function is enabled
- * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
+ * <li> {@link #USB_FUNCTION_ACCESSORY} boolean extra indicating whether the
* accessory function is enabled
* <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
* audio source function is enabled
@@ -196,8 +199,7 @@ public class UsbManager {
/**
* A placeholder indicating that no USB function is being specified.
- * Used to distinguish between selecting no function vs. the default function in
- * {@link #setCurrentFunction(String)}.
+ * Used for compatibility with old init scripts to indicate no functions vs. charging function.
*
* {@hide}
*/
@@ -298,6 +300,69 @@ public class UsbManager {
*/
public static final String EXTRA_PERMISSION_GRANTED = "permission";
+ /**
+ * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_NONE = 0;
+
+ /**
+ * Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_MTP = GadgetFunction.MTP;
+
+ /**
+ * Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_PTP = GadgetFunction.PTP;
+
+ /**
+ * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS;
+
+ /**
+ * Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+ * {@hide}
+ */
+ public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
+
+ /**
+ * Code for the accessory usb function.
+ * {@hide}
+ */
+ public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
+
+ /**
+ * Code for the audio source usb function.
+ * {@hide}
+ */
+ public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
+
+ /**
+ * Code for the adb usb function.
+ * {@hide}
+ */
+ public static final long FUNCTION_ADB = GadgetFunction.ADB;
+
+ private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
+ | FUNCTION_MIDI;
+
+ private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
+
+ static {
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MTP, FUNCTION_MTP);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_PTP, FUNCTION_PTP);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_RNDIS, FUNCTION_RNDIS);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MIDI, FUNCTION_MIDI);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
+ FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
+ }
+
private final Context mContext;
private final IUsbManager mService;
@@ -548,15 +613,14 @@ public class UsbManager {
* services offered by the device.
* </p>
*
+ * @deprecated use getCurrentFunctions() instead.
* @param function name of the USB function
* @return true if the USB function is enabled
*
* {@hide}
*/
+ @Deprecated
public boolean isFunctionEnabled(String function) {
- if (mService == null) {
- return false;
- }
try {
return mService.isFunctionEnabled(function);
} catch (RemoteException e) {
@@ -565,7 +629,7 @@ public class UsbManager {
}
/**
- * Sets the current USB function when in device mode.
+ * Sets the current USB functions when in device mode.
* <p>
* USB functions represent interfaces which are published to the host to access
* services offered by the device.
@@ -574,27 +638,59 @@ public class UsbManager {
* automatically activate additional functions such as {@link #USB_FUNCTION_ADB}
* or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states.
* </p><p>
- * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE},
- * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
- * or {@link #USB_FUNCTION_RNDIS}.
- * </p><p>
- * Also sets whether USB data (for example, MTP exposed pictures) should be made available
- * on the USB connection when in device mode. Unlocking usb data should only be done with
- * user involvement, since exposing pictures or other data could leak sensitive
- * user information.
+ * An argument of 0 indicates that the device is charging, and can pick any
+ * appropriate function for that purpose.
* </p><p>
* Note: This function is asynchronous and may fail silently without applying
* the requested changes.
* </p>
*
- * @param function name of the USB function, or null to restore the default function
- * @param usbDataUnlocked whether user data is accessible
+ * @param functions the USB function(s) to set, as a bitwise mask.
+ * Must satisfy {@link UsbManager#areSettableFunctions}
+ *
+ * {@hide}
+ */
+ public void setCurrentFunctions(long functions) {
+ try {
+ mService.setCurrentFunctions(functions);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the current USB functions when in device mode.
+ *
+ * @deprecated use setCurrentFunctions(long) instead.
+ * @param functions the USB function(s) to set.
+ * @param usbDataUnlocked unused
+
+ * {@hide}
+ */
+ @Deprecated
+ public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
+ try {
+ mService.setCurrentFunction(functions, usbDataUnlocked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current USB functions in device mode.
+ * <p>
+ * This function returns the state of primary USB functions and can return a
+ * mask containing any usb function(s) except for ADB.
+ * </p>
+ *
+ * @return The currently enabled functions, in a bitwise mask.
+ * A zero mask indicates that the current function is the charging function.
*
* {@hide}
*/
- public void setCurrentFunction(String function, boolean usbDataUnlocked) {
+ public long getCurrentFunctions() {
try {
- mService.setCurrentFunction(function, usbDataUnlocked);
+ return mService.getCurrentFunctions();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -604,23 +700,37 @@ public class UsbManager {
* Sets the screen unlocked functions, which are persisted and set as the current functions
* whenever the screen is unlocked.
* <p>
- * The allowed values are: {@link #USB_FUNCTION_NONE},
- * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
- * or {@link #USB_FUNCTION_RNDIS}.
- * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions
+ * A zero mask has the effect of switching off this feature, so functions
* no longer change on screen unlock.
* </p><p>
* Note: When the screen is on, this method will apply given functions as current functions,
* which is asynchronous and may fail silently without applying the requested changes.
* </p>
*
- * @param function function to set as default
+ * @param functions functions to set, in a bitwise mask.
+ * Must satisfy {@link UsbManager#areSettableFunctions}
*
* {@hide}
*/
- public void setScreenUnlockedFunctions(String function) {
+ public void setScreenUnlockedFunctions(long functions) {
try {
- mService.setScreenUnlockedFunctions(function);
+ mService.setScreenUnlockedFunctions(functions);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the current screen unlocked functions.
+ *
+ * @return The currently set screen enabled functions.
+ * A zero mask indicates that the screen unlocked functions feature is not enabled.
+ *
+ * {@hide}
+ */
+ public long getScreenUnlockedFunctions() {
+ try {
+ return mService.getScreenUnlockedFunctions();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -719,51 +829,71 @@ public class UsbManager {
}
}
- /** @hide */
- public static String addFunction(String functions, String function) {
- if (USB_FUNCTION_NONE.equals(functions)) {
- return function;
- }
- if (!containsFunction(functions, function)) {
- if (functions.length() > 0) {
- functions += ",";
- }
- functions += function;
- }
- return functions;
+ /**
+ * Returns whether the given functions are valid inputs to UsbManager.
+ * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+ *
+ * @return Whether the mask is settable.
+ *
+ * {@hide}
+ */
+ public static boolean areSettableFunctions(long functions) {
+ return functions == FUNCTION_NONE
+ || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1);
}
- /** @hide */
- public static String removeFunction(String functions, String function) {
- String[] split = functions.split(",");
- for (int i = 0; i < split.length; i++) {
- if (function.equals(split[i])) {
- split[i] = null;
- }
+ /**
+ * Converts the given function mask to string. Maintains ordering with respect to init scripts.
+ *
+ * @return String representation of given mask
+ *
+ * {@hide}
+ */
+ public static String usbFunctionsToString(long functions) {
+ StringJoiner joiner = new StringJoiner(",");
+ if ((functions & FUNCTION_MTP) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_MTP);
}
- if (split.length == 1 && split[0] == null) {
- return USB_FUNCTION_NONE;
+ if ((functions & FUNCTION_PTP) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_PTP);
}
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < split.length; i++) {
- String s = split[i];
- if (s != null) {
- if (builder.length() > 0) {
- builder.append(",");
- }
- builder.append(s);
- }
+ if ((functions & FUNCTION_RNDIS) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_RNDIS);
+ }
+ if ((functions & FUNCTION_MIDI) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_MIDI);
+ }
+ if ((functions & FUNCTION_ACCESSORY) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_ACCESSORY);
+ }
+ if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
}
- return builder.toString();
+ if ((functions & FUNCTION_ADB) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_ADB);
+ }
+ return joiner.toString();
}
- /** @hide */
- public static boolean containsFunction(String functions, String function) {
- int index = functions.indexOf(function);
- if (index < 0) return false;
- if (index > 0 && functions.charAt(index - 1) != ',') return false;
- int charAfter = index + function.length();
- if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
- return true;
+ /**
+ * Parses a string of usb functions that are comma separated.
+ *
+ * @return A mask of all valid functions in the string
+ *
+ * {@hide}
+ */
+ public static long usbFunctionsFromString(String functions) {
+ if (functions == null || functions.equals(USB_FUNCTION_NONE)) {
+ return FUNCTION_NONE;
+ }
+ long ret = 0;
+ for (String function : functions.split(",")) {
+ if (FUNCTION_NAME_TO_CODE.containsKey(function)) {
+ ret |= FUNCTION_NAME_TO_CODE.get(function);
+ } else if (function.length() > 0) {
+ throw new IllegalArgumentException("Invalid usb function " + functions);
+ }
+ }
+ return ret;
}
}
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 9ccdbe2b1bdc..0829b4a3e9fe 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -462,7 +462,7 @@ public final class IpSecTransform implements AutoCloseable {
mConfig.setMode(MODE_TUNNEL);
mConfig.setSourceAddress(sourceAddress.getHostAddress());
mConfig.setSpiResourceId(spi.getResourceId());
- return new IpSecTransform(mContext, mConfig);
+ return new IpSecTransform(mContext, mConfig).activate();
}
/**
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bda720bb6fdd..7922276edb75 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -439,6 +439,10 @@ public class TrafficStats {
}
}
+ private static long addIfSupported(long stat) {
+ return (stat == UNSUPPORTED) ? 0 : stat;
+ }
+
/**
* Return number of packets transmitted across mobile networks since device
* boot. Counts packets across all mobile network interfaces, and always
@@ -451,7 +455,7 @@ public class TrafficStats {
public static long getMobileTxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxPackets(iface);
+ total += addIfSupported(getTxPackets(iface));
}
return total;
}
@@ -468,7 +472,7 @@ public class TrafficStats {
public static long getMobileRxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxPackets(iface);
+ total += addIfSupported(getRxPackets(iface));
}
return total;
}
@@ -485,7 +489,7 @@ public class TrafficStats {
public static long getMobileTxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxBytes(iface);
+ total += addIfSupported(getTxBytes(iface));
}
return total;
}
@@ -502,7 +506,7 @@ public class TrafficStats {
public static long getMobileRxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxBytes(iface);
+ total += addIfSupported(getRxBytes(iface));
}
return total;
}
@@ -517,9 +521,7 @@ public class TrafficStats {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
@@ -534,9 +536,7 @@ public class TrafficStats {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 9edcc0e9b8d4..5ca3a4106a2d 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -720,6 +720,10 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
LOOP: while (end < length) {
switch (uriString.charAt(end)) {
case '/': // Start of path
+ case '\\':// Start of path
+ // Per http://url.spec.whatwg.org/#host-state, the \ character
+ // is treated as if it were a / character when encountered in a
+ // host
case '?': // Start of query
case '#': // Start of fragment
break LOOP;
@@ -758,6 +762,10 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
case '#': // Start of fragment
return ""; // Empty path.
case '/': // Start of path!
+ case '\\':// Start of path!
+ // Per http://url.spec.whatwg.org/#host-state, the \ character
+ // is treated as if it were a / character when encountered in a
+ // host
break LOOP;
}
pathStart++;
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c53ec198e7d..1160415ce212 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,11 @@
package android.os;
+import static android.system.OsConstants.SPLICE_F_MORE;
+import static android.system.OsConstants.SPLICE_F_MOVE;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISREG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.provider.DocumentsContract.Document;
@@ -28,7 +33,9 @@ import android.util.Slog;
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.SizedInputStream;
+import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import java.io.BufferedInputStream;
@@ -41,10 +48,12 @@ import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -81,6 +90,14 @@ public class FileUtils {
private static final File[] EMPTY = new File[0];
+ private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+
+ private static final long COPY_CHECKPOINT_BYTES = 524288;
+
+ public interface ProgressListener {
+ public void onProgress(long progress);
+ }
+
/**
* Set owner and mode of of given {@link File}.
*
@@ -185,6 +202,9 @@ public class FileUtils {
return false;
}
+ /**
+ * @deprecated use {@link #copy(File, File)} instead.
+ */
@Deprecated
public static boolean copyFile(File srcFile, File destFile) {
try {
@@ -195,14 +215,19 @@ public class FileUtils {
}
}
- // copy a file from srcFile to destFile, return true if succeed, return
- // false if fail
+ /**
+ * @deprecated use {@link #copy(File, File)} instead.
+ */
+ @Deprecated
public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
try (InputStream in = new FileInputStream(srcFile)) {
copyToFileOrThrow(in, destFile);
}
}
+ /**
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ */
@Deprecated
public static boolean copyToFile(InputStream inputStream, File destFile) {
try {
@@ -214,29 +239,247 @@ public class FileUtils {
}
/**
- * Copy data from a source stream to destFile.
- * Return true if succeed, return false if failed.
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
*/
- public static void copyToFileOrThrow(InputStream inputStream, File destFile)
- throws IOException {
+ @Deprecated
+ public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
if (destFile.exists()) {
destFile.delete();
}
- FileOutputStream out = new FileOutputStream(destFile);
- try {
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) >= 0) {
- out.write(buffer, 0, bytesRead);
+ try (FileOutputStream out = new FileOutputStream(destFile)) {
+ copy(in, out);
+ try {
+ Os.fsync(out.getFD());
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
}
- } finally {
- out.flush();
+ }
+ }
+
+ /**
+ * Copy the contents of one file to another, replacing any existing content.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull File from, @NonNull File to) throws IOException {
+ return copy(from, to, null, null);
+ }
+
+ /**
+ * Copy the contents of one file to another, replacing any existing content.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull File from, @NonNull File to,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ try (FileInputStream in = new FileInputStream(from);
+ FileOutputStream out = new FileOutputStream(to)) {
+ return copy(in, out, listener, signal);
+ }
+ }
+
+ /**
+ * Copy the contents of one stream to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
+ return copy(in, out, null, null);
+ }
+
+ /**
+ * Copy the contents of one stream to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
+ if (in instanceof FileInputStream && out instanceof FileOutputStream) {
+ return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
+ listener, signal);
+ }
+ }
+
+ // Worse case fallback to userspace
+ return copyInternalUserspace(in, out, listener, signal);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
+ throws IOException {
+ return copy(in, out, null, null);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ return copy(in, out, listener, signal, Long.MAX_VALUE);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @param count the number of bytes to copy.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal, long count)
+ throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
try {
- out.getFD().sync();
- } catch (IOException e) {
+ final StructStat st_in = Os.fstat(in);
+ final StructStat st_out = Os.fstat(out);
+ if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
+ return copyInternalSendfile(in, out, listener, signal, count);
+ } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
+ return copyInternalSplice(in, out, listener, signal, count);
+ }
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ }
+
+ // Worse case fallback to userspace
+ return copyInternalUserspace(in, out, listener, signal, count);
+ }
+
+ /**
+ * Requires one of input or output to be a pipe.
+ */
+ @VisibleForTesting
+ public static long copyInternalSplice(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count)
+ throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.splice(in, null, out, null, Math.min(count, COPY_CHECKPOINT_BYTES),
+ SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
+ progress += t;
+ checkpoint += t;
+ count -= t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
}
- out.close();
}
+ return progress;
+ }
+
+ /**
+ * Requires both input and output to be a regular file.
+ */
+ @VisibleForTesting
+ public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count)
+ throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.sendfile(out, in, null, Math.min(count, COPY_CHECKPOINT_BYTES))) != 0) {
+ progress += t;
+ checkpoint += t;
+ count -= t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
+ }
+
+ @VisibleForTesting
+ public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count) throws IOException {
+ if (count != Long.MAX_VALUE) {
+ return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
+ new FileOutputStream(out), listener, signal);
+ } else {
+ return copyInternalUserspace(new FileInputStream(in),
+ new FileOutputStream(out), listener, signal);
+ }
+ }
+
+ @VisibleForTesting
+ public static long copyInternalUserspace(InputStream in, OutputStream out,
+ ProgressListener listener, CancellationSignal signal) throws IOException {
+ long progress = 0;
+ long checkpoint = 0;
+ byte[] buffer = new byte[8192];
+
+ int t;
+ while ((t = in.read(buffer)) != -1) {
+ out.write(buffer, 0, t);
+
+ progress += t;
+ checkpoint += t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
}
/**
@@ -797,4 +1040,69 @@ public class FileUtils {
}
return val * pow;
}
+
+ @VisibleForTesting
+ public static class MemoryPipe extends Thread implements AutoCloseable {
+ private final FileDescriptor[] pipe;
+ private final byte[] data;
+ private final boolean sink;
+
+ private MemoryPipe(byte[] data, boolean sink) throws IOException {
+ try {
+ this.pipe = Os.pipe();
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ this.data = data;
+ this.sink = sink;
+ }
+
+ private MemoryPipe startInternal() {
+ super.start();
+ return this;
+ }
+
+ public static MemoryPipe createSource(byte[] data) throws IOException {
+ return new MemoryPipe(data, false).startInternal();
+ }
+
+ public static MemoryPipe createSink(byte[] data) throws IOException {
+ return new MemoryPipe(data, true).startInternal();
+ }
+
+ public FileDescriptor getFD() {
+ return sink ? pipe[1] : pipe[0];
+ }
+
+ public FileDescriptor getInternalFD() {
+ return sink ? pipe[0] : pipe[1];
+ }
+
+ @Override
+ public void run() {
+ final FileDescriptor fd = getInternalFD();
+ try {
+ int i = 0;
+ while (i < data.length) {
+ if (sink) {
+ i += Os.read(fd, data, i, data.length - i);
+ } else {
+ i += Os.write(fd, data, i, data.length - i);
+ }
+ }
+ } catch (IOException | ErrnoException e) {
+ // Ignored
+ } finally {
+ if (sink) {
+ SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
+ }
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ IoUtils.closeQuietly(getFD());
+ }
+ }
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b2b43cfd6ddc..2093cec769da 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -393,8 +393,9 @@ public class UserManager {
public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
/**
- * Specifies if a user is disallowed from configuring location mode. Device owner and profile
- * owners can set this restriction and it only applies on the managed user.
+ * Specifies if a user is disallowed from enabling or disabling location providers. As a
+ * result, user is disallowed from turning on or off location. Device owner and profile owners
+ * can set this restriction and it only applies on the managed user.
*
* <p>In a managed profile, location sharing is forced off when it's off on primary user, so
* user can still turn off location sharing on managed profile when the restriction is set by
@@ -408,11 +409,12 @@ public class UserManager {
*
* <p>Key for user restrictions.
* <p>Type: Boolean
+ * @see android.location.LocationManager#isProviderEnabled(String)
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- public static final String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+ public static final String DISALLOW_CONFIG_LOCATION = "no_config_location";
/**
* Specifies if date, time and timezone configuring is disallowed.
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index 747400d486bf..a5f93050e307 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -21,6 +21,8 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
+import android.os.FileUtils;
+import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -114,28 +116,15 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter {
@Override
protected Void doInBackground(Void... params) {
- InputStream in = null;
- OutputStream out = new FileOutputStream(mDestination.getFileDescriptor());
- final byte[] buffer = new byte[8192];
- try {
- in = new FileInputStream(mFile);
- while (true) {
- if (isCancelled()) {
- break;
- }
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error writing data!", ioe);
- mResultCallback.onWriteFailed(mContext.getString(
- R.string.write_fail_reason_cannot_write));
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
+ try (InputStream in = new FileInputStream(mFile);
+ OutputStream out = new FileOutputStream(mDestination.getFileDescriptor())) {
+ FileUtils.copy(in, out, null, mCancellationSignal);
+ } catch (OperationCanceledException e) {
+ // Ignored; already handled below
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Error writing data!", e);
+ mResultCallback.onWriteFailed(mContext.getString(
+ R.string.write_fail_reason_cannot_write));
}
return null;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1f0d683192d5..e3b8a109bbc7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1903,7 +1903,7 @@ public final class Settings {
cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
String newValue = getStringForUser(cr, name, userHandle);
StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
- makeDefault ? 1 : 0, userHandle);
+ makeDefault, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -9315,17 +9315,6 @@ public final class Settings {
private static final Validator WIFI_WAKEUP_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * Value to specify if Wi-Fi Wakeup is available.
- *
- * Wi-Fi Wakeup will only operate if it's available
- * and {@link #WIFI_WAKEUP_ENABLED} is true.
- *
- * Type: int (0 for false, 1 for true)
- * @hide
- */
- public static final String WIFI_WAKEUP_AVAILABLE = "wifi_wakeup_available";
-
- /**
* Value to specify whether network quality scores and badging should be shown in the UI.
*
* Type: int (0 for false, 1 for true)
@@ -10750,14 +10739,14 @@ public final class Settings {
public static final String LOW_POWER_MODE = "low_power";
/**
- * Battery level [1-99] at which low power mode automatically turns on.
+ * Battery level [1-100] at which low power mode automatically turns on.
* If 0, it will not automatically turn on.
* @hide
*/
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 99);
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
/**
* If not 0, the activity manager will aggressively finish activities and
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index f0b9fa2de5d2..65ce67fcba8f 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -25,6 +25,7 @@ package android.se.omapi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import java.io.IOException;
@@ -168,8 +169,10 @@ public class Channel {
throw new IOException("Error in communicating with Secure Element");
}
return response;
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
}
}
}
@@ -244,8 +247,10 @@ public class Channel {
synchronized (mLock) {
return mChannel.selectNext();
}
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
}
}
}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 9f1573973be4..3dec97631e9c 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -24,6 +24,7 @@ package android.se.omapi;
import android.annotation.NonNull;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import java.io.IOException;
@@ -45,8 +46,7 @@ public class Reader {
private final Object mLock = new Object();
- Reader(SEService service, String name, ISecureElementReader reader) throws
- IOException {
+ Reader(SEService service, String name, ISecureElementReader reader) {
if (reader == null || service == null || name == null) {
throw new IllegalArgumentException("Parameters cannot be null");
}
@@ -96,8 +96,10 @@ public class Reader {
ISecureElementSession session;
try {
session = mReader.openSession();
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
}
if (session == null) {
throw new IOException("service session is null.");
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index 1e37277dcd6d..b8937e69c143 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -42,6 +42,23 @@ import java.util.HashMap;
*/
public class SEService {
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if there was an error communicating with the Secure Element.
+ *
+ * @hide
+ */
+ public static final int IO_ERROR = 1;
+
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if AID cannot be selected or is not available when opening
+ * a logical channel.
+ *
+ * @hide
+ */
+ public static final int NO_SUCH_ELEMENT_ERROR = 2;
+
private static final String TAG = "OMAPI.SEService";
private final Object mLock = new Object();
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index bb2a0327ee0d..19a018ee01bb 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -25,6 +25,7 @@ package android.se.omapi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import java.io.IOException;
@@ -207,8 +208,16 @@ public class Session {
return null;
}
return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
} catch (RemoteException e) {
- throw new IOException(e.getMessage());
+ throw new IllegalStateException(e.getMessage());
}
}
}
@@ -311,8 +320,16 @@ public class Session {
return null;
}
return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
} catch (RemoteException e) {
- throw new IOException(e.getMessage());
+ throw new IllegalStateException(e.getMessage());
}
}
}
diff --git a/core/java/android/security/ConfirmationAlreadyPresentingException.java b/core/java/android/security/ConfirmationAlreadyPresentingException.java
new file mode 100644
index 000000000000..ae4ec5a1bf45
--- /dev/null
+++ b/core/java/android/security/ConfirmationAlreadyPresentingException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * This exception is thrown when presenting a prompt fails because another prompt is already
+ * being presented.
+ */
+public class ConfirmationAlreadyPresentingException extends Exception {
+ public ConfirmationAlreadyPresentingException() {}
+
+ public ConfirmationAlreadyPresentingException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/security/ConfirmationCallback.java b/core/java/android/security/ConfirmationCallback.java
new file mode 100644
index 000000000000..4670bce3a9e0
--- /dev/null
+++ b/core/java/android/security/ConfirmationCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback class used when signaling that a prompt is no longer being presented.
+ */
+public abstract class ConfirmationCallback {
+ /**
+ * Called when the requested prompt was accepted by the user.
+ *
+ * The format of 'dataThatWasConfirmed' parameter is a <a href="http://cbor.io/">CBOR</a>
+ * encoded map (type 5) with (at least) the keys <strong>prompt</strong> and
+ * <strong>extra</strong>. The keys are encoded as CBOR text string (type 3). The value of
+ * promptText is encoded as CBOR text string (type 3), and the value of extraData is encoded as
+ * CBOR byte string (type 2). Other keys may be added in the future.
+ *
+ * @param dataThatWasConfirmed the data that was confirmed, see above for the format.
+ */
+ public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {}
+
+ /**
+ * Called when the requested prompt was dismissed (not accepted) by the user.
+ */
+ public void onDismissedByUser() {}
+
+ /**
+ * Called when the requested prompt was dismissed by the application.
+ */
+ public void onDismissedByApplication() {}
+
+ /**
+ * Called when the requested prompt was dismissed because of a low-level error.
+ *
+ * @param e an exception representing the error.
+ */
+ public void onError(Exception e) {}
+}
diff --git a/core/java/android/security/ConfirmationDialog.java b/core/java/android/security/ConfirmationDialog.java
new file mode 100644
index 000000000000..e9df3705db6e
--- /dev/null
+++ b/core/java/android/security/ConfirmationDialog.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+/**
+ * Class used for displaying confirmation prompts.
+ *
+ * <p>Confirmation prompts are prompts shown to the user to confirm a given text and are
+ * implemented in a way that a positive response indicates with high confidence that the user has
+ * seen the given text, even if the Android framework (including the kernel) was
+ * compromised. Implementing confirmation prompts with these guarantees requires dedicated
+ * hardware-support and may not always be available.
+ *
+ * <p>Confirmation prompts are typically used with an external entitity - the <i>Relying Party</i> -
+ * in the following way. The setup steps are as follows:
+ * <ul>
+ * <li> Before first use, the application generates a key-pair with the
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
+ * CONFIRMATION tag} set. Device attestation,
+ * e.g. {@link java.security.KeyStore#getCertificateChain getCertificateChain()}, is used to
+ * generate a certificate chain that includes the public key (<code>Kpub</code> in the following)
+ * of the newly generated key.
+ * <li> The application sends <code>Kpub</code> and the certificate chain resulting from device
+ * attestation to the <i>Relying Party</i>.
+ * <li> The <i>Relying Party</i> validates the certificate chain which involves checking the root
+ * certificate is what is expected (e.g. a certificate from Google), each certificate signs the
+ * next one in the chain, ending with <code>Kpub</code>, and that the attestation certificate
+ * asserts that <code>Kpub</code> has the
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
+ * CONFIRMATION tag} set.
+ * Additionally the relying party stores <code>Kpub</code> and associates it with the device
+ * it was received from.
+ * </ul>
+ *
+ * <p>The <i>Relying Party</i> is typically an external device (for example connected via
+ * Bluetooth) or application server.
+ *
+ * <p>Before executing a transaction which requires a high assurance of user content, the
+ * application does the following:
+ * <ul>
+ * <li> The application gets a cryptographic nonce from the <i>Relying Party</i> and passes this as
+ * the <code>extraData</code> (via the Builder helper class) to the
+ * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally
+ * since it'll use it in a later step.
+ * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the
+ * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the
+ * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the
+ * user, the <code>extraData</code> parameter, and possibly other data.
+ * <li> The application signs the <i>Confirmation Response</i> with the previously created key and
+ * sends the blob and the signature to the <i>Relying Party</i>.
+ * <li> The <i>Relying Party</i> checks that the signature was made with <code>Kpub</code> and then
+ * extracts <code>promptText</code> matches what is expected and <code>extraData</code> matches the
+ * previously created nonce. If all checks passes, the transaction is executed.
+ * </ul>
+ *
+ * <p>A common way of implementing the "<code>promptText</code> is what is expected" check in the
+ * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it
+ * along the nonce in the <code>extraData</code> blob.
+ */
+public class ConfirmationDialog {
+ private static final String TAG = "ConfirmationDialog";
+
+ private CharSequence mPromptText;
+ private byte[] mExtraData;
+ private ConfirmationCallback mCallback;
+ private Executor mExecutor;
+
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+
+ private void doCallback(int responseCode, byte[] dataThatWasConfirmed,
+ ConfirmationCallback callback) {
+ switch (responseCode) {
+ case KeyStore.CONFIRMATIONUI_OK:
+ callback.onConfirmedByUser(dataThatWasConfirmed);
+ break;
+
+ case KeyStore.CONFIRMATIONUI_CANCELED:
+ callback.onDismissedByUser();
+ break;
+
+ case KeyStore.CONFIRMATIONUI_ABORTED:
+ callback.onDismissedByApplication();
+ break;
+
+ case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR:
+ callback.onError(new Exception("System error returned by ConfirmationUI."));
+ break;
+
+ default:
+ callback.onError(new Exception("Unexpected responseCode=" + responseCode
+ + " from onConfirmtionPromptCompleted() callback."));
+ break;
+ }
+ }
+
+ private final android.os.IBinder mCallbackBinder =
+ new android.security.IConfirmationPromptCallback.Stub() {
+ @Override
+ public void onConfirmationPromptCompleted(
+ int responseCode, final byte[] dataThatWasConfirmed)
+ throws android.os.RemoteException {
+ if (mCallback != null) {
+ ConfirmationCallback callback = mCallback;
+ Executor executor = mExecutor;
+ mCallback = null;
+ mExecutor = null;
+ if (executor == null) {
+ doCallback(responseCode, dataThatWasConfirmed, callback);
+ } else {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ doCallback(responseCode, dataThatWasConfirmed, callback);
+ }
+ });
+ }
+ }
+ }
+ };
+
+ /**
+ * A builder that collects arguments, to be shown on the system-provided confirmation dialog.
+ */
+ public static class Builder {
+
+ private CharSequence mPromptText;
+ private byte[] mExtraData;
+
+ /**
+ * Creates a builder for the confirmation dialog.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets the prompt text for the dialog.
+ *
+ * @param promptText the text to present in the prompt.
+ * @return the builder.
+ */
+ public Builder setPromptText(CharSequence promptText) {
+ mPromptText = promptText;
+ return this;
+ }
+
+ /**
+ * Sets the extra data for the dialog.
+ *
+ * @param extraData data to include in the response data.
+ * @return the builder.
+ */
+ public Builder setExtraData(byte[] extraData) {
+ mExtraData = extraData;
+ return this;
+ }
+
+ /**
+ * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder.
+ *
+ * @param context the application context
+ * @return a {@link ConfirmationDialog}
+ * @throws IllegalArgumentException if any of the required fields are not set.
+ */
+ public ConfirmationDialog build(Context context) {
+ if (TextUtils.isEmpty(mPromptText)) {
+ throw new IllegalArgumentException("prompt text must be set and non-empty");
+ }
+ if (mExtraData == null) {
+ throw new IllegalArgumentException("extraData must be set");
+ }
+ return new ConfirmationDialog(mPromptText, mExtraData);
+ }
+ }
+
+ private ConfirmationDialog(CharSequence promptText, byte[] extraData) {
+ mPromptText = promptText;
+ mExtraData = extraData;
+ }
+
+ /**
+ * Requests a confirmation prompt to be presented to the user.
+ *
+ * When the prompt is no longer being presented, one of the methods in
+ * {@link ConfirmationCallback} is called on the supplied callback object.
+ *
+ * @param executor the executor identifying the thread that will receive the callback.
+ * @param callback the callback to use when the dialog is done showing.
+ * @throws IllegalArgumentException if the prompt text is too long or malfomed.
+ * @throws ConfirmationAlreadyPresentingException if another prompt is being presented.
+ * @throws ConfirmationNotAvailableException if confirmation prompts are not supported.
+ */
+ public void presentPrompt(@NonNull Executor executor, @NonNull ConfirmationCallback callback)
+ throws ConfirmationAlreadyPresentingException,
+ ConfirmationNotAvailableException {
+ if (mCallback != null) {
+ throw new ConfirmationAlreadyPresentingException();
+ }
+ mCallback = callback;
+ mExecutor = executor;
+
+ int uiOptionsAsFlags = 0;
+ // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed.
+ String locale = Locale.getDefault().toLanguageTag();
+ int responseCode = mKeyStore.presentConfirmationPrompt(
+ mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
+ switch (responseCode) {
+ case KeyStore.CONFIRMATIONUI_OK:
+ return;
+
+ case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
+ throw new ConfirmationAlreadyPresentingException();
+
+ case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED:
+ throw new ConfirmationNotAvailableException();
+
+ case KeyStore.CONFIRMATIONUI_UIERROR:
+ throw new IllegalArgumentException();
+
+ default:
+ // Unexpected error code.
+ Log.w(TAG,
+ "Unexpected responseCode=" + responseCode
+ + " from presentConfirmationPrompt() call.");
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Cancels a prompt currently being displayed.
+ *
+ * On success, the
+ * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on
+ * the supplied callback object will be called asynchronously.
+ *
+ * @throws IllegalStateException if no prompt is currently being presented.
+ */
+ public void cancelPrompt() {
+ int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder);
+ if (responseCode == KeyStore.CONFIRMATIONUI_OK) {
+ return;
+ } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) {
+ throw new IllegalStateException();
+ } else {
+ // Unexpected error code.
+ Log.w(TAG,
+ "Unexpected responseCode=" + responseCode
+ + " from cancelConfirmationPrompt() call.");
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Checks if the device supports confirmation prompts.
+ *
+ * @return true if confirmation prompts are supported by the device.
+ */
+ public static boolean isSupported() {
+ // TODO: read and return system property.
+ return true;
+ }
+}
diff --git a/core/java/android/security/ConfirmationNotAvailableException.java b/core/java/android/security/ConfirmationNotAvailableException.java
new file mode 100644
index 000000000000..8d0e672c0671
--- /dev/null
+++ b/core/java/android/security/ConfirmationNotAvailableException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * This exception is thrown when presenting a prompt fails because the the environment lacks
+ * facilities for showing confirmations.
+ */
+public class ConfirmationNotAvailableException extends Exception {
+ public ConfirmationNotAvailableException() {
+ }
+
+ public ConfirmationNotAvailableException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 346437032845..1d1333504350 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -74,6 +74,7 @@ public final class KeymasterDefs {
public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
+ public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 4e4a0374087e..7cd08f76a47e 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -26,9 +26,13 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
import com.android.internal.widget.ILockSettings;
+import java.security.Key;
+import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
@@ -113,9 +117,11 @@ public class RecoveryController {
private final ILockSettings mBinder;
+ private final KeyStore mKeyStore;
- private RecoveryController(ILockSettings binder) {
+ private RecoveryController(ILockSettings binder, KeyStore keystore) {
mBinder = binder;
+ mKeyStore = keystore;
}
/**
@@ -133,7 +139,7 @@ public class RecoveryController {
public static RecoveryController getInstance(Context context) {
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryController(lockSettings);
+ return new RecoveryController(lockSettings, KeyStore.getInstance());
}
/**
@@ -430,6 +436,7 @@ public class RecoveryController {
}
/**
+ * Deprecated.
* Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
* key store. Returns the raw material of the key.
*
@@ -444,7 +451,6 @@ public class RecoveryController {
public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
throws InternalRecoveryServiceException, LockScreenRequiredException {
try {
- // TODO: add account
return mBinder.generateAndStoreKey(alias);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -457,6 +463,72 @@ public class RecoveryController {
}
/**
+ * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
+ * key store. Returns {@link javax.crypto.SecretKey}.
+ *
+ * @param alias The key alias.
+ * @param account The account associated with the key.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
+ * to generate recoverable keys, as the snapshots are encrypted using a key derived from the
+ * lock screen.
+ * @hide
+ */
+ public Key generateKey(@NonNull String alias, byte[] account)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ // TODO: update RecoverySession.recoverKeys
+ try {
+ String grantAlias = mBinder.generateKey(alias, account);
+ if (grantAlias == null) {
+ return null;
+ }
+ Key result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+ mKeyStore,
+ grantAlias,
+ KeyStore.UID_SELF);
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (UnrecoverableKeyException e) {
+ throw new InternalRecoveryServiceException("Access to newly generated key failed for");
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_INSECURE_USER) {
+ throw new LockScreenRequiredException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Gets a key called {@code alias} from the recoverable key store.
+ *
+ * @param alias The key alias.
+ * @return The key.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws UnrecoverableKeyException if key is permanently invalidated or not found.
+ * @hide
+ */
+ public @Nullable Key getKey(@NonNull String alias)
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ try {
+ String grantAlias = mBinder.getKey(alias);
+ if (grantAlias == null) {
+ return null;
+ }
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+ mKeyStore,
+ grantAlias,
+ KeyStore.UID_SELF);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
* Removes a key called {@code alias} from the recoverable key store.
*
* @param alias The key alias.
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 917efa8bd5f4..12aa64e4ecb7 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -468,9 +468,8 @@ import com.android.internal.os.SomeArgs;
* <p>Typically, field classification can be used to detect fields that can be autofilled with
* user data that is not associated with a specific app&mdash;such as email and physical
* address. Once the service identifies that a such field was manually filled by the user, the
- * service could use this signal to improve its heuristics, either locally (i.e., in the same
- * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can
- * be used by other users).
+ * service could use this signal to improve its heuristics on subsequent requests (for example, by
+ * infering which resource ids are associated with known fields).
*
* <p>The field classification workflow involves 4 steps:
*
@@ -481,8 +480,8 @@ import com.android.internal.os.SomeArgs;
* <li>Identify which fields should be analysed by calling
* {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}.
* <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}.
- * <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future
- * requests.
+ * <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in
+ * subsequent requests.
* </ol>
*
* <p>The field classification is an expensive operation and should be used carefully, otherwise it
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index b8e8b19f9786..fb468a8dad6f 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -173,7 +173,10 @@ public final class CustomDescription implements Parcelable {
}
/**
- * Updates the {@link RemoteViews presentation template} when a condition is satisfied.
+ * Updates the {@link RemoteViews presentation template} when a condition is satisfied by
+ * applying a series of remote view operations. This allows dynamic customization of the
+ * portion of the save UI that is controlled by the autofill service. Such dynamic
+ * customization is based on the content of target views.
*
* <p>The updates are applied in the sequence they are added, after the
* {@link #addChild(int, Transformation) transformations} are applied to the children
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 266bcda7797a..f32dee1ec9f2 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -29,7 +29,6 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
-import java.io.Serializable;
import java.util.ArrayList;
import java.util.regex.Pattern;
@@ -99,7 +98,7 @@ public final class Dataset implements Parcelable {
private final ArrayList<AutofillId> mFieldIds;
private final ArrayList<AutofillValue> mFieldValues;
private final ArrayList<RemoteViews> mFieldPresentations;
- private final ArrayList<Pattern> mFieldFilters;
+ private final ArrayList<DatasetFieldFilter> mFieldFilters;
private final RemoteViews mPresentation;
private final IntentSender mAuthentication;
@Nullable String mId;
@@ -132,7 +131,7 @@ public final class Dataset implements Parcelable {
/** @hide */
@Nullable
- public Pattern getFilter(int index) {
+ public DatasetFieldFilter getFilter(int index) {
return mFieldFilters.get(index);
}
@@ -189,7 +188,7 @@ public final class Dataset implements Parcelable {
private ArrayList<AutofillId> mFieldIds;
private ArrayList<AutofillValue> mFieldValues;
private ArrayList<RemoteViews> mFieldPresentations;
- private ArrayList<Pattern> mFieldFilters;
+ private ArrayList<DatasetFieldFilter> mFieldFilters;
private RemoteViews mPresentation;
private IntentSender mAuthentication;
private boolean mDestroyed;
@@ -363,19 +362,21 @@ public final class Dataset implements Parcelable {
* @param value the value to be autofilled. Pass {@code null} if you do not have the value
* but the target view is a logical part of the dataset. For example, if
* the dataset needs authentication and you have no access to the value.
- * @param filter regex used to determine if the dataset should be shown in the autofill UI.
+ * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
*
* @return this builder.
* @throws IllegalStateException if the builder was constructed without a
* {@link RemoteViews presentation}.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
- @NonNull Pattern filter) {
+ @Nullable Pattern filter) {
throwIfDestroyed();
- Preconditions.checkNotNull(filter, "filter cannot be null");
Preconditions.checkState(mPresentation != null,
"Dataset presentation not set on constructor");
- setLifeTheUniverseAndEverything(id, value, null, filter);
+ setLifeTheUniverseAndEverything(id, value, null, new DatasetFieldFilter(filter));
return this;
}
@@ -398,23 +399,26 @@ public final class Dataset implements Parcelable {
* @param value the value to be autofilled. Pass {@code null} if you do not have the value
* but the target view is a logical part of the dataset. For example, if
* the dataset needs authentication and you have no access to the value.
+ * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
* @param presentation the presentation used to visualize this field.
- * @param filter regex used to determine if the dataset should be shown in the autofill UI.
*
* @return this builder.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
- @NonNull Pattern filter, @NonNull RemoteViews presentation) {
+ @Nullable Pattern filter, @NonNull RemoteViews presentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(filter, "filter cannot be null");
Preconditions.checkNotNull(presentation, "presentation cannot be null");
- setLifeTheUniverseAndEverything(id, value, presentation, filter);
+ setLifeTheUniverseAndEverything(id, value, presentation,
+ new DatasetFieldFilter(filter));
return this;
}
private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable RemoteViews presentation,
- @Nullable Pattern filter) {
+ @Nullable DatasetFieldFilter filter) {
Preconditions.checkNotNull(id, "id cannot be null");
if (mFieldIds != null) {
final int existingIdx = mFieldIds.indexOf(id);
@@ -477,8 +481,8 @@ public final class Dataset implements Parcelable {
parcel.writeParcelable(mPresentation, flags);
parcel.writeTypedList(mFieldIds, flags);
parcel.writeTypedList(mFieldValues, flags);
- parcel.writeParcelableList(mFieldPresentations, flags);
- parcel.writeSerializable(mFieldFilters);
+ parcel.writeTypedList(mFieldPresentations, flags);
+ parcel.writeTypedList(mFieldFilters, flags);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeString(mId);
}
@@ -493,22 +497,19 @@ public final class Dataset implements Parcelable {
final Builder builder = (presentation == null)
? new Builder()
: new Builder(presentation);
- final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR);
+ final ArrayList<AutofillId> ids =
+ parcel.createTypedArrayList(AutofillId.CREATOR);
final ArrayList<AutofillValue> values =
parcel.createTypedArrayList(AutofillValue.CREATOR);
- final ArrayList<RemoteViews> presentations = new ArrayList<>();
- parcel.readParcelableList(presentations, null);
- @SuppressWarnings("unchecked")
- final ArrayList<Serializable> filters =
- (ArrayList<Serializable>) parcel.readSerializable();
- final int idCount = (ids != null) ? ids.size() : 0;
- final int valueCount = (values != null) ? values.size() : 0;
- for (int i = 0; i < idCount; i++) {
+ final ArrayList<RemoteViews> presentations =
+ parcel.createTypedArrayList(RemoteViews.CREATOR);
+ final ArrayList<DatasetFieldFilter> filters =
+ parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
+ for (int i = 0; i < ids.size(); i++) {
final AutofillId id = ids.get(i);
- final AutofillValue value = (valueCount > i) ? values.get(i) : null;
- final RemoteViews fieldPresentation = presentations.isEmpty() ? null
- : presentations.get(i);
- final Pattern filter = (Pattern) filters.get(i);
+ final AutofillValue value = values.get(i);
+ final RemoteViews fieldPresentation = presentations.get(i);
+ final DatasetFieldFilter filter = filters.get(i);
builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter);
}
builder.setAuthentication(parcel.readParcelable(null));
@@ -521,4 +522,55 @@ public final class Dataset implements Parcelable {
return new Dataset[size];
}
};
+
+ /**
+ * Helper class used to indicate when the service explicitly set a {@link Pattern} filter for a
+ * dataset field&dash; we cannot use a {@link Pattern} directly because then we wouldn't be
+ * able to differentiate whether the service explicitly passed a {@code null} filter to disable
+ * filter, or when it called the methods that does not take a filter {@link Pattern}.
+ *
+ * @hide
+ */
+ public static final class DatasetFieldFilter implements Parcelable {
+
+ @Nullable
+ public final Pattern pattern;
+
+ private DatasetFieldFilter(@Nullable Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+ @Override
+ public String toString() {
+ if (!sDebug) return super.toString();
+
+ // Cannot log pattern because it could contain PII
+ return pattern == null ? "null" : pattern.pattern().length() + "_chars";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeSerializable(pattern);
+ }
+
+ @SuppressWarnings("hiding")
+ public static final Creator<DatasetFieldFilter> CREATOR =
+ new Creator<DatasetFieldFilter>() {
+
+ @Override
+ public DatasetFieldFilter createFromParcel(Parcel parcel) {
+ return new DatasetFieldFilter((Pattern) parcel.readSerializable());
+ }
+
+ @Override
+ public DatasetFieldFilter[] newArray(int size) {
+ return new DatasetFieldFilter[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 23ae4b96810f..d66322c3aa89 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -17,6 +17,7 @@
package android.service.notification;
import android.app.ActivityManager;
+import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -1445,4 +1446,99 @@ public class ZenModeConfig implements Parcelable {
return !config.allowAlarms && !config.allowMediaSystemOther
&& areAllPriorityOnlyNotificationZenSoundsMuted(config);
}
+
+ /**
+ * Returns a description of the current do not disturb settings from config.
+ * - If turned on manually and end time is known, returns end time.
+ * - If turned on by an automatic rule, returns the automatic rule name.
+ * - If on due to an app, returns the app name.
+ * - If there's a combination of rules/apps that trigger, then shows the one that will
+ * last the longest if applicable.
+ * @return null if do not disturb is off.
+ */
+ public static String getDescription(Context context, boolean zenOn, ZenModeConfig config) {
+ if (!zenOn) {
+ return null;
+ }
+
+ String secondaryText = "";
+ long latestEndTime = -1;
+
+ // DND turned on by manual rule
+ if (config.manualRule != null) {
+ final Uri id = config.manualRule.conditionId;
+ if (config.manualRule.enabler != null) {
+ // app triggered manual rule
+ String appName = getOwnerCaption(context, config.manualRule.enabler);
+ if (!appName.isEmpty()) {
+ secondaryText = appName;
+ }
+ } else {
+ if (id == null) {
+ // Do not disturb manually triggered to remain on forever until turned off
+ // No subtext
+ return null;
+ } else {
+ latestEndTime = tryParseCountdownConditionId(id);
+ if (latestEndTime > 0) {
+ final CharSequence formattedTime = getFormattedTime(context,
+ latestEndTime, isToday(latestEndTime),
+ context.getUserId());
+ secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
+ }
+ }
+ }
+ }
+
+ // DND turned on by an automatic rule
+ for (ZenRule automaticRule : config.automaticRules.values()) {
+ if (automaticRule.isAutomaticActive()) {
+ if (isValidEventConditionId(automaticRule.conditionId)
+ || isValidScheduleConditionId(automaticRule.conditionId)) {
+ // set text if automatic rule end time is the latest active rule end time
+ long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
+ if (endTime > latestEndTime) {
+ latestEndTime = endTime;
+ secondaryText = automaticRule.name;
+ }
+ } else {
+ // set text if 3rd party rule
+ return automaticRule.name;
+ }
+ }
+ }
+
+ return !secondaryText.equals("") ? secondaryText : null;
+ }
+
+ private static long parseAutomaticRuleEndTime(Context context, Uri id) {
+ if (isValidEventConditionId(id)) {
+ // cannot look up end times for events
+ return Long.MAX_VALUE;
+ }
+
+ if (isValidScheduleConditionId(id)) {
+ ScheduleCalendar schedule = toScheduleCalendar(id);
+ long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
+
+ // check if automatic rule will end on next alarm
+ if (schedule.exitAtAlarm()) {
+ long nextAlarm = getNextAlarm(context);
+ schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
+ if (schedule.shouldExitForAlarm(endTimeMs)) {
+ return nextAlarm;
+ }
+ }
+
+ return endTimeMs;
+ }
+
+ return -1;
+ }
+
+ private static long getNextAlarm(Context context) {
+ final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
+ return info != null ? info.getTriggerTime() : 0;
+ }
}
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index 11e1e674a747..e97f963a0986 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -40,6 +40,7 @@ public final class Suggestion implements Parcelable {
*/
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_HAS_BUTTON,
+ FLAG_ICON_TINTABLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flags {
@@ -49,6 +50,10 @@ public final class Suggestion implements Parcelable {
* Flag for suggestion type with a single button
*/
public static final int FLAG_HAS_BUTTON = 1 << 0;
+ /**
+ * @hide
+ */
+ public static final int FLAG_ICON_TINTABLE = 1 << 1;
private final String mId;
private final CharSequence mTitle;
diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java
index 35241796c3c3..cd199b3547c9 100644
--- a/core/java/android/text/style/DrawableMarginSpan.java
+++ b/core/java/android/text/style/DrawableMarginSpan.java
@@ -16,60 +16,100 @@
package android.text.style;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.Spanned;
-public class DrawableMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public DrawableMarginSpan(Drawable b) {
- mDrawable = b;
+/**
+ * A span which adds a drawable and a padding to the paragraph it's attached to.
+ * <p>
+ * If the height of the drawable is bigger than the height of the line it's attached to then the
+ * line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a
+ * padding between the drawable and the text. The default value is 0. The span must be set from the
+ * beginning of the text, otherwise either the span won't be rendered or it will be rendered
+ * incorrectly.
+ * <p>
+ * For example, a drawable and a padding of 20px can be added like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with a drawable.");
+ * string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/drawablemarginspan.png" />
+ * <figcaption>Text with a drawable and a padding.</figcaption>
+ * <p>
+ *
+ * @see IconMarginSpan for working with a {@link android.graphics.Bitmap} instead of
+ * a {@link Drawable}.
+ */
+public class DrawableMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+ private static final int STANDARD_PAD_WIDTH = 0;
+
+ @NonNull
+ private final Drawable mDrawable;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable}. The pad width will be 0.
+ *
+ * @param drawable the drawable to be added
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable) {
+ this(drawable, STANDARD_PAD_WIDTH);
}
- public DrawableMarginSpan(Drawable b, int pad) {
- mDrawable = b;
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable} and a padding, in pixels.
+ *
+ * @param drawable the drawable to be added
+ * @param pad the distance between the drawable and the text
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable, int pad) {
+ mDrawable = drawable;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mDrawable.getIntrinsicWidth() + mPad;
}
- public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ @Override
+ public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
+ int top, int baseline, int bottom,
+ @NonNull CharSequence text, int start, int end,
+ boolean first, @NonNull Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
- int ix = (int)x;
- int itop = (int)layout.getLineTop(layout.getLineForOffset(st));
+ int ix = (int) x;
+ int itop = (int) layout.getLineTop(layout.getLineForOffset(st));
int dw = mDrawable.getIntrinsicWidth();
int dh = mDrawable.getIntrinsicHeight();
// XXX What to do about Paint?
- mDrawable.setBounds(ix, itop, ix+dw, itop+dh);
+ mDrawable.setBounds(ix, itop, ix + dw, itop + dh);
mDrawable.draw(c);
}
- public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ @Override
+ public void chooseHeight(@NonNull CharSequence text, int start, int end,
+ int istartv, int v,
+ @NonNull Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mDrawable.getIntrinsicHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
-
- private Drawable mDrawable;
- private int mPad;
}
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index 5b8a6dd3bf6f..1b16f3345bfa 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -16,6 +16,9 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -24,32 +27,71 @@ import android.graphics.drawable.Drawable;
import java.lang.ref.WeakReference;
/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text.
+ * <p>
+ * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>,
+ * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}.
+ * <p>
+ * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources
+ * looks like this:
+ * <pre>
+ * class MyDynamicDrawableSpan extends DynamicDrawableSpan {
*
+ * private final Context mContext;
+ * private final int mResourceId;
+ *
+ * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) {
+ * mContext = context;
+ * mResourceId = resourceId;
+ * }
+ *
+ * {@literal @}Override
+ * public Drawable getDrawable() {
+ * Drawable drawable = mContext.getDrawable(mResourceId);
+ * drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ * return drawable;
+ * }
+ * }</pre>
+ * The class can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a drawable span");
+ * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned
+ * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" />
+ * <figcaption>Replacing text with a drawable.</figcaption>
*/
public abstract class DynamicDrawableSpan extends ReplacementSpan {
- private static final String TAG = "DynamicDrawableSpan";
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the bottom of the surrounding text, i.e., at the same level as the
* lowest descender in the text.
*/
public static final int ALIGN_BOTTOM = 0;
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the baseline of the surrounding text.
*/
public static final int ALIGN_BASELINE = 1;
-
+
protected final int mVerticalAlignment;
-
+
+ private WeakReference<Drawable> mDrawableRef;
+
+ /**
+ * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is
+ * {@link #ALIGN_BOTTOM}
+ */
public DynamicDrawableSpan() {
mVerticalAlignment = ALIGN_BOTTOM;
}
/**
- * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}.
+ * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\
+ *
+ * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}
*/
protected DynamicDrawableSpan(int verticalAlignment) {
mVerticalAlignment = verticalAlignment;
@@ -64,22 +106,22 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan {
}
/**
- * Your subclass must implement this method to provide the bitmap
+ * Your subclass must implement this method to provide the bitmap
* to be drawn. The dimensions of the bitmap must be the same
* from each call to the next.
*/
public abstract Drawable getDrawable();
@Override
- public int getSize(Paint paint, CharSequence text,
- int start, int end,
- Paint.FontMetricsInt fm) {
+ public int getSize(@NonNull Paint paint, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @Nullable Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
if (fm != null) {
- fm.ascent = -rect.bottom;
- fm.descent = 0;
+ fm.ascent = -rect.bottom;
+ fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
@@ -89,12 +131,12 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan {
}
@Override
- public void draw(Canvas canvas, CharSequence text,
- int start, int end, float x,
- int top, int y, int bottom, Paint paint) {
+ public void draw(@NonNull Canvas canvas, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
+ int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();
-
+
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
@@ -109,8 +151,9 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan {
WeakReference<Drawable> wr = mDrawableRef;
Drawable d = null;
- if (wr != null)
+ if (wr != null) {
d = wr.get();
+ }
if (d == null) {
d = getDrawable();
@@ -119,7 +162,5 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan {
return d;
}
-
- private WeakReference<Drawable> mDrawableRef;
}
diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java
index 304c83f19f02..ad78bd574696 100644
--- a/core/java/android/text/style/IconMarginSpan.java
+++ b/core/java/android/text/style/IconMarginSpan.java
@@ -16,57 +16,98 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.Spanned;
-public class IconMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public IconMarginSpan(Bitmap b) {
- mBitmap = b;
+/**
+ * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows
+ * setting a padding between the bitmap and the text. The default value of the padding is 0px. The
+ * span should be attached from the first character of the text.
+ * <p>
+ * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set
+ * like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with icon and padding");
+ * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" />
+ * <figcaption>Text with <code>IconMarginSpan</code></figcaption>
+ * <p>
+ *
+ * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
+ * a {@link Bitmap}.
+ */
+public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+
+ @NonNull
+ private final Bitmap mBitmap;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap) {
+ this(bitmap, 0);
}
- public IconMarginSpan(Bitmap b, int pad) {
- mBitmap = b;
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ * @param pad padding width, in pixels, between the bitmap and the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) {
+ mBitmap = bitmap;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mBitmap.getWidth() + mPad;
}
+ @Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ int top, int baseline, int bottom,
+ CharSequence text, int start, int end,
+ boolean first, Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
int itop = layout.getLineTop(layout.getLineForOffset(st));
- if (dir < 0)
+ if (dir < 0) {
x -= mBitmap.getWidth();
+ }
c.drawBitmap(mBitmap, x, itop, p);
}
+ @Override
public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ int istartv, int v,
+ Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mBitmap.getHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
- private Bitmap mBitmap;
- private int mPad;
}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index b0bff680f390..95f0b43341a2 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -17,6 +17,8 @@
package android.text.style;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -27,18 +29,49 @@ import android.util.Log;
import java.io.InputStream;
+/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text. The drawable can be constructed from
+ * varied sources:
+ * <ul>
+ * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and
+ * {@link #ImageSpan(Context, Bitmap, int)}
+ * </li>
+ * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li>
+ * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li>
+ * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li>
+ * </ul>
+ * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ * <p>
+ * For example, an <code>ImagedSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = SpannableString("Bottom: span.\nBaseline: span.");
+ * // using the default alignment: ALIGN_BOTTOM
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE),
+ * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" />
+ * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption>
+ */
public class ImageSpan extends DynamicDrawableSpan {
+
+ @Nullable
private Drawable mDrawable;
+ @Nullable
private Uri mContentUri;
+ @DrawableRes
private int mResourceId;
+ @Nullable
private Context mContext;
+ @Nullable
private String mSource;
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b) {
+ public ImageSpan(@NonNull Bitmap b) {
this(null, b, ALIGN_BOTTOM);
}
@@ -46,80 +79,143 @@ public class ImageSpan extends DynamicDrawableSpan {
* @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Bitmap b, int verticalAlignment) {
this(null, b, verticalAlignment);
}
- public ImageSpan(Context context, Bitmap b) {
- this(context, b, ALIGN_BOTTOM);
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param bitmap bitmap to be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) {
+ this(context, bitmap, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical
+ * alignment.
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display metrics of the resources
+ * @param bitmap bitmap to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mDrawable = context != null
- ? new BitmapDrawable(context.getResources(), b)
- : new BitmapDrawable(b);
+ ? new BitmapDrawable(context.getResources(), bitmap)
+ : new BitmapDrawable(bitmap);
int width = mDrawable.getIntrinsicWidth();
int height = mDrawable.getIntrinsicHeight();
- mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
+ mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
}
- public ImageSpan(Drawable d) {
- this(d, ALIGN_BOTTOM);
+ /**
+ * Constructs an {@link ImageSpan} from a drawable with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}.
+ *
+ * @param drawable drawable to be rendered
+ */
+ public ImageSpan(@NonNull Drawable drawable) {
+ this(drawable, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a drawable and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Drawable d, int verticalAlignment) {
+ public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) {
super(verticalAlignment);
- mDrawable = d;
+ mDrawable = drawable;
}
- public ImageSpan(Drawable d, String source) {
- this(d, source, ALIGN_BOTTOM);
+ /**
+ * Constructs an {@link ImageSpan} from a drawable and a source with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's Uri source
+ */
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source) {
+ this(drawable, source, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's uri source
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Drawable d, String source, int verticalAlignment) {
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) {
super(verticalAlignment);
- mDrawable = d;
+ mDrawable = drawable;
mSource = source;
}
- public ImageSpan(Context context, Uri uri) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via
+ * {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri) {
this(context, uri, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical
+ * alignment. The Uri source can be retrieved via {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Uri uri, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mContentUri = uri;
mSource = uri.toString();
}
- public ImageSpan(Context context, @DrawableRes int resourceId) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved
+ */
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) {
this(context, resourceId, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical
+ * alignment.
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId,
+ int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mResourceId = resourceId;
@@ -128,10 +224,10 @@ public class ImageSpan extends DynamicDrawableSpan {
@Override
public Drawable getDrawable() {
Drawable drawable = null;
-
+
if (mDrawable != null) {
drawable = mDrawable;
- } else if (mContentUri != null) {
+ } else if (mContentUri != null) {
Bitmap bitmap = null;
try {
InputStream is = mContext.getContentResolver().openInputStream(
@@ -142,7 +238,7 @@ public class ImageSpan extends DynamicDrawableSpan {
drawable.getIntrinsicHeight());
is.close();
} catch (Exception e) {
- Log.e("sms", "Failed to loaded content " + mContentUri, e);
+ Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e);
}
} else {
try {
@@ -150,8 +246,8 @@ public class ImageSpan extends DynamicDrawableSpan {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
} catch (Exception e) {
- Log.e("sms", "Unable to find resource: " + mResourceId);
- }
+ Log.e("ImageSpan", "Unable to find resource: " + mResourceId);
+ }
}
return drawable;
@@ -159,9 +255,12 @@ public class ImageSpan extends DynamicDrawableSpan {
/**
* Returns the source string that was saved during construction.
+ *
+ * @return the source string that was saved during construction
+ * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
*/
+ @Nullable
public String getSource() {
return mSource;
}
-
}
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 1ebee82c18a8..50ee5f387595 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -19,16 +19,42 @@ package android.text.style;
import android.graphics.Paint;
import android.text.TextPaint;
-public interface LineHeightSpan
-extends ParagraphStyle, WrapTogetherSpan
-{
+/**
+ * The classes that affect the height of the line should implement this interface.
+ */
+public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param fm font metrics of the paint, in integers
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm);
+ /**
+ * The classes that affect the height of the line with respect to density, should implement this
+ * interface.
+ */
public interface WithDensity extends LineHeightSpan {
+
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param paint the paint
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm, TextPaint paint);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm, TextPaint paint);
}
}
diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java
index 2ff52a8f70e8..d76ef941d42e 100644
--- a/core/java/android/text/style/MaskFilterSpan.java
+++ b/core/java/android/text/style/MaskFilterSpan.java
@@ -18,15 +18,36 @@ package android.text.style;
import android.graphics.MaskFilter;
import android.text.TextPaint;
-
+/**
+ * Span that allows setting a {@link MaskFilter} to the text it's attached to.
+ * <p>
+ * For example, to blur a text, a {@link android.graphics.BlurMaskFilter} can be used:
+ * <pre>
+ * MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL);
+ * SpannableString string = new SpannableString("Text with blur mask");
+ * string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
+ * <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
+ */
public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
private MaskFilter mFilter;
+ /**
+ * Creates a {@link MaskFilterSpan} from a {@link MaskFilter}.
+ *
+ * @param filter the filter to be applied to the <code>TextPaint</code>
+ */
public MaskFilterSpan(MaskFilter filter) {
mFilter = filter;
}
+ /**
+ * Return the mask filter for this span.
+ *
+ * @return the mask filter for this span
+ */
public MaskFilter getMaskFilter() {
return mFilter;
}
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index f900db502d0d..bdfa700215f8 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,55 +25,76 @@ import android.text.TextPaint;
import android.text.TextUtils;
/**
- *
- * Describes a style in a span.
+ * Span that allows setting the style of the text it's attached to.
+ * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and
+ * {@link Typeface#BOLD_ITALIC}.
+ * <p>
* Note that styles are cumulative -- if both bold and italic are set in
* separate spans, or if the base style is bold and a span calls for italic,
* you get bold italic. You can't turn off a style from the base style.
- *
+ * <p>
+ * For example, the <code>StyleSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Bold and italic text");
+ * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
+ * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
*/
public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mStyle;
/**
- *
+ * Creates a {@link StyleSpan} from a style.
+ *
* @param style An integer constant describing the style for this span. Examples
- * include bold, italic, and normal. Values are constants defined
- * in {@link android.graphics.Typeface}.
+ * include bold, italic, and normal. Values are constants defined
+ * in {@link Typeface}.
*/
public StyleSpan(int style) {
mStyle = style;
}
- public StyleSpan(Parcel src) {
+ /**
+ * Creates a {@link StyleSpan} from a parcel.
+ *
+ * @param src the parcel
+ */
+ public StyleSpan(@NonNull Parcel src) {
mStyle = src.readInt();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.STYLE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mStyle);
}
/**
- * Returns the style constant defined in {@link android.graphics.Typeface}.
+ * Returns the style constant defined in {@link Typeface}.
*/
public int getStyle() {
return mStyle;
diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java
index 056642841815..2cceb2c8ced9 100644
--- a/core/java/android/text/style/TabStopSpan.java
+++ b/core/java/android/text/style/TabStopSpan.java
@@ -16,39 +16,54 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.Px;
+
/**
- * Represents a single tab stop on a line.
+ * Paragraph affecting span that changes the position of the tab with respect to
+ * the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
+ * encountered on the first line of the text.
*/
-public interface TabStopSpan
-extends ParagraphStyle
-{
+public interface TabStopSpan extends ParagraphStyle {
+
/**
- * Returns the offset of the tab stop from the leading margin of the
- * line.
- * @return the offset
+ * Returns the offset of the tab stop from the leading margin of the line, in pixels.
+ *
+ * @return the offset, in pixels
*/
- public int getTabStop();
+ int getTabStop();
/**
- * The default implementation of TabStopSpan.
+ * The default implementation of TabStopSpan that allows setting the offset of the tab stop
+ * from the leading margin of the first line of text.
+ * <p>
+ * Let's consider that we have the following text: <i>"\tParagraph text beginning with tab."</i>
+ * and we want to move the tab stop with 100px. This can be achieved like this:
+ * <pre>
+ * SpannableString string = new SpannableString("\tParagraph text beginning with tab.");
+ * string.setSpan(new TabStopSpan.Standard(100), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/tabstopspan.png" />
+ * <figcaption>Text with a tab stop and a <code>TabStopSpan</code></figcaption>
*/
- public static class Standard
- implements TabStopSpan
- {
+ class Standard implements TabStopSpan {
+
+ @Px
+ private int mTabOffset;
+
/**
- * Constructor.
+ * Constructs a {@link TabStopSpan.Standard} based on an offset.
*
- * @param where the offset of the tab stop from the leading margin of
- * the line
+ * @param offset the offset of the tab stop from the leading margin of
+ * the line, in pixels
*/
- public Standard(int where) {
- mTab = where;
+ public Standard(@IntRange(from = 0) int offset) {
+ mTabOffset = offset;
}
+ @Override
public int getTabStop() {
- return mTab;
+ return mTabOffset;
}
-
- private int mTab;
}
}
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index aa622d87c74e..162281250208 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,42 +25,59 @@ import android.text.TextPaint;
import android.text.TextUtils;
/**
- * Changes the typeface family of the text to which the span is attached.
+ * Changes the typeface family of the text to which the span is attached. Examples of typeface
+ * family include "monospace", "serif", and "sans-serif".
+ * <p>
+ * For example, change the typeface of a text to "monospace" like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with typeface span");
+ * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" />
+ * <figcaption>Text with "monospace" typeface family.</figcaption>
*/
public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
+
private final String mFamily;
/**
- * @param family The font family for this typeface. Examples include
- * "monospace", "serif", and "sans-serif".
+ * Constructs a {@link TypefaceSpan} based on a font family.
+ *
+ * @param family The font family for this typeface. Examples include
+ * "monospace", "serif", and "sans-serif".
*/
public TypefaceSpan(String family) {
mFamily = family;
}
- public TypefaceSpan(Parcel src) {
+ public TypefaceSpan(@NonNull Parcel src) {
mFamily = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.TYPEFACE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mFamily);
}
@@ -71,16 +89,16 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan
}
@Override
- public void updateDrawState(TextPaint ds) {
- apply(ds, mFamily);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
@Override
- public void updateMeasureState(TextPaint paint) {
- apply(paint, mFamily);
+ public void updateMeasureState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
- private static void apply(Paint paint, String family) {
+ private static void apply(@NonNull Paint paint, String family) {
int oldStyle;
Typeface old = paint.getTypeface();
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 58239efe5c5f..eab1ef4f6afd 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -27,40 +28,71 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+/**
+ * Implementation of the {@link ClickableSpan} that allows setting a url string. When
+ * selecting and clicking on the text to which the span is attached, the <code>URLSpan</code>
+ * will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent.
+ * <p>
+ * For example, a <code>URLSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a url span");
+ * string.setSpan(new URLSpan("http://www.developer.android.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
+ * <figcaption>Text with <code>URLSpan</code>.</figcaption>
+ */
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
+ /**
+ * Constructs a {@link URLSpan} from a url string.
+ *
+ * @param url the url string
+ */
public URLSpan(String url) {
mURL = url;
}
- public URLSpan(Parcel src) {
+ /**
+ * Constructs a {@link URLSpan} from a parcel.
+ */
+ public URLSpan(@NonNull Parcel src) {
mURL = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.URL_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mURL);
}
+ /**
+ * Get the url string for this span.
+ *
+ * @return the url string.
+ */
public String getURL() {
return mURL;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 57d23ced7860..410cdc6a9bf1 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,7 +42,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_battery_v2", "true");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
- DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
}
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index 687aa8375e01..51fb18a95c4c 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -60,9 +60,19 @@ public class StatsManager {
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
- // To prevent breakages of dependencies on old API.
-
- return false;
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when adding configuration");
+ return false;
+ }
+ return service.addConfiguration(Long.parseLong(configKey), config, pkg, cls);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+ return false;
+ }
+ }
}
/**
@@ -99,7 +109,19 @@ public class StatsManager {
@RequiresPermission(Manifest.permission.DUMP)
public boolean removeConfiguration(String configKey) {
// To prevent breakages of old dependencies.
- return false;
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when removing configuration");
+ return false;
+ }
+ return service.removeConfiguration(Long.parseLong(configKey));
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+ return false;
+ }
+ }
}
/**
@@ -132,7 +154,19 @@ public class StatsManager {
public byte[] getData(String configKey) {
// TODO: remove this and all other methods with String-based config keys.
// To prevent build breakages of dependencies.
- return null;
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when getting data");
+ return null;
+ }
+ return service.getData(Long.parseLong(configKey));
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connecto statsd when getting data");
+ return null;
+ }
+ }
}
/**
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 4c6e511ede46..a3eeb275064a 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -106,18 +106,22 @@ abstract class ApkVerityBuilder {
calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
- md.update(DEFAULT_SALT);
- md.update(verityBlock);
+ md.update(header);
+ md.update(extensions);
md.update(apkDigest);
return md.digest();
}
+ /**
+ * Internal method to generate various parts of FSVerity constructs, including the header,
+ * extensions, Merkle tree, and the tree's root hash. The output buffer is flipped to the
+ * generated data size and is readey for consuming.
+ */
private static void calculateFsveritySignatureInternal(
RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput,
ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput)
throws IOException, NoSuchAlgorithmException, DigestException {
assertSigningBlockAlignedAndHasFullPages(signatureInfo);
-
long signingBlockSize =
signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
long dataSize = apk.length() - signingBlockSize - ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
@@ -128,6 +132,7 @@ abstract class ApkVerityBuilder {
levelOffset, treeOutput);
if (rootHashOutput != null) {
rootHashOutput.put(apkRootHash);
+ rootHashOutput.flip();
}
}
@@ -333,9 +338,9 @@ abstract class ApkVerityBuilder {
buffer.put((byte) 0); // auth block offset, disabled here
buffer.put((byte) 2); // extension count
buffer.put(salt); // salt (8 bytes)
- // skip(buffer, 22); // reserved
+ skip(buffer, 22); // reserved
- buffer.rewind();
+ buffer.flip();
return buffer;
}
@@ -396,12 +401,10 @@ abstract class ApkVerityBuilder {
buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE); // length
skip(buffer, 7); // reserved
buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes
-
- // There are extra kPadding bytes of 0s here, included in the total size field of the
- // extension header. The output ByteBuffer is assumed to be initialized to 0.
+ skip(buffer, kPadding); // padding
}
- buffer.rewind();
+ buffer.flip();
return buffer;
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 3fd469630db0..8cb46b704c18 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -23,6 +23,10 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -396,6 +400,33 @@ public final class PointerIcon implements Parcelable {
return true;
}
+ /**
+ * Get the Bitmap from the Drawable.
+ *
+ * If the Bitmap needed to be scaled up to account for density, BitmapDrawable
+ * handles this at draw time. But this class doesn't actually draw the Bitmap;
+ * it is just a holder for native code to access its SkBitmap. So this needs to
+ * get a version that is scaled to account for density.
+ */
+ private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) {
+ Bitmap bitmap = bitmapDrawable.getBitmap();
+ final int scaledWidth = bitmapDrawable.getIntrinsicWidth();
+ final int scaledHeight = bitmapDrawable.getIntrinsicHeight();
+ if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) {
+ return bitmap;
+ }
+
+ Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ RectF dst = new RectF(0, 0, scaledWidth, scaledHeight);
+
+ Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig());
+ Canvas canvas = new Canvas(scaled);
+ Paint paint = new Paint();
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(bitmap, src, dst, paint);
+ return scaled;
+ }
+
private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
final XmlResourceParser parser = resources.getXml(resourceId);
final int bitmapRes;
@@ -452,7 +483,8 @@ public final class PointerIcon implements Parcelable {
+ "is different. All frames should have the exact same size and "
+ "share the same hotspot.");
}
- mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap();
+ BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame;
+ mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame);
}
}
}
@@ -461,7 +493,8 @@ public final class PointerIcon implements Parcelable {
+ "refer to a bitmap drawable.");
}
- final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
validateHotSpot(bitmap, hotSpotX, hotSpotY);
// Set the properties now that we have successfully loaded the icon.
mBitmap = bitmap;
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 5070151815f5..ce7e8f3b55b5 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -353,9 +353,24 @@ public class RenderNode {
return nHasShadow(mNativeRenderNode);
}
- /** setShadowColor */
- public boolean setShadowColor(int color) {
- return nSetShadowColor(mNativeRenderNode, color);
+ /** setSpotShadowColor */
+ public boolean setSpotShadowColor(int color) {
+ return nSetSpotShadowColor(mNativeRenderNode, color);
+ }
+
+ /** setAmbientShadowColor */
+ public boolean setAmbientShadowColor(int color) {
+ return nSetAmbientShadowColor(mNativeRenderNode, color);
+ }
+
+ /** getSpotShadowColor */
+ public int getSpotShadowColor() {
+ return nGetSpotShadowColor(mNativeRenderNode);
+ }
+
+ /** getAmbientShadowColor */
+ public int getAmbientShadowColor() {
+ return nGetAmbientShadowColor(mNativeRenderNode);
}
/**
@@ -915,7 +930,13 @@ public class RenderNode {
@CriticalNative
private static native boolean nHasShadow(long renderNode);
@CriticalNative
- private static native boolean nSetShadowColor(long renderNode, int color);
+ private static native boolean nSetSpotShadowColor(long renderNode, int color);
+ @CriticalNative
+ private static native boolean nSetAmbientShadowColor(long renderNode, int color);
+ @CriticalNative
+ private static native int nGetSpotShadowColor(long renderNode);
+ @CriticalNative
+ private static native int nGetAmbientShadowColor(long renderNode);
@CriticalNative
private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
@CriticalNative
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5150c1fec8a3..c3e9e7387732 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,7 +17,6 @@
package android.view;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
-
import static java.lang.Math.max;
import android.animation.AnimatorInflater;
@@ -726,6 +725,8 @@ import java.util.function.Predicate;
* @attr ref android.R.styleable#View_nextFocusRight
* @attr ref android.R.styleable#View_nextFocusUp
* @attr ref android.R.styleable#View_onClick
+ * @attr ref android.R.styleable#View_outlineSpotShadowColor
+ * @attr ref android.R.styleable#View_outlineAmbientShadowColor
* @attr ref android.R.styleable#View_padding
* @attr ref android.R.styleable#View_paddingHorizontal
* @attr ref android.R.styleable#View_paddingVertical
@@ -3975,6 +3976,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Current clip bounds. to which all drawing of this view are constrained.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
Rect mClipBounds = null;
private boolean mLastIsOpaque;
@@ -5446,6 +5448,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setAccessibilityPaneTitle(a.getString(attr));
}
break;
+ case R.styleable.View_outlineSpotShadowColor:
+ setOutlineSpotShadowColor(a.getColor(attr, Color.BLACK));
+ break;
+ case R.styleable.View_outlineAmbientShadowColor:
+ setOutlineAmbientShadowColor(a.getColor(attr, Color.BLACK));
+ break;
}
}
@@ -15481,14 +15489,61 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * @hide
+ * Sets the color of the spot shadow that is drawn when the view has a positive Z or
+ * elevation value.
+ * <p>
+ * By default the shadow color is black. Generally, this color will be opaque so the intensity
+ * of the shadow is consistent between different views with different colors.
+ * <p>
+ * The opacity of the final spot shadow is a function of the shadow caster height, the
+ * alpha channel of the outlineSpotShadowColor (typically opaque), and the
+ * {@link android.R.attr#spotShadowAlpha} theme attribute.
+ *
+ * @attr ref android.R.styleable#View_outlineSpotShadowColor
+ * @param color The color this View will cast for its elevation spot shadow.
*/
- public void setShadowColor(@ColorInt int color) {
- if (mRenderNode.setShadowColor(color)) {
+ public void setOutlineSpotShadowColor(@ColorInt int color) {
+ if (mRenderNode.setSpotShadowColor(color)) {
invalidateViewProperty(true, true);
}
}
+ /**
+ * @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing
+ * was set
+ */
+ public @ColorInt int getOutlineSpotShadowColor() {
+ return mRenderNode.getSpotShadowColor();
+ }
+
+ /**
+ * Sets the color of the ambient shadow that is drawn when the view has a positive Z or
+ * elevation value.
+ * <p>
+ * By default the shadow color is black. Generally, this color will be opaque so the intensity
+ * of the shadow is consistent between different views with different colors.
+ * <p>
+ * The opacity of the final ambient shadow is a function of the shadow caster height, the
+ * alpha channel of the outlineAmbientShadowColor (typically opaque), and the
+ * {@link android.R.attr#ambientShadowAlpha} theme attribute.
+ *
+ * @attr ref android.R.styleable#View_outlineAmbientShadowColor
+ * @param color The color this View will cast for its elevation shadow.
+ */
+ public void setOutlineAmbientShadowColor(@ColorInt int color) {
+ if (mRenderNode.setAmbientShadowColor(color)) {
+ invalidateViewProperty(true, true);
+ }
+ }
+
+ /**
+ * @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if
+ * nothing was set
+ */
+ public @ColorInt int getOutlineAmbientShadowColor() {
+ return mRenderNode.getAmbientShadowColor();
+ }
+
/** @hide */
public void setRevealClip(boolean shouldClip, float x, float y, float radius) {
@@ -23634,9 +23689,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
Point shadowTouchPoint = new Point();
shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
- if ((shadowSize.x <= 0) || (shadowSize.y <= 0)
+ if ((shadowSize.x < 0) || (shadowSize.y < 0)
|| (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) {
- throw new IllegalStateException("Drag shadow dimensions must be positive");
+ throw new IllegalStateException("Drag shadow dimensions must not be negative");
+ }
+
+ // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
+ // does not accept zero size surface.
+ if (shadowSize.x == 0 || shadowSize.y == 0) {
+ shadowSize.x = 1;
+ shadowSize.y = 1;
}
if (ViewDebug.DEBUG_DRAG) {
@@ -26979,6 +27041,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
stream.addProperty("drawing:scaleY", getScaleY());
stream.addProperty("drawing:pivotX", getPivotX());
stream.addProperty("drawing:pivotY", getPivotY());
+ stream.addProperty("drawing:clipBounds",
+ mClipBounds == null ? null : mClipBounds.toString());
stream.addProperty("drawing:opaque", isOpaque());
stream.addProperty("drawing:alpha", getAlpha());
stream.addProperty("drawing:transitionAlpha", getTransitionAlpha());
@@ -26990,6 +27054,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing());
stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled());
stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering());
+ stream.addProperty("drawing:outlineAmbientShadowColor", getOutlineAmbientShadowColor());
+ stream.addProperty("drawing:outlineSpotShadowColor", getOutlineSpotShadowColor());
// focus
stream.addProperty("focus:hasFocus", hasFocus());
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7db5c3207296..33c9f7a2671e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
@@ -2137,6 +2138,26 @@ public final class InputMethodManager {
}
/**
+ * A test API for CTS to make sure that {@link #showInputMethodPicker()} works as expected.
+ *
+ * <p>When customizing the implementation of {@link #showInputMethodPicker()} API, make sure
+ * that this test API returns when and only while and only while
+ * {@link #showInputMethodPicker()} is showing UI. Otherwise your OS implementation may not
+ * pass CTS.</p>
+ *
+ * @return {@code true} while and only while {@link #showInputMethodPicker()} is showing UI.
+ * @hide
+ */
+ @TestApi
+ public boolean isInputMethodPickerShown() {
+ try {
+ return mService.isInputMethodPickerShownForTest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Show the settings for enabling subtypes of the specified input method.
* @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
* subtypes of all input methods will be shown.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index b03c70d19199..9f389ba0c140 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -73,7 +73,7 @@ public final class TextClassifierImpl implements TextClassifier {
private static final String LOG_TAG = DEFAULT_LOG_TAG;
private static final String MODEL_DIR = "/etc/textclassifier/";
- private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model";
+ private static final String MODEL_FILE_REGEX = "textclassifier\\.(.*)\\.model";
private static final String UPDATED_MODEL_FILE_PATH =
"/data/misc/textclassifier/textclassifier.model";
private static final List<String> ENTITY_TYPES_ALL =
@@ -232,6 +232,7 @@ public final class TextClassifierImpl implements TextClassifier {
}
builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores);
}
+ return builder.build();
} catch (Throwable t) {
// Avoid throwing from this method. Log the error.
Log.e(LOG_TAG, "Error getting links info.", t);
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 670efdd286e9..ede52119390a 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -23,13 +23,13 @@ import android.annotation.Nullable;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
-import android.widget.TextView;
import android.text.Spannable;
import android.text.style.ClickableSpan;
import android.text.util.Linkify;
import android.text.util.Linkify.LinkifyMask;
import android.view.View;
import android.view.textclassifier.TextClassifier.EntityType;
+import android.widget.TextView;
import com.android.internal.util.Preconditions;
@@ -351,7 +351,7 @@ public final class TextLinks implements Parcelable {
* @throws IllegalArgumentException if applyStrategy is not valid
*
* @see #APPLY_STRATEGY_IGNORE
- * @see #APPLY_STRAGETY_REPLACE
+ * @see #APPLY_STRATEGY_REPLACE
*/
public Options setApplyStrategy(@ApplyStrategy int applyStrategy) {
checkValidApplyStrategy(applyStrategy);
@@ -391,8 +391,8 @@ public final class TextLinks implements Parcelable {
* @return the strategy for resolving conflictswhen applying generated links to text that
* already have links.
*
- * @see #APPLY_STATEGY_IGNORE
- * @see #APPLY_STRAGETY_REPLACE
+ * @see #APPLY_STRATEGY_IGNORE
+ * @see #APPLY_STRATEGY_REPLACE
*/
@ApplyStrategy
public int getApplyStrategy() {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 1dc5b44bed4f..4b951fa1824e 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -23,10 +23,12 @@ import android.annotation.TestApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -53,7 +55,6 @@ import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
import java.io.IOException;
-import java.io.InputStream;
/**
* Displays image resources, for example {@link android.graphics.Bitmap}
@@ -946,21 +947,15 @@ public class ImageView extends View {
}
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
|| ContentResolver.SCHEME_FILE.equals(scheme)) {
- InputStream stream = null;
try {
- stream = mContext.getContentResolver().openInputStream(uri);
- return Drawable.createFromResourceStream(sCompatUseCorrectStreamDensity
- ? getResources() : null, null, stream, null);
- } catch (Exception e) {
+ Resources res = sCompatUseCorrectStreamDensity ? getResources() : null;
+ ImageDecoder.Source src = ImageDecoder.createSource(mContext.getContentResolver(),
+ uri, res);
+ return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException e) {
Log.w(LOG_TAG, "Unable to open content: " + uri, e);
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- Log.w(LOG_TAG, "Unable to close content: " + uri, e);
- }
- }
}
} else {
return Drawable.createFromPath(uri.toString());
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 2e4cccfc230f..e58e62f972c6 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -19,12 +19,11 @@ package android.widget;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.session.MediaController;
import android.media.update.ApiLoader;
-import android.media.update.FrameLayoutHelper;
import android.media.update.MediaControlView2Provider;
+import android.media.update.ViewGroupHelper;
import android.util.AttributeSet;
import java.lang.annotation.Retention;
@@ -62,7 +61,7 @@ import java.lang.annotation.RetentionPolicy;
* TODO PUBLIC API
* @hide
*/
-public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provider> {
+public class MediaControlView2 extends ViewGroupHelper<MediaControlView2Provider> {
/** @hide */
@IntDef({
BUTTON_PLAY_PAUSE,
@@ -156,18 +155,12 @@ public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provid
public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
- super((instance, superProvider) ->
+ super((instance, superProvider, privateProvider) ->
ApiLoader.getProvider(context).createMediaControlView2(
- (MediaControlView2) instance, superProvider),
+ (MediaControlView2) instance, superProvider, privateProvider,
+ attrs, defStyleAttr, defStyleRes),
context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * @hide
- */
- @SystemApi
- public MediaControlView2Provider getProvider() {
- return mProvider;
+ mProvider.initialize(attrs, defStyleAttr, defStyleRes);
}
/**
@@ -178,13 +171,6 @@ public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provid
}
/**
- * Returns whether the control view is currently shown or hidden.
- */
- public boolean isShowing() {
- return mProvider.isShowing_impl();
- }
-
- /**
* Changes the visibility state of an individual button. Default value is View.Visible.
*
* @param button the {@code Button} assigned to individual buttons
@@ -232,9 +218,7 @@ public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provid
}
@Override
- // TODO Move this method to ViewProvider
- public void onVisibilityAggregated(boolean isVisible) {
-
- mProvider.onVisibilityAggregated_impl(isVisible);
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mProvider.onLayout_impl(changed, l, t, r, b);
}
}
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 78ca0114b798..30811803bf9a 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -27,8 +27,8 @@ import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.media.update.ApiLoader;
-import android.media.update.FrameLayoutHelper;
import android.media.update.VideoView2Provider;
+import android.media.update.ViewGroupHelper;
import android.net.Uri;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -101,7 +101,7 @@ import java.util.concurrent.Executor;
*
* @hide
*/
-public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> {
+public class VideoView2 extends ViewGroupHelper<VideoView2Provider> {
/** @hide */
@IntDef({
VIEW_TYPE_TEXTUREVIEW,
@@ -139,10 +139,12 @@ public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> {
public VideoView2(
@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
- super((instance, superProvider) ->
+ super((instance, superProvider, privateProvider) ->
ApiLoader.getProvider(context).createVideoView2(
- (VideoView2) instance, superProvider, attrs, defStyleAttr, defStyleRes),
+ (VideoView2) instance, superProvider, privateProvider,
+ attrs, defStyleAttr, defStyleRes),
context, attrs, defStyleAttr, defStyleRes);
+ mProvider.initialize(attrs, defStyleAttr, defStyleRes);
}
/**
@@ -487,4 +489,9 @@ public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> {
*/
void onCustomAction(String action, Bundle extras);
}
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mProvider.onLayout_impl(changed, l, t, r, b);
+ }
}
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
index bb75bf6e6fb8..fa8db96781ac 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -31,7 +31,9 @@ public class AlsaCardsParser {
private static final String TAG = "AlsaCardsParser";
protected static final boolean DEBUG = false;
- private static final String kCardsFilePath = "/proc/asound/cards";
+ private static final String kAlsaFolderPath = "/proc/asound";
+ private static final String kCardsFilePath = kAlsaFolderPath + "/cards";
+ private static final String kDeviceAddressPrefix = "/dev/bus/usb/";
private static LineTokenizer mTokenizer = new LineTokenizer(" :[]");
@@ -47,14 +49,31 @@ public class AlsaCardsParser {
private static final String TAG = "AlsaCardRecord";
private static final String kUsbCardKeyStr = "at usb-";
- public int mCardNum = -1;
- public String mField1 = "";
- public String mCardName = "";
- public String mCardDescription = "";
- public boolean mIsUsb = false;
+ int mCardNum = -1;
+ String mField1 = "";
+ String mCardName = "";
+ String mCardDescription = "";
+
+ private String mUsbDeviceAddress = null;
public AlsaCardRecord() {}
+ public int getCardNum() {
+ return mCardNum;
+ }
+
+ public String getCardName() {
+ return mCardName;
+ }
+
+ public String getCardDescription() {
+ return mCardDescription;
+ }
+
+ public void setDeviceAddress(String usbDeviceAddress) {
+ mUsbDeviceAddress = usbDeviceAddress;
+ }
+
private boolean parse(String line, int lineIndex) {
int tokenIndex = 0;
int delimIndex = 0;
@@ -87,8 +106,8 @@ public class AlsaCardsParser {
tokenIndex = mTokenizer.nextToken(line, 0);
if (tokenIndex != -1) {
int keyIndex = line.indexOf(kUsbCardKeyStr);
- mIsUsb = keyIndex != -1;
- if (mIsUsb) {
+ boolean isUsb = keyIndex != -1;
+ if (isUsb) {
mCardDescription = line.substring(tokenIndex, keyIndex - 1);
}
}
@@ -97,14 +116,18 @@ public class AlsaCardsParser {
return true;
}
+ boolean isUsb() {
+ return mUsbDeviceAddress != null;
+ }
+
public String textFormat() {
- return mCardName + " : " + mCardDescription;
+ return mCardName + " : " + mCardDescription + " [addr:" + mUsbDeviceAddress + "]";
}
public void log(int listIndex) {
Slog.d(TAG, "" + listIndex +
" [" + mCardNum + " " + mCardName + " : " + mCardDescription +
- " usb:" + mIsUsb);
+ " usb:" + isUsb());
}
}
@@ -112,7 +135,7 @@ public class AlsaCardsParser {
public int scan() {
if (DEBUG) {
- Slog.i(TAG, "AlsaCardsParser.scan()....");
+ Slog.d(TAG, "AlsaCardsParser.scan()....");
}
mCardRecords = new ArrayList<AlsaCardRecord>();
@@ -125,7 +148,7 @@ public class AlsaCardsParser {
while ((line = bufferedReader.readLine()) != null) {
AlsaCardRecord cardRecord = new AlsaCardRecord();
if (DEBUG) {
- Slog.i(TAG, " " + line);
+ Slog.d(TAG, " " + line);
}
cardRecord.parse(line, 0);
@@ -134,10 +157,23 @@ public class AlsaCardsParser {
break;
}
if (DEBUG) {
- Slog.i(TAG, " " + line);
+ Slog.d(TAG, " " + line);
}
cardRecord.parse(line, 1);
+ // scan "usbbus" file
+ int cardNum = cardRecord.mCardNum;
+ String cardFolderPath = kAlsaFolderPath + "/card" + cardNum;
+ File usbbusFile = new File(cardFolderPath + "/usbbus");
+ if (usbbusFile.exists()) {
+ // read it in
+ FileReader usbbusReader = new FileReader(usbbusFile);
+ String deviceAddress = (new BufferedReader(usbbusReader)).readLine();
+ if (deviceAddress != null) {
+ cardRecord.setDeviceAddress(kDeviceAddressPrefix + deviceAddress);
+ }
+ usbbusReader.close();
+ }
mCardRecords.add(cardRecord);
}
reader.close();
@@ -147,14 +183,12 @@ public class AlsaCardsParser {
mScanStatus = SCANSTATUS_EMPTY;
}
} catch (FileNotFoundException e) {
- e.printStackTrace();
mScanStatus = SCANSTATUS_FAIL;
} catch (IOException e) {
- e.printStackTrace();
mScanStatus = SCANSTATUS_FAIL;
}
if (DEBUG) {
- Slog.i(TAG, " status:" + mScanStatus);
+ Slog.d(TAG, " status:" + mScanStatus);
}
return mScanStatus;
}
@@ -163,142 +197,33 @@ public class AlsaCardsParser {
return mScanStatus;
}
- public ArrayList<AlsaCardRecord> getScanRecords() {
- return mCardRecords;
- }
-
- public AlsaCardRecord getCardRecordAt(int index) {
- return mCardRecords.get(index);
- }
-
- public AlsaCardRecord getCardRecordFor(int cardNum) {
- for (AlsaCardRecord rec : mCardRecords) {
- if (rec.mCardNum == cardNum) {
- return rec;
+ public AlsaCardRecord findCardNumFor(String deviceAddress) {
+ for(AlsaCardRecord cardRec : mCardRecords) {
+ if (cardRec.isUsb() && cardRec.mUsbDeviceAddress.equals(deviceAddress)) {
+ return cardRec;
}
}
-
return null;
}
- public int getNumCardRecords() {
- return mCardRecords.size();
- }
-
- public boolean isCardUsb(int cardNum) {
- for (AlsaCardRecord rec : mCardRecords) {
- if (rec.mCardNum == cardNum) {
- return rec.mIsUsb;
- }
- }
-
- return false;
- }
-
- // return -1 if none found
- public int getDefaultUsbCard() {
- // save the current list of devices
- ArrayList<AlsaCardsParser.AlsaCardRecord> prevRecs = mCardRecords;
- if (DEBUG) {
- LogDevices("Previous Devices:", prevRecs);
- }
-
- // get the new list of devices
- if (scan() != SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error scanning Alsa cards file.");
- return -1;
- }
-
- if (DEBUG) {
- LogDevices("Current Devices:", mCardRecords);
- }
-
- // Calculate the difference between the old and new device list
- ArrayList<AlsaCardRecord> newRecs = getNewCardRecords(prevRecs);
- if (DEBUG) {
- LogDevices("New Devices:", newRecs);
- }
-
- // Choose the most-recently added EXTERNAL card
- // Check recently added devices
- for (AlsaCardRecord rec : newRecs) {
- if (DEBUG) {
- Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
- }
- if (rec.mIsUsb) {
- // Found it
- return rec.mCardNum;
- }
- }
-
- // or return the first added EXTERNAL card?
- for (AlsaCardRecord rec : prevRecs) {
- if (DEBUG) {
- Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
- }
- if (rec.mIsUsb) {
- return rec.mCardNum;
- }
- }
-
- return -1;
- }
-
- public int getDefaultCard() {
- // return an external card if possible
- int card = getDefaultUsbCard();
- if (DEBUG) {
- Slog.d(TAG, "getDefaultCard() default usb card:" + card);
- }
-
- if (card < 0 && getNumCardRecords() > 0) {
- // otherwise return the (internal) card with the highest number
- card = getCardRecordAt(getNumCardRecords() - 1).mCardNum;
- }
- if (DEBUG) {
- Slog.d(TAG, " returns card:" + card);
- }
- return card;
- }
-
- static public boolean hasCardNumber(ArrayList<AlsaCardRecord> recs, int cardNum) {
- for (AlsaCardRecord cardRec : recs) {
- if (cardRec.mCardNum == cardNum) {
- return true;
- }
- }
- return false;
- }
-
- public ArrayList<AlsaCardRecord> getNewCardRecords(ArrayList<AlsaCardRecord> prevScanRecs) {
- ArrayList<AlsaCardRecord> newRecs = new ArrayList<AlsaCardRecord>();
- for (AlsaCardRecord rec : mCardRecords) {
- // now scan to see if this card number is in the previous scan list
- if (!hasCardNumber(prevScanRecs, rec.mCardNum)) {
- newRecs.add(rec);
- }
- }
- return newRecs;
- }
-
//
// Logging
//
private void Log(String heading) {
if (DEBUG) {
- Slog.i(TAG, heading);
+ Slog.d(TAG, heading);
for (AlsaCardRecord cardRec : mCardRecords) {
- Slog.i(TAG, cardRec.textFormat());
+ Slog.d(TAG, cardRec.textFormat());
}
}
}
- static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
- Slog.d(TAG, caption + " ----------------");
- int listIndex = 0;
- for (AlsaCardRecord device : deviceList) {
- device.log(listIndex++);
- }
- Slog.d(TAG, "----------------");
- }
+// static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
+// Slog.d(TAG, caption + " ----------------");
+// int listIndex = 0;
+// for (AlsaCardRecord device : deviceList) {
+// device.log(listIndex++);
+// }
+// Slog.d(TAG, caption + "----------------");
+// }
}
diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
index 15261bafd299..8d92d016be24 100644
--- a/core/java/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
@@ -27,6 +27,9 @@ import java.util.ArrayList;
* @hide
* Retrieves information from an ALSA "devices" file.
*/
+/*
+ * NOTE: This class is currently not being used, but may be needed in the future.
+ */
public class AlsaDevicesParser {
private static final String TAG = "AlsaDevicesParser";
protected static final boolean DEBUG = false;
@@ -207,12 +210,6 @@ public class AlsaDevicesParser {
//
// Predicates
//
-/*
- public boolean hasPlaybackDevices() {
- return mHasPlaybackDevices;
- }
-*/
-
public boolean hasPlaybackDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -224,12 +221,6 @@ public class AlsaDevicesParser {
return false;
}
-/*
- public boolean hasCaptureDevices() {
- return mHasCaptureDevices;
- }
-*/
-
public boolean hasCaptureDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -241,12 +232,6 @@ public class AlsaDevicesParser {
return false;
}
-/*
- public boolean hasMIDIDevices() {
- return mHasMIDIDevices;
- }
-*/
-
public boolean hasMIDIDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -280,6 +265,7 @@ public class AlsaDevicesParser {
if (isLineDeviceRecord(line)) {
AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord();
deviceRecord.parse(line);
+ Slog.i(TAG, deviceRecord.textFormat());
mDeviceRecords.add(deviceRecord);
}
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 2ab2d20ed20d..1dfff5efcab5 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -103,11 +103,14 @@ public class ResolverListController {
List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null;
for (int i = 0, N = intents.size(); i < N; i++) {
final Intent intent = intents.get(i);
- final List<ResolveInfo> infos = mpm.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY
- | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
- | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0)
- | PackageManager.MATCH_INSTANT);
+ int flags = PackageManager.MATCH_DEFAULT_ONLY
+ | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
+ | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0);
+ if (intent.isBrowsableWebIntent()
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
+ flags |= PackageManager.MATCH_INSTANT;
+ }
+ final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags);
// Remove any activities that are not exported.
int totalSize = infos.size();
for (int j = totalSize - 1; j >= 0 ; j--) {
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 902c8c1662ea..6f7695ce8c34 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -21,24 +21,15 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
import android.view.Window;
-import android.widget.TextView;
import com.android.internal.R;
@@ -85,14 +76,9 @@ public class UnlaunchableAppActivity extends Activity
return;
}
- View rootView = LayoutInflater.from(this).inflate(R.layout.unlaunchable_app_activity, null);
- TextView titleView = (TextView)rootView.findViewById(R.id.unlaunchable_app_title);
- TextView messageView = (TextView)rootView.findViewById(R.id.unlaunchable_app_message);
- titleView.setText(dialogTitle);
- messageView.setText(dialogMessage);
-
AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setView(rootView)
+ .setTitle(dialogTitle)
+ .setMessage(dialogMessage)
.setOnDismissListener(this);
if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) {
builder.setPositiveButton(R.string.work_mode_turn_on, this)
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7def87655ae3..40dcf25bbd10 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3880,7 +3880,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
mIsolatedUids.put(isolatedUid, appUid);
- StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
+ StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+ StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
final Uid u = getUidStatsLocked(appUid);
u.addIsolatedUid(isolatedUid);
}
@@ -3904,7 +3905,8 @@ public class BatteryStatsImpl extends BatteryStats {
*/
public void removeIsolatedUidLocked(int isolatedUid) {
StatsLog.write(
- StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+ StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
+ isolatedUid, StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
final int idx = mIsolatedUids.indexOfKey(isolatedUid);
if (idx >= 0) {
final int ownerUid = mIsolatedUids.valueAt(idx);
@@ -4255,10 +4257,12 @@ public class BatteryStatsImpl extends BatteryStats {
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
}
@@ -4298,10 +4302,12 @@ public class BatteryStatsImpl extends BatteryStats {
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
}
}
@@ -4426,7 +4432,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 1);
+ uid, null, name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4439,7 +4446,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 1);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4450,7 +4458,8 @@ public class BatteryStatsImpl extends BatteryStats {
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 1);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -4470,8 +4479,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
- StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, null,
+ name, historyName, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4484,7 +4493,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 0);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4494,7 +4504,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 0);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -4518,7 +4529,8 @@ public class BatteryStatsImpl extends BatteryStats {
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptime * 1000, 1); // time in in microseconds
- StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000);
+ StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
+ /* duration_usec */ deltaUptime * 1000);
mLastWakeupReason = null;
}
}
@@ -4659,10 +4671,12 @@ public class BatteryStatsImpl extends BatteryStats {
mGpsNesting++;
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
}
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -4683,10 +4697,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 0);
+ workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
@@ -4941,7 +4956,9 @@ public class BatteryStatsImpl extends BatteryStats {
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
- StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0);
+ StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ?
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON :
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -5545,16 +5562,19 @@ public class BatteryStatsImpl extends BatteryStats {
if (workChain != null) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 1);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
}
@@ -5594,16 +5614,19 @@ public class BatteryStatsImpl extends BatteryStats {
if (workChain != null) {
StatsLog.write(
- StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0);
+ StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 0);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
}
@@ -5656,7 +5679,8 @@ public class BatteryStatsImpl extends BatteryStats {
for (int j = 0; j < allWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
allWorkChains.get(j).getUids(),
- allWorkChains.get(j).getTags(), 0);
+ allWorkChains.get(j).getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
}
allWorkChains.clear();
}
@@ -5666,7 +5690,8 @@ public class BatteryStatsImpl extends BatteryStats {
for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
unoptimizedWorkChains.get(j).getUids(),
- unoptimizedWorkChains.get(j).getTags(), 0);
+ unoptimizedWorkChains.get(j).getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
unoptimizedWorkChains.clear();
}
@@ -6011,7 +6036,8 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6021,7 +6047,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockAcquiredLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6031,7 +6058,8 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6041,7 +6069,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockReleasedLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -6052,7 +6081,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 1);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6062,7 +6091,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 1);
+ workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6073,7 +6102,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 0);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6083,7 +6112,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7035,7 +7065,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
}
}
@@ -7045,7 +7076,8 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiMulticastEnabled = false;
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
}
}
@@ -7098,14 +7130,16 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7113,7 +7147,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
@@ -7127,7 +7162,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7135,7 +7171,7 @@ public class BatteryStatsImpl extends BatteryStats {
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
- null, 0);
+ null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
}
@@ -7144,7 +7180,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
- 0);
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
@@ -7158,7 +7194,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7166,7 +7203,7 @@ public class BatteryStatsImpl extends BatteryStats {
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
- 0);
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7174,7 +7211,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
@@ -7188,14 +7226,16 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7203,7 +7243,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
@@ -9777,7 +9818,8 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__ON);
}
}
@@ -9786,7 +9828,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -9796,7 +9839,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 1);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED);
}
}
@@ -9806,7 +9849,7 @@ public class BatteryStatsImpl extends BatteryStats {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 0);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED);
}
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9919,7 +9962,7 @@ public class BatteryStatsImpl extends BatteryStats {
t.startRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
- 1);
+ StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
}
}
@@ -9930,7 +9973,7 @@ public class BatteryStatsImpl extends BatteryStats {
t.stopRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
- sensor, 0);
+ sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
}
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index f9a2341fc3e3..e69a36064693 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,8 @@ public final class Zygote {
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
/** Do not enfore hidden API access restrictions. */
public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Force generation of native debugging information for backtraces. */
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 39279b5055b1..74802c8bdf79 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -583,7 +583,7 @@ public class ZygoteInit {
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion, /*profileName*/ null);
+ targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null);
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 95bc352747d2..eadefc919934 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -86,6 +86,7 @@ import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
+import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -2216,7 +2217,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
elevation = dipToPx(elevation);
mElevationAdjustedForStack = true;
} else if (windowingMode == WINDOWING_MODE_PINNED) {
- elevation = dipToPx(DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP);
+ elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
mElevationAdjustedForStack = true;
} else {
mElevationAdjustedForStack = false;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ebb5f9f9e446..f70d3c2a27fd 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -132,6 +132,12 @@ oneway interface IStatusBar
void clickQsTile(in ComponentName tile);
void handleSystemKey(in int key);
+ /**
+ * Methods to show toast messages for screen pinning
+ */
+ void showPinningEnterExitToast(boolean entering);
+ void showPinningEscapeToast();
+
void showShutdownUi(boolean isReboot, String reason);
// Used to show the dialog when FingerprintService starts authentication
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index cb0b53c495dc..adf42878ebb3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -81,6 +81,12 @@ interface IStatusBarService
void clickTile(in ComponentName tile);
void handleSystemKey(in int key);
+ /**
+ * Methods to show toast messages for screen pinning
+ */
+ void showPinningEnterExitToast(boolean entering);
+ void showPinningEscapeToast();
+
// Used to show the dialog when FingerprintService starts authentication
void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver);
// Used to hide the dialog when a finger is authenticated
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 71550be1c8d7..f8885a20970d 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -160,7 +160,7 @@ public class FileRotator {
final File file = new File(mBasePath, name);
final FileInputStream is = new FileInputStream(file);
try {
- Streams.copy(is, zos);
+ FileUtils.copy(is, zos);
} finally {
IoUtils.closeQuietly(is);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5e0a986b432b..02822869f421 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -68,6 +68,7 @@ interface IInputMethodManager {
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
+ boolean isInputMethodPickerShownForTest();
void setInputMethod(in IBinder token, String id);
void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype);
void hideMySoftInput(in IBinder token, int flags);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 5673814ca362..732534ccbcbe 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -66,6 +66,8 @@ interface ILockSettings {
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
KeyChainSnapshot getKeyChainSnapshot();
byte[] generateAndStoreKey(String alias);
+ String generateKey(String alias, in byte[] account);
+ String getKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
Map getRecoverySnapshotVersions();
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 32a7a2dadddb..7a248f264274 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -118,6 +118,7 @@ public class LockPatternView extends View {
private float mInProgressY = -1;
private long mAnimatingPeriodStart;
+ private long[] mLineFadeStart = new long[9];
private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
private boolean mInputEnabled = true;
@@ -596,12 +597,14 @@ public class LockPatternView extends View {
}
/**
- * Clear the pattern lookup table.
+ * Clear the pattern lookup table. Also reset the line fade start times for
+ * the next attempt.
*/
private void clearPatternDrawLookup() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
mPatternDrawLookup[i][j] = false;
+ mLineFadeStart[i+j] = 0;
}
}
}
@@ -1136,7 +1139,8 @@ public class LockPatternView extends View {
boolean anyCircles = false;
float lastX = 0f;
float lastY = 0f;
- for (int i = 0; i < count; i++) {
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < count; i++) {
Cell cell = pattern.get(i);
// only draw the part of the pattern stored in
@@ -1147,16 +1151,26 @@ public class LockPatternView extends View {
}
anyCircles = true;
+ if (mLineFadeStart[i] == 0) {
+ mLineFadeStart[i] = SystemClock.elapsedRealtime();
+ }
+
float centerX = getCenterXForColumn(cell.column);
float centerY = getCenterYForRow(cell.row);
if (i != 0) {
+ // Set this line segment to slowly fade over the next second.
+ int lineFadeVal = (int) Math.min((elapsedRealtime -
+ mLineFadeStart[i])/2f, 255f);
+
CellState state = mCellStates[cell.row][cell.column];
currentPath.rewind();
currentPath.moveTo(lastX, lastY);
if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) {
currentPath.lineTo(state.lineEndX, state.lineEndY);
+ mPathPaint.setAlpha((int) 255 - lineFadeVal );
} else {
currentPath.lineTo(centerX, centerY);
+ mPathPaint.setAlpha((int) 255 - lineFadeVal );
}
canvas.drawPath(currentPath, mPathPaint);
}
diff --git a/core/java/com/android/server/backup/SliceBackupHelper.java b/core/java/com/android/server/backup/SliceBackupHelper.java
new file mode 100644
index 000000000000..8e5a5eecec89
--- /dev/null
+++ b/core/java/com/android/server/backup/SliceBackupHelper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.app.backup.BlobBackupHelper;
+import android.app.slice.ISliceManager;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+public class SliceBackupHelper extends BlobBackupHelper {
+ static final String TAG = "SliceBackupHelper";
+ static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ // Current version of the blob schema
+ static final int BLOB_VERSION = 1;
+
+ // Key under which the payload blob is stored
+ static final String KEY_SLICES = "slices";
+
+ public SliceBackupHelper(Context context) {
+ super(BLOB_VERSION, KEY_SLICES);
+ // context is currently unused
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ byte[] newPayload = null;
+ if (KEY_SLICES.equals(key)) {
+ try {
+ ISliceManager sm = ISliceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SLICE_SERVICE));
+ // TODO: http://b/22388012
+ newPayload = sm.getBackupPayload(UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ // Treat as no data
+ Slog.e(TAG, "Couldn't communicate with slice manager");
+ newPayload = null;
+ }
+ }
+ return newPayload;
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ if (DEBUG) Slog.v(TAG, "Got restore of " + key);
+
+ if (KEY_SLICES.equals(key)) {
+ try {
+ ISliceManager sm = ISliceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SLICE_SERVICE));
+ // TODO: http://b/22388012
+ sm.applyRestore(payload, UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ Slog.e(TAG, "Couldn't communicate with slice manager");
+ }
+ }
+ }
+
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index a96b5dd3ed70..47e7a0e74563 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -51,6 +51,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final String USAGE_STATS_HELPER = "usage_stats";
private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager";
private static final String ACCOUNT_MANAGER_HELPER = "account_manager";
+ private static final String SLICES_HELPER = "slices";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -88,6 +89,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+ addHelper(SLICES_HELPER, new SliceBackupHelper(this));
super.onBackup(oldState, data, newState);
}
@@ -116,6 +118,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+ addHelper(SLICES_HELPER, new SliceBackupHelper(this));
super.onRestore(data, appVersionCode, newState);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 93f95c634ae3..33f80ce8ffb0 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -164,6 +164,7 @@ cc_library_shared {
"android_media_DeviceCallback.cpp",
"android_media_JetPlayer.cpp",
"android_media_MediaMetricsJNI.cpp",
+ "android_media_MicrophoneInfo.cpp",
"android_media_RemoteDisplay.cpp",
"android_media_ToneGenerator.cpp",
"android_hardware_Camera.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index bf7a7794fcc6..d20217386b1e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -104,6 +104,7 @@ extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
extern int register_android_media_AudioTrack(JNIEnv *env);
+extern int register_android_media_MicrophoneInfo(JNIEnv *env);
extern int register_android_media_JetPlayer(JNIEnv *env);
extern int register_android_media_ToneGenerator(JNIEnv *env);
@@ -765,18 +766,17 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
/*
* Enable debugging only for apps forked from zygote.
- * Set suspend=y to pause during VM init and use android ADB transport.
*/
if (zygote) {
+ // Set the JDWP provider and required arguments. By default let the runtime choose how JDWP is
+ // implemented. When this is not set the runtime defaults to not allowing JDWP.
addOption("-XjdwpOptions:suspend=n,server=y");
+ parseRuntimeOption("dalvik.vm.jdwp-provider",
+ jdwpProviderBuf,
+ "-XjdwpProvider:",
+ "default");
}
- // Set the JDWP provider. By default let the runtime choose.
- parseRuntimeOption("dalvik.vm.jdwp-provider",
- jdwpProviderBuf,
- "-XjdwpProvider:",
- "default");
-
parseRuntimeOption("dalvik.vm.lockprof.threshold",
lockProfThresholdBuf,
"-Xlockprofthreshold:");
@@ -1461,6 +1461,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioTrack),
REG_JNI(register_android_media_JetPlayer),
+ REG_JNI(register_android_media_MicrophoneInfo),
REG_JNI(register_android_media_RemoteDisplay),
REG_JNI(register_android_media_ToneGenerator),
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index c88cf5cff04d..ba56d592acef 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -26,10 +26,11 @@
#include <SkPictureRecorder.h>
#include <hwui/AnimatedImageDrawable.h>
#include <hwui/Canvas.h>
+#include <utils/Looper.h>
using namespace android;
-static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID;
+static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
@@ -123,9 +124,9 @@ static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlo
return drawable->start();
}
-static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->stop();
+ return drawable->stop();
}
// Java's LOOP_INFINITE relies on this being the same.
@@ -137,33 +138,63 @@ static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/,
drawable->setRepetitionCount(loopCount);
}
-class JniAnimationEndListener : public OnAnimationEndListener {
+class InvokeListener : public MessageHandler {
public:
- JniAnimationEndListener(JNIEnv* env, jobject javaObject) {
+ InvokeListener(JNIEnv* env, jobject javaObject) {
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
- mJavaObject = env->NewGlobalRef(javaObject);
+ // Hold a weak reference to break a cycle that would prevent GC.
+ mWeakRef = env->NewWeakGlobalRef(javaObject);
}
- ~JniAnimationEndListener() override {
+ ~InvokeListener() override {
auto* env = get_env_or_die(mJvm);
- env->DeleteGlobalRef(mJavaObject);
+ env->DeleteWeakGlobalRef(mWeakRef);
}
- void onAnimationEnd() override {
+ virtual void handleMessage(const Message&) override {
auto* env = get_env_or_die(mJvm);
- env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID);
+ jobject localRef = env->NewLocalRef(mWeakRef);
+ if (localRef) {
+ env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
+ }
}
private:
JavaVM* mJvm;
- jobject mJavaObject;
+ jweak mWeakRef;
+};
+
+class JniAnimationEndListener : public OnAnimationEndListener {
+public:
+ JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
+ mListener = new InvokeListener(env, javaObject);
+ mLooper = std::move(looper);
+ }
+
+ void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
+
+private:
+ sp<InvokeListener> mListener;
+ sp<Looper> mLooper;
};
static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
jlong nativePtr, jobject jdrawable) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>(
- new JniAnimationEndListener(env, jdrawable)));
+ if (!jdrawable) {
+ drawable->setOnAnimationEndListener(nullptr);
+ } else {
+ sp<Looper> looper = Looper::getForThread();
+ if (!looper.get()) {
+ doThrowISE(env,
+ "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
+ "looper!");
+ return;
+ }
+
+ drawable->setOnAnimationEndListener(
+ std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
+ }
}
static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
@@ -186,7 +217,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
{ "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
{ "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
{ "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
- { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop },
+ { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
{ "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount },
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
@@ -195,7 +226,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
- gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V");
+ gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dd3e6f02e9fe..937b3ffb9d60 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,11 +44,9 @@ namespace android {
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
- : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
- allowUnsupportedFont(false) {}
+ : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)) {}
uint32_t langId;
minikin::FontFamily::Variant variant;
- bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
};
@@ -70,22 +68,17 @@ static jlong FontFamily_create(jlong builderPtr) {
}
std::unique_ptr<NativeFamilyBuilder> builder(
reinterpret_cast<NativeFamilyBuilder*>(builderPtr));
+ if (builder->fonts.empty()) {
+ return 0;
+ }
std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
builder->langId, builder->variant, std::move(builder->fonts));
- if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) {
+ if (family->getCoverage().length() == 0) {
return 0;
}
return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
}
-static void FontFamily_allowUnsupportedFont(jlong builderPtr) {
- if (builderPtr == 0) {
- return;
- }
- NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- builder->allowUnsupportedFont = true;
-}
-
static void FontFamily_abort(jlong builderPtr) {
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
delete builder;
@@ -270,7 +263,6 @@ static void FontFamily_addAxisValue(jlong builderPtr, jint tag, jfloat value) {
static const JNINativeMethod gFontFamilyMethods[] = {
{ "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
{ "nCreateFamily", "(J)J", (void*)FontFamily_create },
- { "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont },
{ "nAbort", "(J)V", (void*)FontFamily_abort },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index c79f5bd96e99..12da27307ade 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -36,6 +36,7 @@
#define ENCODING_AAC_ELD 15
#define ENCODING_AAC_XHE 16
#define ENCODING_AC4 17
+#define ENCODING_E_AC3_JOC 18
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -80,6 +81,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat)
return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
case ENCODING_AC4:
return AUDIO_FORMAT_AC4;
+ // case ENCODING_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // return AUDIO_FORMAT_E_AC3_JOC;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -130,6 +133,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
// return ENCODING_AAC_XHE;
case AUDIO_FORMAT_AC4:
return ENCODING_AC4;
+ // case AUDIO_FORMAT_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // return ENCODING_E_AC3_JOC;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index ebd16c7084ac..375d68b85824 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -25,6 +25,8 @@
#include <utils/Log.h>
#include <media/AudioRecord.h>
+#include <media/MicrophoneInfo.h>
+#include <vector>
#include <nativehelper/ScopedUtfChars.h>
@@ -32,6 +34,7 @@
#include "android_media_AudioErrors.h"
#include "android_media_DeviceCallback.h"
#include "android_media_MediaMetricsJNI.h"
+#include "android_media_MicrophoneInfo.h"
// ----------------------------------------------------------------------------
@@ -41,6 +44,11 @@ using namespace android;
static const char* const kClassPathName = "android/media/AudioRecord";
static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
+static jclass gArrayListClass;
+static struct {
+ jmethodID add;
+} gArrayListMethods;
+
struct audio_record_fields_t {
// these fields provide access from C++ to the...
jmethodID postNativeEventInJava; //... event post callback method
@@ -785,6 +793,46 @@ android_media_AudioRecord_native_getMetrics(JNIEnv *env, jobject thiz)
}
// ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env,
+ jobject thiz, jobject jActiveMicrophones) {
+ if (jActiveMicrophones == NULL) {
+ ALOGE("jActiveMicrophones is null");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jActiveMicrophones, gArrayListClass)) {
+ ALOGE("getActiveMicrophones not an arraylist");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioRecord pointer for getActiveMicrophones()");
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ jint jStatus = AUDIO_JAVA_SUCCESS;
+ std::vector<media::MicrophoneInfo> activeMicrophones;
+ status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
+ jStatus = nativeToJavaStatus(status);
+ return jStatus;
+ }
+
+ for (size_t i = 0; i < activeMicrophones.size(); i++) {
+ jobject jMicrophoneInfo;
+ jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ env->CallBooleanMethod(jActiveMicrophones, gArrayListMethods.add, jMicrophoneInfo);
+ env->DeleteLocalRef(jMicrophoneInfo);
+ }
+ return jStatus;
+}
+
+// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
@@ -824,6 +872,8 @@ static const JNINativeMethod gMethods[] = {
(void *)android_media_AudioRecord_disableDeviceCallback},
{"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
(void *)android_media_AudioRecord_get_timestamp},
+ {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
+ (void *)android_media_AudioRecord_get_active_microphones},
};
// field names found in android/media/AudioRecord.java
@@ -873,6 +923,10 @@ int register_android_media_AudioRecord(JNIEnv *env)
javaAudioTimestampFields.fieldNanoTime =
GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
+ jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+ gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+ gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 376a79717677..adaff1fcfbcb 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -21,17 +21,20 @@
#include <utils/Log.h>
#include <sstream>
+#include <vector>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
#include <media/AudioSystem.h>
#include <media/AudioPolicy.h>
+#include <media/MicrophoneInfo.h>
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
+#include "android_media_MicrophoneInfo.h"
// ----------------------------------------------------------------------------
@@ -143,7 +146,6 @@ static struct {
jfieldID mSource;
} gAudioAttributesFields;
-
static const char* const kEventHandlerClassPathName =
"android/media/AudioPortEventHandler";
static struct {
@@ -1158,7 +1160,6 @@ exit:
return jStatus;
}
-
static jint
android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
jobject jPorts, jintArray jGeneration)
@@ -1789,6 +1790,45 @@ android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz,
return AudioSystem::isOffloadSupported(format);
}
+static jint
+android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMicrophonesInfo)
+{
+ ALOGV("getMicrophones");
+
+ if (jMicrophonesInfo == NULL) {
+ ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) {
+ ALOGE("getMicrophones not an arraylist");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ jint jStatus;
+ std::vector<media::MicrophoneInfo> microphones;
+ status_t status = AudioSystem::getMicrophones(&microphones);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "AudioSystem::getMicrophones error %d", status);
+ jStatus = nativeToJavaStatus(status);
+ return jStatus;
+ }
+ if (microphones.size() == 0) {
+ jStatus = (jint)AUDIO_JAVA_SUCCESS;
+ return jStatus;
+ }
+ for (size_t i = 0; i < microphones.size(); i++) {
+ jobject jMicrophoneInfo;
+ jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &microphones[i]);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ env->CallBooleanMethod(jMicrophonesInfo, gArrayListMethods.add, jMicrophoneInfo);
+ env->DeleteLocalRef(jMicrophoneInfo);
+ }
+
+ return jStatus;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1843,6 +1883,7 @@ static const JNINativeMethod gMethods[] = {
{"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
{"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
{"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
+ {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
};
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index afbc57913688..61a22c144f1b 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1262,6 +1262,18 @@ static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, job
return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
}
+static int android_media_AudioTrack_setPresentation(
+ JNIEnv *env, jobject thiz, jint presentationId, jint programId) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId);
+}
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1333,6 +1345,7 @@ static const JNINativeMethod gMethods[] = {
{"native_getVolumeShaperState",
"(I)Landroid/media/VolumeShaper$State;",
(void *)android_media_AudioTrack_get_volume_shaper_state},
+ {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
};
diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp
new file mode 100644
index 000000000000..9198cbe648eb
--- /dev/null
+++ b/core/jni/android_media_MicrophoneInfo.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android_media_MicrophoneInfo.h"
+#include "android_media_AudioErrors.h"
+#include "core_jni_helpers.h"
+
+using namespace android;
+
+static jclass gArrayListClass;
+static jmethodID gArrayListCstor;
+static struct {
+ jmethodID add;
+} gArrayListMethods;
+
+static jclass gFloatClass;
+static jmethodID gFloatCstor;
+
+static jclass gFloatArrayClass;
+
+static jclass gIntegerClass;
+static jmethodID gIntegerCstor;
+
+static jclass gMicrophoneInfoClass;
+static jmethodID gMicrophoneInfoCstor;
+
+static jclass gMicrophoneInfoCoordinateClass;
+static jmethodID gMicrophoneInfoCoordinateCstor;
+
+static jclass gPairClass;
+static jmethodID gPairCstor;
+
+namespace android {
+
+jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
+ const media::MicrophoneInfo *microphoneInfo)
+{
+ jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+ jstring jDeviceId = NULL;
+ jstring jAddress = NULL;
+ jobject jGeometricLocation = NULL;
+ jobject jOrientation = NULL;
+ jobject jFrequencyResponses = NULL;
+ jobject jChannelMappings = NULL;
+
+ jDeviceId = env->NewStringUTF(String8(microphoneInfo->getDeviceId()).string());
+ jAddress = env->NewStringUTF(String8(microphoneInfo->getAddress()).string());
+ if (microphoneInfo->getGeometricLocation().size() != 3 ||
+ microphoneInfo->getOrientation().size() != 3) {
+ jStatus = nativeToJavaStatus(BAD_VALUE);
+ goto exit;
+ }
+ jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass,
+ gMicrophoneInfoCoordinateCstor,
+ NULL,
+ microphoneInfo->getGeometricLocation()[0],
+ microphoneInfo->getGeometricLocation()[1],
+ microphoneInfo->getGeometricLocation()[2]);
+ jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass,
+ gMicrophoneInfoCoordinateCstor,
+ NULL,
+ microphoneInfo->getOrientation()[0],
+ microphoneInfo->getOrientation()[1],
+ microphoneInfo->getOrientation()[2]);
+ // Create a list of Pair for frequency response.
+ if (microphoneInfo->getFrequencyResponses().size() != 2 ||
+ microphoneInfo->getFrequencyResponses()[0].size() !=
+ microphoneInfo->getFrequencyResponses()[1].size()) {
+ jStatus = nativeToJavaStatus(BAD_VALUE);
+ goto exit;
+ }
+ jFrequencyResponses = env->NewObject(gArrayListClass, gArrayListCstor);
+ for (size_t i = 0; i < microphoneInfo->getFrequencyResponses()[0].size(); i++) {
+ jobject jFrequency = env->NewObject(gFloatClass, gFloatCstor,
+ microphoneInfo->getFrequencyResponses()[0][i]);
+ jobject jResponse = env->NewObject(gFloatClass, gFloatCstor,
+ microphoneInfo->getFrequencyResponses()[1][i]);
+ jobject jFrequencyResponse = env->NewObject(gPairClass, gPairCstor, jFrequency, jResponse);
+ env->CallBooleanMethod(jFrequencyResponses, gArrayListMethods.add, jFrequencyResponse);
+ env->DeleteLocalRef(jFrequency);
+ env->DeleteLocalRef(jResponse);
+ env->DeleteLocalRef(jFrequencyResponse);
+ }
+ // Create a list of Pair for channel mapping.
+ if (microphoneInfo->getChannelMapping().size() != AUDIO_CHANNEL_COUNT_MAX) {
+ jStatus = nativeToJavaStatus(BAD_VALUE);
+ goto exit;
+ }
+ jChannelMappings = env->NewObject(gArrayListClass, gArrayListCstor);
+ for (size_t i = 0; i < microphoneInfo->getChannelMapping().size(); i++) {
+ int channelMappingType = microphoneInfo->getChannelMapping()[i];
+ if (channelMappingType != AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) {
+ jobject jChannelIndex = env->NewObject(gIntegerClass, gIntegerCstor, i);
+ jobject jChannelMappingType = env->NewObject(gIntegerClass, gIntegerCstor,
+ channelMappingType);
+ jobject jChannelMapping = env->NewObject(gPairClass, gPairCstor,
+ jChannelIndex, jChannelMappingType);
+ env->CallBooleanMethod(jChannelMappings, gArrayListMethods.add, jChannelMapping);
+ env->DeleteLocalRef(jChannelIndex);
+ env->DeleteLocalRef(jChannelMappingType);
+ env->DeleteLocalRef(jChannelMapping);
+ }
+ }
+ *jMicrophoneInfo = env->NewObject(gMicrophoneInfoClass, gMicrophoneInfoCstor, jDeviceId,
+ microphoneInfo->getType(), jAddress,
+ microphoneInfo->getDeviceLocation(),
+ microphoneInfo->getDeviceGroup(),
+ microphoneInfo->getIndexInTheGroup(),
+ jGeometricLocation, jOrientation,
+ jFrequencyResponses, jChannelMappings,
+ microphoneInfo->getSensitivity(),
+ microphoneInfo->getMaxSpl(),
+ microphoneInfo->getMinSpl(),
+ microphoneInfo->getDirectionality());
+
+exit:
+ if (jDeviceId != NULL) {
+ env->DeleteLocalRef(jDeviceId);
+ }
+ if (jAddress != NULL) {
+ env->DeleteLocalRef(jAddress);
+ }
+ if (jFrequencyResponses != NULL) {
+ env->DeleteLocalRef(jFrequencyResponses);
+ }
+ if (jChannelMappings != NULL) {
+ env->DeleteLocalRef(jChannelMappings);
+ }
+ if (jGeometricLocation != NULL) {
+ env->DeleteLocalRef(jGeometricLocation);
+ }
+ if (jOrientation != NULL) {
+ env->DeleteLocalRef(jOrientation);
+ }
+ return jStatus;
+}
+
+}
+
+int register_android_media_MicrophoneInfo(JNIEnv *env)
+{
+ jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+ gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+ gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
+ gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+ jclass floatClass = FindClassOrDie(env, "java/lang/Float");
+ gFloatClass = MakeGlobalRefOrDie(env, floatClass);
+ gFloatCstor = GetMethodIDOrDie(env, floatClass, "<init>", "(F)V");
+
+ jclass floatArrayClass = FindClassOrDie(env, "[F");
+ gFloatArrayClass = MakeGlobalRefOrDie(env, floatArrayClass);
+
+ jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
+ gIntegerClass = MakeGlobalRefOrDie(env, integerClass);
+ gIntegerCstor = GetMethodIDOrDie(env, integerClass, "<init>", "(I)V");
+
+ jclass microphoneInfoClass = FindClassOrDie(env, "android/media/MicrophoneInfo");
+ gMicrophoneInfoClass = MakeGlobalRefOrDie(env, microphoneInfoClass);
+ gMicrophoneInfoCstor = GetMethodIDOrDie(env, microphoneInfoClass, "<init>",
+ "(Ljava/lang/String;ILjava/lang/String;IIILandroid/media/MicrophoneInfo$Coordinate3F;Landroid/media/MicrophoneInfo$Coordinate3F;Ljava/util/List;Ljava/util/List;FFFI)V");
+
+ jclass microphoneInfoCoordinateClass = FindClassOrDie(
+ env, "android/media/MicrophoneInfo$Coordinate3F");
+ gMicrophoneInfoCoordinateClass = MakeGlobalRefOrDie(env, microphoneInfoCoordinateClass);
+ gMicrophoneInfoCoordinateCstor = GetMethodIDOrDie(env, microphoneInfoCoordinateClass, "<init>",
+ "(Landroid/media/MicrophoneInfo;FFF)V");
+
+ jclass pairClass = FindClassOrDie(env, "android/util/Pair");
+ gPairClass = MakeGlobalRefOrDie(env, pairClass);
+ gPairCstor = GetMethodIDOrDie(env, pairClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
+
+ return 0;
+}
diff --git a/core/jni/android_media_MicrophoneInfo.h b/core/jni/android_media_MicrophoneInfo.h
new file mode 100644
index 000000000000..241b6d05b724
--- /dev/null
+++ b/core/jni/android_media_MicrophoneInfo.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_MICROPHONEINFO_H
+#define ANDROID_MEDIA_MICROPHONEINFO_H
+
+#include <system/audio.h>
+#include <media/MicrophoneInfo.h>
+
+#include "jni.h"
+
+namespace android {
+
+// Conversion from C++ MicrophoneInfo object to Java MicrophoneInfo object
+
+extern jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
+ const media::MicrophoneInfo *microphoneInfo);
+} // namespace android
+
+#endif \ No newline at end of file
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 37ff8c8cefd2..8770d788fc37 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -174,8 +174,25 @@ static jboolean android_view_RenderNode_hasShadow(jlong renderNodePtr) {
return renderNode->stagingProperties().hasShadow();
}
-static jboolean android_view_RenderNode_setShadowColor(jlong renderNodePtr, jint shadowColor) {
- return SET_AND_DIRTY(setShadowColor, static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+static jboolean android_view_RenderNode_setSpotShadowColor(jlong renderNodePtr, jint shadowColor) {
+ return SET_AND_DIRTY(setSpotShadowColor,
+ static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+}
+
+static jint android_view_RenderNode_getSpotShadowColor(jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ return renderNode->stagingProperties().getSpotShadowColor();
+}
+
+static jboolean android_view_RenderNode_setAmbientShadowColor(jlong renderNodePtr,
+ jint shadowColor) {
+ return SET_AND_DIRTY(setAmbientShadowColor,
+ static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+}
+
+static jint android_view_RenderNode_getAmbientShadowColor(jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ return renderNode->stagingProperties().getAmbientShadowColor();
}
static jboolean android_view_RenderNode_setClipToOutline(jlong renderNodePtr,
@@ -575,7 +592,10 @@ static const JNINativeMethod gMethods[] = {
{ "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
{ "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
{ "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow },
- { "nSetShadowColor", "(JI)Z", (void*) android_view_RenderNode_setShadowColor },
+ { "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor },
+ { "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor },
+ { "nSetAmbientShadowColor","(JI)Z", (void*) android_view_RenderNode_setAmbientShadowColor },
+ { "nGetAmbientShadowColor","(J)I", (void*) android_view_RenderNode_getAmbientShadowColor },
{ "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline },
{ "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index bfd575a5b18d..ff0f4fd4d3ac 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -208,7 +208,7 @@ message GlobalSettingsProto {
optional SettingProto wifi_on = 136 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_scan_always_available = 137 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_wakeup_enabled = 138 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto wifi_wakeup_available = 305 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 305; // Removed wifi_wakeup_available
optional SettingProto network_scoring_ui_enabled = 306 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto speed_label_cache_eviction_age_millis = 307 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c11058a22107..449e54672207 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -295,7 +295,6 @@ message WindowStateProto {
optional bool animating_exit = 14;
repeated WindowStateProto child_windows = 15;
optional .android.graphics.RectProto surface_position = 16;
- optional .android.graphics.RectProto shown_position = 17;
optional int32 requested_width = 18;
optional int32 requested_height = 19;
optional int32 view_visibility = 20;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e5ba6d76578d..d58b95a6697c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1785,13 +1785,22 @@
<!-- Must be required by an ImsService to ensure that only the
system can bind to it.
- <p>Protection level: signature|privileged
+ <p>Protection level: signature|privileged|vendorPrivileged
@SystemApi
@hide
-->
<permission android:name="android.permission.BIND_IMS_SERVICE"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- Must be required by a DataService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_DATA_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
<!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
EuiccManager APIs.
<p>Protection level: signature|privileged|development
@@ -3029,6 +3038,13 @@
<permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
android:protectionLevel="signature|privileged|development" />
+ <!-- Allows an application to collect ambient light stats.
+ <p>Not for use by third party applications.</p>
+ TODO: Make a system API
+ @hide -->
+ <permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS"
+ android:protectionLevel="signature|privileged|development" />
+
<!-- Allows an application to modify the display brightness configuration
@hide
@SystemApi -->
diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml
index 3b89f0d0cde7..1be915e809e6 100644
--- a/core/res/res/layout/popup_menu_item_layout.xml
+++ b/core/res/res/layout/popup_menu_item_layout.xml
@@ -28,18 +28,18 @@
android:layout_marginBottom="4dip"
android:background="@drawable/list_divider_material" />
- <!-- Icon will be inserted here. -->
-
- <!-- The title and summary have some gap between them,
- and this 'group' should be centered vertically. -->
<LinearLayout
android:id="@+id/content"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="?attr/dropdownListPreferredItemHeight"
android:paddingEnd="16dip"
android:duplicateParentState="true" >
- <LinearLayout
+ <!-- Icon will be inserted here. -->
+
+ <!-- The title and summary have some gap between them,
+ and this 'group' should be centered vertically. -->
+ <RelativeLayout
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
@@ -71,7 +71,7 @@
android:duplicateParentState="true"
android:textAlignment="viewStart" />
- </LinearLayout>
+ </RelativeLayout>
<ImageView
android:id="@+id/submenuarrow"
@@ -81,8 +81,9 @@
android:layout_marginStart="8dp"
android:scaleType="center"
android:visibility="gone" />
- </LinearLayout>
- <!-- Checkbox, and/or radio button will be inserted here. -->
+ <!-- Checkbox, and/or radio button will be inserted here. -->
+
+ </LinearLayout>
</com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/layout/unlaunchable_app_activity.xml b/core/res/res/layout/unlaunchable_app_activity.xml
deleted file mode 100644
index 429d5edea8b7..000000000000
--- a/core/res/res/layout/unlaunchable_app_activity.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="@dimen/dialog_padding"
- android:orientation="vertical">
- <TextView android:id="@+id/unlaunchable_app_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/dialog_padding"
- android:paddingBottom="@dimen/dialog_padding"
- android:textAppearance="@android:style/TextAppearance.Material.Title" />
-
- <TextView android:id="@+id/unlaunchable_app_message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/dialog_padding"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="?android:attr/textColorSecondary" />
-</LinearLayout>
-
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0543305f9880..68dad87bd292 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3036,6 +3036,28 @@
<!-- The title this view should present to accessibility as a pane title.
See {@link android.view.View#setAccessibilityPaneTitle(CharSequence)} -->
<attr name="accessibilityPaneTitle" format="string" />
+
+ <!-- Sets the color of the spot shadow that is drawn when the view has a positive Z or
+ elevation value.
+ <p>
+ By default the shadow color is black. Generally, this color will be opaque so the
+ intensity of the shadow is consistent between different views with different colors.
+ <p>
+ The opacity of the final spot shadow is a function of the shadow caster height, the
+ alpha channel of the outlineSpotShadowColor (typically opaque), and the
+ {@link android.R.attr#spotShadowAlpha} theme attribute. -->
+ <attr name="outlineSpotShadowColor" format="color" />
+
+ <!-- Sets the color of the ambient shadow that is drawn when the view has a positive Z
+ or elevation value.
+ <p>
+ By default the shadow color is black. Generally, this color will be opaque so the
+ intensity of the shadow is consistent between different views with different colors.
+ <p>
+ The opacity of the final ambient shadow is a function of the shadow caster height,
+ the alpha channel of the outlineAmbientShadowColor (typically opaque), and the
+ {@link android.R.attr#ambientShadowAlpha} theme attribute. -->
+ <attr name="outlineAmbientShadowColor" format="color" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 607414d666cc..05d51ec8e48e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -432,12 +432,6 @@
<!-- Activity name to enable wifi tethering after provisioning app succeeds -->
<string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
- <!-- Controls the WiFi wakeup feature.
- 0 = Not available.
- 1 = Available.
- -->
- <integer translatable="false" name="config_wifi_wakeup_available">1</integer>
-
<!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering.
Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
@@ -1132,7 +1126,7 @@
<bool name="config_hotswapCapable">false</bool>
<!-- Component name of the ICC hotswap prompt for restart dialog -->
- <string name="config_iccHotswapPromptForRestartDialogComponent" translateable="false">@null</string>
+ <string name="config_iccHotswapPromptForRestartDialogComponent" translatable="false">@null</string>
<!-- Enable puk unlockscreen by default.
If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
@@ -1431,7 +1425,7 @@
permission.
[This is only used if config_enableUpdateableTimeZoneRules and
config_timeZoneRulesUpdateTrackingEnabled are true.] -->
- <string name="config_timeZoneRulesUpdaterPackage" translateable="false">com.android.timezone.updater</string>
+ <string name="config_timeZoneRulesUpdaterPackage" translatable="false">com.android.timezone.updater</string>
<!-- The package of the time zone rules data application. Expected to be configured
by OEMs to reference their own priv-app APK package.
@@ -1440,7 +1434,7 @@
data app packages.
[This is only used if config_enableUpdateableTimeZoneRules and
config_timeZoneRulesUpdateTrackingEnabled are true.] -->
- <string name="config_timeZoneRulesDataPackage" translateable="false"></string>
+ <string name="config_timeZoneRulesDataPackage" translatable="false"></string>
<!-- The allowed time in milliseconds between an update check intent being broadcast and the
response being considered overdue. Reliability triggers will not fire in this time.
@@ -3258,7 +3252,7 @@
<string translatable="false" name="config_deviceSpecificAudioService"></string>
<!-- Component name of media projection permission dialog -->
- <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
+ <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">2dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 30586d17cf3c..dee880f0ea2d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2866,6 +2866,8 @@
<public name="lastBaselineToBottomHeight" />
<public name="lineHeight" />
<public name="accessibilityHeading" />
+ <public name="outlineSpotShadowColor" />
+ <public name="outlineAmbientShadowColor" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3cde765e04cb..ec81df7b89e2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1351,6 +1351,10 @@
<string name="fingerprint_error_lockout_permanent">Too many attempts. Fingerprint sensor disabled.</string>
<!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint -->
<string name="fingerprint_error_unable_to_process">Try again.</string>
+ <!-- Generic error message shown when the user has no enrolled fingerprints -->
+ <string name="fingerprint_error_no_fingerprints">No fingerprints enrolled.</string>
+ <!-- Generic error message shown when the app requests fingerprint authentication on a device without a sensor -->
+ <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor</string>
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
@@ -3222,19 +3226,23 @@
<string name="dlg_ok">OK</string>
<!-- USB_PREFERENCES: Notification for when the user connected to the charger only. This is the title -->
- <string name="usb_charging_notification_title">USB charging this device</string>
+ <string name="usb_charging_notification_title">Charging this device via USB</string>
<!-- USB_PREFERENCES: Notification for when the user connects the phone to supply power to attached device. This is the title -->
- <string name="usb_supplying_notification_title">USB supplying power to attached device</string>
+ <string name="usb_supplying_notification_title">Charging connected device via USB</string>
<!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MTP mode. This is the title -->
- <string name="usb_mtp_notification_title">USB for file transfer</string>
+ <string name="usb_mtp_notification_title">USB file transfer turned on</string>
<!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in PTP mode. This is the title -->
- <string name="usb_ptp_notification_title">USB for photo transfer</string>
+ <string name="usb_ptp_notification_title">PTP via USB turned on</string>
+ <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in Tethering mode. This is the title -->
+ <string name="usb_tether_notification_title">USB tethering turned on</string>
<!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MIDI mode. This is the title -->
- <string name="usb_midi_notification_title">USB for MIDI</string>
+ <string name="usb_midi_notification_title">MIDI via USB turned on</string>
<!-- USB_PREFERENCES: Notification for when a USB accessory is attached. This is the title -->
- <string name="usb_accessory_notification_title">Connected to a USB accessory</string>
+ <string name="usb_accessory_notification_title">USB accessory mode turned on</string>
<!-- See USB_PREFERENCES. This is the message. -->
<string name="usb_notification_message">Tap for more options.</string>
+ <!-- See USB_PREFERENCES. This is the message when a data mode is turned on (mtp, ptp, midi) and the device is supplying power.. -->
+ <string name="usb_power_notification_message">Charging connected device. Tap for more options.</string>
<!-- USB_PREFERENCES: Notification for when a type-c USB audio accessory is attached but not supported. This is the title -->
<string name="usb_unsupported_audio_accessory_title">Analog audio accessory detected</string>
<!-- Message of notification shown when a type-c USB audio accessory is attached but not supported. -->
@@ -4419,15 +4427,6 @@
<!-- DO NOT TRANSLATE -->
<string name="date_picker_day_typeface">sans-serif-medium</string>
- <!-- Notify use that they are in Lock-to-app -->
- <string name="lock_to_app_toast">To unpin this screen, touch &amp; hold Back and Overview
- buttons</string>
-
- <!-- Starting lock-to-app indication. -->
- <string name="lock_to_app_start">Screen pinned</string>
- <!-- Exting lock-to-app indication. -->
- <string name="lock_to_app_exit">Screen unpinned</string>
-
<!-- Lock-to-app unlock pin string -->
<string name="lock_to_app_unlock_pin">Ask for PIN before unpinning</string>
<!-- Lock-to-app unlock pattern string -->
@@ -4811,7 +4810,7 @@
A toast message shown when an app shortcut that was restored from a previous device is clicked,
but it cannot be started because the shortcut was created by a newer version of the app.
-->
- <string name="shortcut_restored_on_lower_version">This shortcut requires latest app</string>
+ <string name="shortcut_restored_on_lower_version">App version downgraded, or isn\u2019t compatible with this shortcut</string>
<!--
A toast message shown when an app shortcut that was restored from a previous device is clicked,
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a62d49ee7bfd..2ed94a0365b4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -767,9 +767,6 @@
<java-symbol type="string" name="kilobyteShort" />
<java-symbol type="string" name="last_month" />
<java-symbol type="string" name="launchBrowserDefault" />
- <java-symbol type="string" name="lock_to_app_toast" />
- <java-symbol type="string" name="lock_to_app_start" />
- <java-symbol type="string" name="lock_to_app_exit" />
<java-symbol type="string" name="lock_to_app_unlock_pin" />
<java-symbol type="string" name="lock_to_app_unlock_pattern" />
<java-symbol type="string" name="lock_to_app_unlock_password" />
@@ -2026,8 +2023,10 @@
<java-symbol type="string" name="usb_mtp_notification_title" />
<java-symbol type="string" name="usb_charging_notification_title" />
<java-symbol type="string" name="usb_notification_message" />
+ <java-symbol type="string" name="usb_power_notification_message" />
<java-symbol type="string" name="usb_ptp_notification_title" />
<java-symbol type="string" name="usb_midi_notification_title" />
+ <java-symbol type="string" name="usb_tether_notification_title" />
<java-symbol type="string" name="usb_supplying_notification_title" />
<java-symbol type="string" name="usb_unsupported_audio_accessory_title" />
<java-symbol type="string" name="usb_unsupported_audio_accessory_message" />
@@ -2153,7 +2152,6 @@
<java-symbol type="string" name="config_mobile_hotspot_provision_response" />
<java-symbol type="integer" name="config_mobile_hotspot_provision_check_period" />
<java-symbol type="string" name="config_wifi_tether_enable" />
- <java-symbol type="integer" name="config_wifi_wakeup_available" />
<java-symbol type="bool" name="config_intrusiveNotificationLed" />
<java-symbol type="bool" name="config_notificationBadging" />
<java-symbol type="dimen" name="preference_fragment_padding_bottom" />
@@ -2352,6 +2350,8 @@
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
<java-symbol type="string" name="fingerprint_not_recognized" />
+ <java-symbol type="string" name="fingerprint_error_no_fingerprints" />
+ <java-symbol type="string" name="fingerprint_error_hw_not_present" />
<!-- Fingerprint config -->
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
@@ -2700,9 +2700,6 @@
<java-symbol type="string" name="language_selection_title" />
<java-symbol type="string" name="search_language_hint" />
- <java-symbol type="layout" name="unlaunchable_app_activity" />
- <java-symbol type="id" name="unlaunchable_app_title" />
- <java-symbol type="id" name="unlaunchable_app_message" />
<java-symbol type="string" name="work_mode_off_title" />
<java-symbol type="string" name="work_mode_off_message" />
<java-symbol type="string" name="work_mode_turn_on" />
@@ -3151,6 +3148,7 @@
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
<java-symbol type="string" name="config_headlineFontFamily" />
<java-symbol type="string" name="config_headlineFontFamilyLight" />
+ <java-symbol type="string" name="config_headlineFontFamilyMedium" />
<java-symbol type="drawable" name="stat_sys_vitals" />
diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
new file mode 100644
index 000000000000..5989da7af655
--- /dev/null
+++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os;
+
+import static android.os.FileUtils.copyInternalSendfile;
+import static android.os.FileUtils.copyInternalSplice;
+import static android.os.FileUtils.copyInternalUserspace;
+
+import android.os.FileUtils.MemoryPipe;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Param;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class FileUtilsBenchmark {
+ @Param({"32", "32000", "32000000"})
+ private int mSize;
+
+ private File mSrc;
+ private File mDest;
+
+ private byte[] mData;
+
+ @BeforeExperiment
+ protected void setUp() throws Exception {
+ mSrc = new File("/data/local/tmp/src");
+ mDest = new File("/data/local/tmp/dest");
+
+ mData = new byte[mSize];
+
+ try (FileOutputStream os = new FileOutputStream(mSrc)) {
+ os.write(mData);
+ }
+ }
+
+ public void timeRegularUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timeRegularSendfile(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSendfile(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSourceUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSourceSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSinkUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSinkSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
new file mode 100644
index 000000000000..f90ae340b2e9
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AmbientBrightnessDayStatsTest {
+
+ private static final LocalDate LOCAL_DATE = LocalDate.now();
+ private static final float[] BUCKET_BOUNDARIES = {0, 1, 10, 100};
+ private static final float[] STATS = {1.3f, 2.6f, 5.8f, 10};
+
+ @Test
+ public void testParamsMustNotBeNull() {
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES, STATS));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null, STATS));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBucketBoundariesMustNotBeEmpty() {
+ new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testStatsAndBoundariesMustHaveSameLength() {
+ float[] stats = Arrays.copyOf(STATS, STATS.length + 1);
+ stats[stats.length - 1] = 0;
+ new AmbientBrightnessDayStats(LOCAL_DATE, BUCKET_BOUNDARIES, stats);
+ }
+
+ @Test
+ public void testAmbientBrightnessDayStatsAdd() {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
+ dayStats.log(0, 1);
+ dayStats.log(0.5f, 1.5f);
+ dayStats.log(50, 12.5f);
+ dayStats.log(2000, 1.24f);
+ dayStats.log(-10, 0.5f);
+ assertEquals(4, dayStats.getStats().length);
+ assertEquals(2.5f, dayStats.getStats()[0], 0);
+ assertEquals(0, dayStats.getStats()[1], 0);
+ assertEquals(12.5f, dayStats.getStats()[2], 0);
+ assertEquals(1.24f, dayStats.getStats()[3], 0);
+ }
+
+ @Test
+ public void testGetters() {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(LOCAL_DATE, dayStats.getLocalDate());
+ assertArrayEquals(BUCKET_BOUNDARIES, dayStats.getBucketBoundaries(), 0);
+ assertArrayEquals(STATS, dayStats.getStats(), 0);
+ }
+
+ @Test
+ public void testParcelUnparcelAmbientBrightnessDayStats() {
+ LocalDate today = LocalDate.now();
+ AmbientBrightnessDayStats stats = new AmbientBrightnessDayStats(today,
+ new float[]{0, 1, 10, 100}, new float[]{1.3f, 2.6f, 5.8f, 10});
+ // Parcel the data
+ Parcel parcel = Parcel.obtain();
+ stats.writeToParcel(parcel, 0);
+ byte[] parceled = parcel.marshall();
+ parcel.recycle();
+ // Unparcel and check that it has not changed
+ parcel = Parcel.obtain();
+ parcel.unmarshall(parceled, 0, parceled.length);
+ parcel.setDataPosition(0);
+ AmbientBrightnessDayStats statsAgain = AmbientBrightnessDayStats.CREATOR.createFromParcel(
+ parcel);
+ assertEquals(stats, statsAgain);
+ }
+
+ @Test
+ public void testAmbientBrightnessDayStatsEquals() {
+ AmbientBrightnessDayStats emptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
+ AmbientBrightnessDayStats identicalEmptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, new float[BUCKET_BOUNDARIES.length]);
+ assertEquals(emptyDayStats, identicalEmptyDayStats);
+ assertEquals(emptyDayStats.hashCode(), identicalEmptyDayStats.hashCode());
+
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ AmbientBrightnessDayStats identicalDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(dayStats, identicalDayStats);
+ assertEquals(dayStats.hashCode(), identicalDayStats.hashCode());
+
+ assertNotEquals(emptyDayStats, dayStats);
+ assertNotEquals(emptyDayStats.hashCode(), dayStats.hashCode());
+
+ AmbientBrightnessDayStats differentDateDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE.plusDays(1), BUCKET_BOUNDARIES, STATS);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentDateDayStats.hashCode());
+
+ float[] differentStats = Arrays.copyOf(STATS, STATS.length);
+ differentStats[differentStats.length - 1] += 5f;
+ AmbientBrightnessDayStats differentStatsDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, differentStats);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentStatsDayStats.hashCode());
+
+ float[] differentBucketBoundaries = Arrays.copyOf(BUCKET_BOUNDARIES,
+ BUCKET_BOUNDARIES.length);
+ differentBucketBoundaries[differentBucketBoundaries.length - 1] += 100f;
+ AmbientBrightnessDayStats differentBoundariesDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE, differentBucketBoundaries, STATS);
+ assertNotEquals(dayStats, differentBoundariesDayStats);
+ assertNotEquals(dayStats.hashCode(), differentBoundariesDayStats.hashCode());
+ }
+
+ private interface ExceptionRunnable {
+ void run() throws Exception;
+ }
+
+ private static void assertThrows(Class<? extends Throwable> exceptionClass,
+ ExceptionRunnable r) {
+ try {
+ r.run();
+ } catch (Throwable e) {
+ assertTrue("Expected exception type " + exceptionClass.getName() + " but got "
+ + e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass()));
+ return;
+ }
+ fail("Expected exception type " + exceptionClass.getName()
+ + ", but no exception was thrown");
+ }
+
+}
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 27b7f9e185bb..ea0347d67ad7 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -192,6 +192,12 @@ public class UriTest extends TestCase {
assertEquals("a:a@example.com:a@example2.com", uri.getAuthority());
assertEquals("example2.com", uri.getHost());
assertEquals(-1, uri.getPort());
+ assertEquals("/path", uri.getPath());
+
+ uri = Uri.parse("http://a.foo.com\\.example.com/path");
+ assertEquals("a.foo.com", uri.getHost());
+ assertEquals(-1, uri.getPort());
+ assertEquals("\\.example.com/path", uri.getPath());
}
@SmallTest
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index cd20192edfd6..0bc3a2d879ab 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,16 +21,19 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.os.FileUtils.MemoryPipe;
import android.provider.DocumentsContract.Document;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import libcore.io.IoUtils;
+import libcore.io.Streams;
import com.google.android.collect.Sets;
@@ -40,11 +43,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Random;
@RunWith(AndroidJUnit4.class)
public class FileUtilsTest {
@@ -56,6 +61,8 @@ public class FileUtilsTest {
private File mCopyFile;
private File mTarget;
+ private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 };
+
private Context getContext() {
return InstrumentationRegistry.getContext();
}
@@ -80,7 +87,7 @@ public class FileUtilsTest {
@Test
public void testCopyFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertFalse(mCopyFile.exists());
FileUtils.copyFile(mTestFile, mCopyFile);
assertTrue(mCopyFile.exists());
@@ -97,6 +104,104 @@ public class FileUtilsTest {
}
@Test
+ public void testCopy_FileToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in, out);
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_FileToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_ShortPipeToFile() throws Exception {
+ byte[] source = new byte[33_000_000];
+ new Random().nextBytes(source);
+
+ for (int size : DATA_SIZES) {
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = Arrays.copyOf(source, size);
+ byte[] actual = new byte[size];
+
+ try (MemoryPipe in = MemoryPipe.createSource(source);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in.getFD(), out.getFD(), null, null, size);
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
public void testIsFilenameSafe() throws Exception {
assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -106,7 +211,7 @@ public class FileUtilsTest {
@Test
public void testReadTextFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
@@ -127,7 +232,7 @@ public class FileUtilsTest {
@Test
public void testReadTextFileWithZeroLengthFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
new FileOutputStream(mTestFile).close(); // Zero out the file
assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
@@ -381,12 +486,21 @@ public class FileUtilsTest {
file.setLastModified(System.currentTimeMillis() - age);
}
- private void stageFile(File file, String data) throws Exception {
- FileWriter writer = new FileWriter(file);
- try {
- writer.write(data, 0, data.length());
- } finally {
- writer.close();
+ private void writeFile(File file, String data) throws Exception {
+ writeFile(file, data.getBytes());
+ }
+
+ private void writeFile(File file, byte[] data) throws Exception {
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ out.write(data);
+ }
+ }
+
+ private byte[] readFile(File file) throws Exception {
+ try (FileInputStream in = new FileInputStream(file);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ Streams.copy(in, out);
+ return out.toByteArray();
}
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0083b017033b..83d0719c89d4 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -436,7 +436,6 @@ public class SettingsBackupTest {
Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED,
Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED,
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
Settings.Global.WIFI_WATCHDOG_ON,
Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON,
Settings.Global.WINDOW_ANIMATION_SCALE,
diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk
new file mode 100644
index 000000000000..4ca3e4ce2389
--- /dev/null
+++ b/core/tests/overlaytests/device/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_TARGET_REQUIRED_MODULES := \
+ OverlayDeviceTests_AppOverlayOne \
+ OverlayDeviceTests_AppOverlayTwo \
+ OverlayDeviceTests_FrameworkOverlay
+include $(BUILD_PACKAGE)
+
+# Include to build test-apps.
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index e01caeedd862..d14fdf5ee819 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -15,15 +15,15 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.om.test">
+ package="com.android.overlaytest">
<uses-sdk android:minSdkVersion="21" />
<application>
- <uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.test.runner"/>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.om.test" />
-
+ android:targetPackage="com.android.overlaytest"
+ android:label="Runtime resource overlay tests" />
</manifest>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
new file mode 100644
index 000000000000..f06983559830
--- /dev/null
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<configuration description="Test module config for OverlayDeviceTests">
+ <option name="test-tag" value="OverlayDeviceTests" />
+ <option name="test-suite-tag" value="apct" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="OverlayDeviceTests.apk" />
+ <option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
+ <option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
+ <option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="cmd overlay disable com.android.overlaytest.framework" />
+ <option name="run-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_one" />
+ <option name="run-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_two" />
+ <option name="teardown-command"
+ value="cmd overlay disable com.android.overlaytest.framework" />
+ <option name="teardown-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_one" />
+ <option name="teardown-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_two" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.overlaytest" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk b/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk
deleted file mode 100644
index f76de7a93b2e..000000000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := system_current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.filtered_app_overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml
deleted file mode 100644
index 5b7950a25fbf..000000000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.filtered_app_overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="com.android.overlaytest"
- android:requiredSystemPropertyName="persist.oem.overlay.test"
- android:requiredSystemPropertyValue="foo"
- android:priority="3"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt
deleted file mode 100644
index 0954cedeb5d5..000000000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt
+++ /dev/null
@@ -1 +0,0 @@
-Lorem ipsum: filtered overlays.
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml b/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml
deleted file mode 100644
index 60b94eec5994..000000000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="str">filtered</string>
-</resources>
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml b/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml
deleted file mode 100644
index e2652b7e2915..000000000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<integer value="3"/>
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/Android.mk b/core/tests/overlaytests/device/OverlayAppFirst/Android.mk
deleted file mode 100644
index bf9416c279be..000000000000
--- a/core/tests/overlaytests/device/OverlayAppFirst/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.first_app_overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml
deleted file mode 100644
index ec10bbcf752e..000000000000
--- a/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.first_app_overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="com.android.overlaytest" android:priority="1"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/Android.mk b/core/tests/overlaytests/device/OverlayAppSecond/Android.mk
deleted file mode 100644
index bb7d142d6809..000000000000
--- a/core/tests/overlaytests/device/OverlayAppSecond/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.second_app_overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml
deleted file mode 100644
index ed498637b454..000000000000
--- a/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.second_app_overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="com.android.overlaytest" android:priority="2"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTest/Android.mk b/core/tests/overlaytests/device/OverlayTest/Android.mk
deleted file mode 100644
index 5fe7b917102e..000000000000
--- a/core/tests/overlaytests/device/OverlayTest/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_PACKAGE_NAME := OverlayTest
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVA_LIBRARIES += android.test.base
-
-LOCAL_MODULE_PATH := $(TARGET_OUT)/app
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml
deleted file mode 100644
index 9edba12ffa8f..000000000000
--- a/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest">
- <uses-permission android:name="android.permission.RUN_INSTRUMENTATION"/>
- <application>
- <uses-library android:name="android.test.runner"/>
- </application>
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.overlaytest"
- android:label="Runtime resource overlay tests"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java
deleted file mode 100644
index e104f5a670c1..000000000000
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.overlaytest;
-
-public class WithMultipleOverlaysTest extends OverlayBaseTest {
- public WithMultipleOverlaysTest() {
- mMode = MODE_MULTIPLE_OVERLAYS;
- }
-}
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
deleted file mode 100644
index 816a476e28cf..000000000000
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.overlaytest;
-
-public class WithOverlayTest extends OverlayBaseTest {
- public WithOverlayTest() {
- mMode = MODE_SINGLE_OVERLAY;
- }
-}
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
deleted file mode 100644
index 318cccc85461..000000000000
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.overlaytest;
-
-public class WithoutOverlayTest extends OverlayBaseTest {
- public WithoutOverlayTest() {
- mMode = MODE_NO_OVERLAY;
- }
-}
diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk
deleted file mode 100644
index ed330467f68a..000000000000
--- a/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml
deleted file mode 100644
index f8b6c7b888b5..000000000000
--- a/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="android" android:priority="1"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTest/res/drawable-nodpi/drawable.jpg b/core/tests/overlaytests/device/res/drawable-nodpi/drawable.jpg
index a3f14f325b85..a3f14f325b85 100644
--- a/core/tests/overlaytests/device/OverlayTest/res/drawable-nodpi/drawable.jpg
+++ b/core/tests/overlaytests/device/res/drawable-nodpi/drawable.jpg
Binary files differ
diff --git a/core/tests/overlaytests/device/OverlayTest/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/res/raw/lorem_ipsum.txt
index cee7a927c5fa..cee7a927c5fa 100644
--- a/core/tests/overlaytests/device/OverlayTest/res/raw/lorem_ipsum.txt
+++ b/core/tests/overlaytests/device/res/raw/lorem_ipsum.txt
diff --git a/core/tests/overlaytests/device/OverlayTest/res/values-sv/config.xml b/core/tests/overlaytests/device/res/values-sv/config.xml
index 891853edb4c5..891853edb4c5 100644
--- a/core/tests/overlaytests/device/OverlayTest/res/values-sv/config.xml
+++ b/core/tests/overlaytests/device/res/values-sv/config.xml
diff --git a/core/tests/overlaytests/device/OverlayTest/res/values/config.xml b/core/tests/overlaytests/device/res/values/config.xml
index c692a2625199..c692a2625199 100644
--- a/core/tests/overlaytests/device/OverlayTest/res/values/config.xml
+++ b/core/tests/overlaytests/device/res/values/config.xml
diff --git a/core/tests/overlaytests/device/OverlayTest/res/xml/integer.xml b/core/tests/overlaytests/device/res/xml/integer.xml
index 9383daa20b6c..9383daa20b6c 100644
--- a/core/tests/overlaytests/device/OverlayTest/res/xml/integer.xml
+++ b/core/tests/overlaytests/device/res/xml/integer.xml
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index e57c55ced046..96ab977b799c 100644
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -1,45 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.overlaytest;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.UiAutomation;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import android.test.AndroidTestCase;
+import android.os.LocaleList;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
import android.util.AttributeSet;
import android.util.Xml;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.util.Locale;
-public abstract class OverlayBaseTest extends AndroidTestCase {
+@Ignore
+public abstract class OverlayBaseTest {
private Resources mResources;
- protected int mMode; // will be set by subclasses
- static final protected int MODE_NO_OVERLAY = 0;
- static final protected int MODE_SINGLE_OVERLAY = 1;
- static final protected int MODE_MULTIPLE_OVERLAYS = 2;
+ private final int mMode;
+ static final int MODE_NO_OVERLAY = 0;
+ static final int MODE_SINGLE_OVERLAY = 1;
+ static final int MODE_MULTIPLE_OVERLAYS = 2;
+
+ static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
+ static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
+ static final String FRAMEWORK_OVERLAY_PKG = "com.android.overlaytest.framework";
+
+ protected OverlayBaseTest(int mode) {
+ mMode = mode;
+ }
- protected void setUp() {
- mResources = getContext().getResources();
+ @Before
+ public void setUp() {
+ mResources = InstrumentationRegistry.getContext().getResources();
}
private int calculateRawResourceChecksum(int resId) throws Throwable {
- InputStream input = null;
- try {
- input = mResources.openRawResource(resId);
+ try (InputStream input = mResources.openRawResource(resId)) {
int ch, checksum = 0;
while ((ch = input.read()) != -1) {
checksum = (checksum + ch) % 0xffddbb00;
}
return checksum;
- } finally {
- input.close();
}
}
private void setLocale(Locale locale) {
- Locale.setDefault(locale);
+ final LocaleList locales = new LocaleList(locale);
+ LocaleList.setDefault(locales);
Configuration config = new Configuration();
- config.locale = locale;
+ config.setLocales(locales);
mResources.updateConfiguration(config, mResources.getDisplayMetrics());
}
@@ -126,6 +161,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
}
}
+ @Test
public void testFrameworkBooleanOverlay() throws Throwable {
// config_annoy_dianne has the value:
// - true when no overlay exists (MODE_NO_OVERLAY)
@@ -135,6 +171,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
assertResource(resId, true, false, false);
}
+ @Test
public void testBooleanOverlay() throws Throwable {
// usually_false has the value:
// - false when no overlay exists (MODE_NO_OVERLAY)
@@ -144,12 +181,14 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
assertResource(resId, false, true, false);
}
+ @Test
public void testBoolean() throws Throwable {
// always_true has no overlay
final int resId = R.bool.always_true;
assertResource(resId, true, true, true);
}
+ @Test
public void testIntegerArrayOverlay() throws Throwable {
// fibonacci has values:
// - eight first values of Fibonacci sequence, when no overlay exists (MODE_NO_OVERLAY)
@@ -162,6 +201,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
new int[]{21, 13, 8, 5, 3, 2, 1, 1});
}
+ @Test
public void testIntegerArray() throws Throwable {
// prime_numbers has no overlay
final int resId = R.array.prime_numbers;
@@ -169,6 +209,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
assertResource(resId, expected, expected, expected);
}
+ @Test
public void testDrawable() throws Throwable {
// drawable-nodpi/drawable has overlay (default config)
final int resId = R.drawable.drawable;
@@ -188,16 +229,19 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
assertEquals(expected, actual);
}
+ @Test
public void testAppString() throws Throwable {
final int resId = R.string.str;
assertResource(resId, "none", "single", "multiple");
}
+ @Test
public void testApp2() throws Throwable {
final int resId = R.string.str2; // only in base package and first app overlay
assertResource(resId, "none", "single", "single");
}
+ @Test
public void testAppXml() throws Throwable {
int expected = getExpected(0, 1, 2);
int actual = -1;
@@ -214,6 +258,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
assertEquals(expected, actual);
}
+ @Test
public void testAppRaw() throws Throwable {
final int resId = R.raw.lorem_ipsum;
@@ -256,10 +301,10 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
* SLOT PACKAGE CONFIGURATION VALUE
* A target package (default) 100
* B target package -sv 200
- * C OverlayAppFirst (default) 300
- * D OverlayAppFirst -sv 400
- * E OverlayAppSecond (default) 500
- * F OverlayAppSecond -sv 600
+ * C AppOverlayOne (default) 300
+ * D AppOverlayOne -sv 400
+ * E AppOverlayTwo (default) 500
+ * F AppOverlayTwo -sv 600
*
* Example: in testMatrix101110, the base package defines the
* R.integer.matrix101110 resource for the default configuration (value
@@ -269,195 +314,283 @@ public abstract class OverlayBaseTest extends AndroidTestCase {
* are loaded, the expected value after setting the language to Swedish is
* 400.
*/
+ @Test
public void testMatrix100000() throws Throwable {
final int resId = R.integer.matrix_100000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 100);
}
+ @Test
public void testMatrix100001() throws Throwable {
final int resId = R.integer.matrix_100001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
+ @Test
public void testMatrix100010() throws Throwable {
final int resId = R.integer.matrix_100010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 500);
}
+ @Test
public void testMatrix100011() throws Throwable {
final int resId = R.integer.matrix_100011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
+ @Test
public void testMatrix100100() throws Throwable {
final int resId = R.integer.matrix_100100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix100101() throws Throwable {
final int resId = R.integer.matrix_100101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix100110() throws Throwable {
final int resId = R.integer.matrix_100110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix100111() throws Throwable {
final int resId = R.integer.matrix_100111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix101000() throws Throwable {
final int resId = R.integer.matrix_101000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 300);
}
+ @Test
public void testMatrix101001() throws Throwable {
final int resId = R.integer.matrix_101001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
+ @Test
public void testMatrix101010() throws Throwable {
final int resId = R.integer.matrix_101010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 500);
}
+ @Test
public void testMatrix101011() throws Throwable {
final int resId = R.integer.matrix_101011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
+ @Test
public void testMatrix101100() throws Throwable {
final int resId = R.integer.matrix_101100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix101101() throws Throwable {
final int resId = R.integer.matrix_101101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix101110() throws Throwable {
final int resId = R.integer.matrix_101110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix101111() throws Throwable {
final int resId = R.integer.matrix_101111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix110000() throws Throwable {
final int resId = R.integer.matrix_110000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix110001() throws Throwable {
final int resId = R.integer.matrix_110001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix110010() throws Throwable {
final int resId = R.integer.matrix_110010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix110011() throws Throwable {
final int resId = R.integer.matrix_110011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix110100() throws Throwable {
final int resId = R.integer.matrix_110100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix110101() throws Throwable {
final int resId = R.integer.matrix_110101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+ @Test
public void testMatrix110110() throws Throwable {
final int resId = R.integer.matrix_110110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix110111() throws Throwable {
final int resId = R.integer.matrix_110111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+ @Test
public void testMatrix111000() throws Throwable {
final int resId = R.integer.matrix_111000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix111001() throws Throwable {
final int resId = R.integer.matrix_111001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix111010() throws Throwable {
final int resId = R.integer.matrix_111010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix111011() throws Throwable {
final int resId = R.integer.matrix_111011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix111100() throws Throwable {
final int resId = R.integer.matrix_111100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix111101() throws Throwable {
final int resId = R.integer.matrix_111101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+ @Test
public void testMatrix111110() throws Throwable {
final int resId = R.integer.matrix_111110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix111111() throws Throwable {
final int resId = R.integer.matrix_111111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+
+ /**
+ * Executes the shell command and reads all the output to ensure the command ran and didn't
+ * get stuck buffering on output.
+ */
+ protected static String executeShellCommand(UiAutomation automation, String command)
+ throws Exception {
+ final ParcelFileDescriptor pfd = automation.executeShellCommand(command);
+ try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ final BufferedReader reader = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8));
+ StringBuilder str = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ str.append(line);
+ }
+ return str.toString();
+ }
+ }
+
+ /**
+ * Enables overlay packages and waits for a configuration change event before
+ * returning, to guarantee that Resources are up-to-date.
+ * @param packages the list of package names to enable.
+ */
+ protected static void enableOverlayPackages(String... packages) throws Exception {
+ enableOverlayPackages(true, packages);
+ }
+
+ /**
+ * Disables overlay packages and waits for a configuration change event before
+ * returning, to guarantee that Resources are up-to-date.
+ * @param packages the list of package names to disable.
+ */
+ protected static void disableOverlayPackages(String... packages) throws Exception {
+ enableOverlayPackages(false, packages);
+ }
+
+ /**
+ * Enables/disables overlay packages and waits for a configuration change event before
+ * returning, to guarantee that Resources are up-to-date.
+ * @param enable enables the overlays when true, disables when false.
+ * @param packages the list of package names to enable/disable.
+ */
+ private static void enableOverlayPackages(boolean enable, String[] packages)
+ throws Exception {
+ final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+ for (final String pkg : packages) {
+ executeShellCommand(uiAutomation,
+ "cmd overlay " + (enable ? "enable " : "disable ") + pkg);
+ }
+
+ // Wait for the overlay change to propagate.
+ Thread.sleep(1000);
+ }
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
new file mode 100644
index 000000000000..f35e511cdcf4
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class WithMultipleOverlaysTest extends OverlayBaseTest {
+ public WithMultipleOverlaysTest() {
+ super(MODE_MULTIPLE_OVERLAYS);
+ }
+
+ @BeforeClass
+ public static void enableOverlay() throws Exception {
+ enableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
new file mode 100644
index 000000000000..037449fc2c05
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class WithOverlayTest extends OverlayBaseTest {
+ public WithOverlayTest() {
+ super(MODE_SINGLE_OVERLAY);
+ }
+
+ @BeforeClass
+ public static void enableOverlay() throws Exception {
+ disableOverlayPackages(APP_OVERLAY_TWO_PKG);
+ enableOverlayPackages(APP_OVERLAY_ONE_PKG, FRAMEWORK_OVERLAY_PKG);
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
new file mode 100644
index 000000000000..f657b5cef0e5
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class WithoutOverlayTest extends OverlayBaseTest {
+ public WithoutOverlayTest() {
+ super(MODE_NO_OVERLAY);
+ }
+
+ @BeforeClass
+ public static void disableOverlays() throws Exception {
+ disableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+ }
+}
diff --git a/core/tests/overlaytests/device/test-apps/Android.mk b/core/tests/overlaytests/device/test-apps/Android.mk
new file mode 100644
index 000000000000..9af9f444ca59
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/Android.mk
@@ -0,0 +1,15 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include $(call all-subdir-makefiles)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
new file mode 100644
index 000000000000..17e20eeeda85
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayOne
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
new file mode 100644
index 000000000000..17191589e3f2
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.app_overlay_one"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.overlaytest" android:priority="1" />
+</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/drawable-nodpi/drawable.jpg b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/drawable-nodpi/drawable.jpg
index 0d944d02d633..0d944d02d633 100644
--- a/core/tests/overlaytests/device/OverlayAppFirst/res/drawable-nodpi/drawable.jpg
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/drawable-nodpi/drawable.jpg
Binary files differ
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/raw/lorem_ipsum.txt
index 756b0a3fc532..756b0a3fc532 100644
--- a/core/tests/overlaytests/device/OverlayAppFirst/res/raw/lorem_ipsum.txt
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/raw/lorem_ipsum.txt
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/values-sv/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values-sv/config.xml
index 9cdc73e27ade..9cdc73e27ade 100644
--- a/core/tests/overlaytests/device/OverlayAppFirst/res/values-sv/config.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values-sv/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml
index 972137a3d1bf..972137a3d1bf 100644
--- a/core/tests/overlaytests/device/OverlayAppFirst/res/values/config.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/xml/integer.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/integer.xml
index 7f628d9125f3..7f628d9125f3 100644
--- a/core/tests/overlaytests/device/OverlayAppFirst/res/xml/integer.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/integer.xml
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
new file mode 100644
index 000000000000..c24bea9e06e9
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayTwo
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml
new file mode 100644
index 000000000000..ae8307c446c1
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.app_overlay_two"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.overlaytest" android:priority="2" />
+</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/raw/lorem_ipsum.txt
index 613f5b63c66c..613f5b63c66c 100644
--- a/core/tests/overlaytests/device/OverlayAppSecond/res/raw/lorem_ipsum.txt
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/raw/lorem_ipsum.txt
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/values-sv/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values-sv/config.xml
index ec4b6c03e5ff..ec4b6c03e5ff 100644
--- a/core/tests/overlaytests/device/OverlayAppSecond/res/values-sv/config.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values-sv/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values/config.xml
index 8b072160ffba..8b072160ffba 100644
--- a/core/tests/overlaytests/device/OverlayAppSecond/res/values/config.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/xml/integer.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/xml/integer.xml
index f3370a6b8bcf..f3370a6b8bcf 100644
--- a/core/tests/overlaytests/device/OverlayAppSecond/res/xml/integer.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/xml/integer.xml
diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk
new file mode 100644
index 000000000000..dc811c51e926
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests_FrameworkOverlay
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml
new file mode 100644
index 000000000000..77ea16afff83
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.framework"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:priority="1" />
+</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/res/values/config.xml b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/res/values/config.xml
index c1e3de12059a..c1e3de12059a 100644
--- a/core/tests/overlaytests/device/OverlayTestOverlay/res/values/config.xml
+++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/res/values/config.xml
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
index 2d6843948f29..b08ac96aca68 100644
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
@@ -15,6 +15,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.om.hosttest.signature_overlay">
+ package="com.android.server.om.hosttest.signature_overlay">
<overlay android:targetPackage="android" />
</manifest>
diff --git a/core/tests/overlaytests/testrunner.py b/core/tests/overlaytests/testrunner.py
deleted file mode 100755
index e88805e8cbf1..000000000000
--- a/core/tests/overlaytests/testrunner.py
+++ /dev/null
@@ -1,732 +0,0 @@
-#!/usr/bin/python
-import hashlib
-import optparse
-import os
-import re
-import shlex
-import subprocess
-import sys
-import threading
-import time
-
-TASK_COMPILATION = 'compile'
-TASK_DISABLE_OVERLAYS = 'disable overlays'
-TASK_ENABLE_MULTIPLE_OVERLAYS = 'enable multiple overlays'
-TASK_ENABLE_SINGLE_OVERLAY = 'enable single overlay'
-TASK_ENABLE_FILTERED_OVERLAYS = 'enable filtered overlays'
-TASK_FILE_EXISTS_TEST = 'test (file exists)'
-TASK_GREP_IDMAP_TEST = 'test (grep idmap)'
-TASK_MD5_TEST = 'test (md5)'
-TASK_IDMAP_PATH = 'idmap --path'
-TASK_IDMAP_SCAN = 'idmap --scan'
-TASK_INSTRUMENTATION = 'instrumentation'
-TASK_INSTRUMENTATION_TEST = 'test (instrumentation)'
-TASK_MKDIR = 'mkdir'
-TASK_PUSH = 'push'
-TASK_ROOT = 'root'
-TASK_REMOUNT = 'remount'
-TASK_RM = 'rm'
-TASK_SETPROP = 'setprop'
-TASK_SETUP_IDMAP_PATH = 'setup idmap --path'
-TASK_SETUP_IDMAP_SCAN = 'setup idmap --scan'
-TASK_START = 'start'
-TASK_STOP = 'stop'
-
-adb = 'adb'
-
-def _adb_shell(cmd):
- argv = shlex.split(adb + " shell '" + cmd + "; echo $?'")
- proc = subprocess.Popen(argv, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- (stdout, stderr) = (stdout.replace('\r', ''), stderr.replace('\r', ''))
- tmp = stdout.rsplit('\n', 2)
- if len(tmp) == 2:
- stdout == ''
- returncode = int(tmp[0])
- else:
- stdout = tmp[0] + '\n'
- returncode = int(tmp[1])
- return returncode, stdout, stderr
-
-class VerbosePrinter:
- class Ticker(threading.Thread):
- def _print(self):
- s = '\r' + self.text + '[' + '.' * self.i + ' ' * (4 - self.i) + ']'
- sys.stdout.write(s)
- sys.stdout.flush()
- self.i = (self.i + 1) % 5
-
- def __init__(self, cond_var, text):
- threading.Thread.__init__(self)
- self.text = text
- self.setDaemon(True)
- self.cond_var = cond_var
- self.running = False
- self.i = 0
- self._print()
- self.running = True
-
- def run(self):
- self.cond_var.acquire()
- while True:
- self.cond_var.wait(0.25)
- running = self.running
- if not running:
- break
- self._print()
- self.cond_var.release()
-
- def stop(self):
- self.cond_var.acquire()
- self.running = False
- self.cond_var.notify_all()
- self.cond_var.release()
-
- def _start_ticker(self):
- self.ticker = VerbosePrinter.Ticker(self.cond_var, self.text)
- self.ticker.start()
-
- def _stop_ticker(self):
- self.ticker.stop()
- self.ticker.join()
- self.ticker = None
-
- def _format_begin(self, type, name):
- N = self.width - len(type) - len(' [ ] ')
- fmt = '%%s %%-%ds ' % N
- return fmt % (type, name)
-
- def __init__(self, use_color):
- self.cond_var = threading.Condition()
- self.ticker = None
- if use_color:
- self.color_RED = '\033[1;31m'
- self.color_red = '\033[0;31m'
- self.color_reset = '\033[0;37m'
- else:
- self.color_RED = ''
- self.color_red = ''
- self.color_reset = ''
-
- argv = shlex.split('stty size') # get terminal width
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- if proc.returncode == 0:
- (h, w) = stdout.split()
- self.width = int(w)
- else:
- self.width = 72 # conservative guesstimate
-
- def begin(self, type, name):
- self.text = self._format_begin(type, name)
- sys.stdout.write(self.text + '[ ]')
- sys.stdout.flush()
- self._start_ticker()
-
- def end_pass(self, type, name):
- self._stop_ticker()
- sys.stdout.write('\r' + self.text + '[ OK ]\n')
- sys.stdout.flush()
-
- def end_fail(self, type, name, msg):
- self._stop_ticker()
- sys.stdout.write('\r' + self.color_RED + self.text + '[FAIL]\n')
- sys.stdout.write(self.color_red)
- sys.stdout.write(msg)
- sys.stdout.write(self.color_reset)
- sys.stdout.flush()
-
-class QuietPrinter:
- def begin(self, type, name):
- pass
-
- def end_pass(self, type, name):
- sys.stdout.write('PASS ' + type + ' ' + name + '\n')
- sys.stdout.flush()
-
- def end_fail(self, type, name, msg):
- sys.stdout.write('FAIL ' + type + ' ' + name + '\n')
- sys.stdout.flush()
-
-class CompilationTask:
- def __init__(self, makefile):
- self.makefile = makefile
-
- def get_type(self):
- return TASK_COMPILATION
-
- def get_name(self):
- return self.makefile
-
- def execute(self):
- os.putenv('ONE_SHOT_MAKEFILE', os.getcwd() + "/" + self.makefile)
- argv = shlex.split('make -C "../../../../../" files')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- return proc.returncode, stdout, stderr
-
-class InstrumentationTask:
- def __init__(self, instrumentation_class):
- self.instrumentation_class = instrumentation_class
-
- def get_type(self):
- return TASK_INSTRUMENTATION
-
- def get_name(self):
- return self.instrumentation_class
-
- def execute(self):
- return _adb_shell('am instrument -r -w -e class %s com.android.overlaytest/android.test.InstrumentationTestRunner' % self.instrumentation_class)
-
-class PushTask:
- def __init__(self, src, dest):
- self.src = src
- self.dest = dest
-
- def get_type(self):
- return TASK_PUSH
-
- def get_name(self):
- return "%s -> %s" % (self.src, self.dest)
-
- def execute(self):
- src = os.getenv('OUT')
- if (src is None):
- return 1, "", "Unable to proceed - $OUT environment var not set\n"
- src += "/" + self.src
- argv = shlex.split(adb + ' push %s %s' % (src, self.dest))
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- return proc.returncode, stdout, stderr
-
-class MkdirTask:
- def __init__(self, path):
- self.path = path
-
- def get_type(self):
- return TASK_MKDIR
-
- def get_name(self):
- return self.path
-
- def execute(self):
- return _adb_shell('mkdir -p %s' % self.path)
-
-class RmTask:
- def __init__(self, path):
- self.path = path
-
- def get_type(self):
- return TASK_RM
-
- def get_name(self):
- return self.path
-
- def execute(self):
- returncode, stdout, stderr = _adb_shell('ls %s' % self.path)
- if returncode != 0 and stderr.endswith(': No such file or directory\n'):
- return 0, "", ""
- return _adb_shell('rm -r %s' % self.path)
-
-class SetPropTask:
- def __init__(self, prop, value):
- self.prop = prop
- self.value = value
-
- def get_type(self):
- return TASK_SETPROP
-
- def get_name(self):
- return self.prop
-
- def execute(self):
- return _adb_shell('setprop %s %s' % (self.prop, self.value))
-
-class IdmapPathTask:
- def __init__(self, path_target_apk, path_overlay_apk, path_idmap):
- self.path_target_apk = path_target_apk
- self.path_overlay_apk = path_overlay_apk
- self.path_idmap = path_idmap
-
- def get_type(self):
- return TASK_IDMAP_PATH
-
- def get_name(self):
- return self.path_idmap
-
- def execute(self):
- return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.target_pkg_name, self.target_pkg, self.idmap_dir, self.overlay_dir))
-
-class IdmapScanTask:
- def __init__(self, overlay_dir, target_pkg_name, target_pkg, idmap_dir, symlink_dir):
- self.overlay_dir = overlay_dir
- self.target_pkg_name = target_pkg_name
- self.target_pkg = target_pkg
- self.idmap_dir = idmap_dir
- self.symlink_dir = symlink_dir
-
- def get_type(self):
- return TASK_IDMAP_SCAN
-
- def get_name(self):
- return self.target_pkg_name
-
- def execute(self):
- return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.overlay_dir, self.target_pkg_name, self.target_pkg, self.idmap_dir))
-
-class FileExistsTest:
- def __init__(self, path):
- self.path = path
-
- def get_type(self):
- return TASK_FILE_EXISTS_TEST
-
- def get_name(self):
- return self.path
-
- def execute(self):
- return _adb_shell('ls %s' % self.path)
-
-class GrepIdmapTest:
- def __init__(self, path_idmap, pattern, expected_n):
- self.path_idmap = path_idmap
- self.pattern = pattern
- self.expected_n = expected_n
-
- def get_type(self):
- return TASK_GREP_IDMAP_TEST
-
- def get_name(self):
- return self.pattern
-
- def execute(self):
- returncode, stdout, stderr = _adb_shell('idmap --inspect %s' % self.path_idmap)
- if returncode != 0:
- return returncode, stdout, stderr
- all_matches = re.findall('\s' + self.pattern + '$', stdout, flags=re.MULTILINE)
- if len(all_matches) != self.expected_n:
- return 1, 'pattern=%s idmap=%s expected=%d found=%d\n' % (self.pattern, self.path_idmap, self.expected_n, len(all_matches)), ''
- return 0, "", ""
-
-class Md5Test:
- def __init__(self, path, expected_content):
- self.path = path
- self.expected_md5 = hashlib.md5(expected_content).hexdigest()
-
- def get_type(self):
- return TASK_MD5_TEST
-
- def get_name(self):
- return self.path
-
- def execute(self):
- returncode, stdout, stderr = _adb_shell('md5sum %s' % self.path)
- if returncode != 0:
- return returncode, stdout, stderr
- actual_md5 = stdout.split()[0]
- if actual_md5 != self.expected_md5:
- return 1, 'expected %s, got %s\n' % (self.expected_md5, actual_md5), ''
- return 0, "", ""
-
-class StartTask:
- def get_type(self):
- return TASK_START
-
- def get_name(self):
- return ""
-
- def execute(self):
- (returncode, stdout, stderr) = _adb_shell('start')
- if returncode != 0:
- return returncode, stdout, stderr
-
- while True:
- (returncode, stdout, stderr) = _adb_shell('getprop dev.bootcomplete')
- if returncode != 0:
- return returncode, stdout, stderr
- if stdout.strip() == "1":
- break
- time.sleep(0.5)
-
- return 0, "", ""
-
-class StopTask:
- def get_type(self):
- return TASK_STOP
-
- def get_name(self):
- return ""
-
- def execute(self):
- (returncode, stdout, stderr) = _adb_shell('stop')
- if returncode != 0:
- return returncode, stdout, stderr
- return _adb_shell('setprop dev.bootcomplete 0')
-
-class RootTask:
- def get_type(self):
- return TASK_ROOT
-
- def get_name(self):
- return ""
-
- def execute(self):
- (returncode, stdout, stderr) = _adb_shell('getprop service.adb.root 0')
- if returncode != 0:
- return returncode, stdout, stderr
- if stdout.strip() == '1': # already root
- return 0, "", ""
-
- argv = shlex.split(adb + ' root')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- if proc.returncode != 0:
- return proc.returncode, stdout, stderr
-
- argv = shlex.split(adb + ' wait-for-device')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- return proc.returncode, stdout, stderr
-
-class RemountTask:
- def get_type(self):
- return TASK_REMOUNT
-
- def get_name(self):
- return ""
-
- def execute(self):
- argv = shlex.split(adb + ' remount')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- # adb remount returns 0 even if the operation failed, so check stdout
- if stdout.startswith('remount failed:'):
- return 1, stdout, stderr
- return proc.returncode, stdout, stderr
-
-class CompoundTask:
- def __init__(self, type, tasks):
- self.type = type
- self.tasks = tasks
-
- def get_type(self):
- return self.type
-
- def get_name(self):
- return ""
-
- def execute(self):
- for t in self.tasks:
- (returncode, stdout, stderr) = t.execute()
- if returncode != 0:
- return returncode, stdout, stderr
- return 0, "", ""
-
-def _create_disable_overlays_task():
- tasks = [
- RmTask("/vendor/overlay/framework_a.apk"),
- RmTask("/vendor/overlay/framework_b.apk"),
- RmTask("/data/resource-cache/vendor@overlay@framework_a.apk@idmap"),
- RmTask("/data/resource-cache/vendor@overlay@framework_b.apk@idmap"),
- RmTask("/vendor/overlay/app_a.apk"),
- RmTask("/vendor/overlay/app_b.apk"),
- RmTask("/vendor/overlay/app_c.apk"),
- RmTask("/data/resource-cache/vendor@overlay@app_a.apk@idmap"),
- RmTask("/data/resource-cache/vendor@overlay@app_b.apk@idmap"),
- RmTask("/data/resource-cache/vendor@overlay@app_c.apk@idmap"),
- SetPropTask('persist.oem.overlay.test', '""'),
- RmTask("/data/property/persist.oem.overlay.test"),
- ]
- return CompoundTask(TASK_DISABLE_OVERLAYS, tasks)
-
-def _create_enable_single_overlay_task():
- tasks = [
- _create_disable_overlays_task(),
- MkdirTask('/system/vendor'),
- MkdirTask('/vendor/overlay'),
- PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_a.apk'),
- PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
- ]
- return CompoundTask(TASK_ENABLE_SINGLE_OVERLAY, tasks)
-
-def _create_enable_multiple_overlays_task():
- tasks = [
- _create_disable_overlays_task(),
- MkdirTask('/system/vendor'),
- MkdirTask('/vendor/overlay'),
-
- PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
- PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
- PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
- PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
- ]
- return CompoundTask(TASK_ENABLE_MULTIPLE_OVERLAYS, tasks)
-
-def _create_enable_filtered_overlays_task():
- tasks = [
- _create_disable_overlays_task(),
- SetPropTask('persist.oem.overlay.test', 'foo'),
- MkdirTask('/system/vendor'),
- MkdirTask('/vendor/overlay'),
- PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
- PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
- PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
- PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
- ]
- return CompoundTask(TASK_ENABLE_FILTERED_OVERLAYS, tasks)
-
-def _create_setup_idmap_path_task(idmaps, symlinks):
- tasks = [
- _create_enable_single_overlay_task(),
- RmTask(symlinks),
- RmTask(idmaps),
- MkdirTask(idmaps),
- MkdirTask(symlinks),
- ]
- return CompoundTask(TASK_SETUP_IDMAP_PATH, tasks)
-
-def _create_setup_idmap_scan_task(idmaps, symlinks):
- tasks = [
- _create_enable_filtered_overlays_task(),
- RmTask(symlinks),
- RmTask(idmaps),
- MkdirTask(idmaps),
- MkdirTask(symlinks),
- ]
- return CompoundTask(TASK_SETUP_IDMAP_SCAN, tasks)
-
-def _handle_instrumentation_task_output(stdout, printer):
- regex_status_code = re.compile(r'^INSTRUMENTATION_STATUS_CODE: -?(\d+)')
- regex_name = re.compile(r'^INSTRUMENTATION_STATUS: test=(.*)')
- regex_begin_stack = re.compile(r'^INSTRUMENTATION_STATUS: stack=(.*)')
- regex_end_stack = re.compile(r'^$')
-
- failed_tests = 0
- current_test = None
- current_stack = []
- mode_stack = False
- for line in stdout.split("\n"):
- line = line.rstrip() # strip \r from adb output
- m = regex_status_code.match(line)
- if m:
- c = int(m.group(1))
- if c == 1:
- printer.begin(TASK_INSTRUMENTATION_TEST, current_test)
- elif c == 0:
- printer.end_pass(TASK_INSTRUMENTATION_TEST, current_test)
- else:
- failed_tests += 1
- current_stack.append("\n")
- msg = "\n".join(current_stack)
- printer.end_fail(TASK_INSTRUMENTATION_TEST, current_test, msg.rstrip() + '\n')
- continue
-
- m = regex_name.match(line)
- if m:
- current_test = m.group(1)
- continue
-
- m = regex_begin_stack.match(line)
- if m:
- mode_stack = True
- current_stack = []
- current_stack.append(" " + m.group(1))
- continue
-
- m = regex_end_stack.match(line)
- if m:
- mode_stack = False
- continue
-
- if mode_stack:
- current_stack.append(" " + line.strip())
-
- return failed_tests
-
-def _set_adb_device(option, opt, value, parser):
- global adb
- if opt == '-d' or opt == '--device':
- adb = 'adb -d'
- if opt == '-e' or opt == '--emulator':
- adb = 'adb -e'
- if opt == '-s' or opt == '--serial':
- adb = 'adb -s ' + value
-
-def _create_opt_parser():
- parser = optparse.OptionParser()
- parser.add_option('-d', '--device', action='callback', callback=_set_adb_device,
- help='pass -d to adb')
- parser.add_option('-e', '--emulator', action='callback', callback=_set_adb_device,
- help='pass -e to adb')
- parser.add_option('-s', '--serial', type="str", action='callback', callback=_set_adb_device,
- help='pass -s <serical> to adb')
- parser.add_option('-C', '--no-color', action='store_false',
- dest='use_color', default=True,
- help='disable color escape sequences in output')
- parser.add_option('-q', '--quiet', action='store_true',
- dest='quiet_mode', default=False,
- help='quiet mode, output only results')
- parser.add_option('-b', '--no-build', action='store_false',
- dest='do_build', default=True,
- help='do not rebuild test projects')
- parser.add_option('-k', '--continue', action='store_true',
- dest='do_continue', default=False,
- help='do not rebuild test projects')
- parser.add_option('-i', '--test-idmap', action='store_true',
- dest='test_idmap', default=False,
- help='run tests for idmap')
- parser.add_option('-0', '--test-no-overlay', action='store_true',
- dest='test_no_overlay', default=False,
- help='run tests without any overlay')
- parser.add_option('-1', '--test-single-overlay', action='store_true',
- dest='test_single_overlay', default=False,
- help='run tests for single overlay')
- parser.add_option('-2', '--test-multiple-overlays', action='store_true',
- dest='test_multiple_overlays', default=False,
- help='run tests for multiple overlays')
- parser.add_option('-3', '--test-filtered-overlays', action='store_true',
- dest='test_filtered_overlays', default=False,
- help='run tests for filtered (sys prop) overlays')
- return parser
-
-if __name__ == '__main__':
- opt_parser = _create_opt_parser()
- opts, args = opt_parser.parse_args(sys.argv[1:])
- if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays and not opts.test_filtered_overlays:
- opts.test_idmap = True
- opts.test_no_overlay = True
- opts.test_single_overlay = True
- opts.test_multiple_overlays = True
- opts.test_filtered_overlays = True
-
- if len(args) > 0:
- opt_parser.error("unexpected arguments: %s" % " ".join(args))
- # will never reach this: opt_parser.error will call sys.exit
-
- if opts.quiet_mode:
- printer = QuietPrinter()
- else:
- printer = VerbosePrinter(opts.use_color)
- tasks = []
-
- # must be in the same directory as this script for compilation tasks to work
- script = sys.argv[0]
- dirname = os.path.dirname(script)
- wd = os.path.realpath(dirname)
- os.chdir(wd)
-
- # build test cases
- if opts.do_build:
- tasks.append(CompilationTask('OverlayTest/Android.mk'))
- tasks.append(CompilationTask('OverlayTestOverlay/Android.mk'))
- tasks.append(CompilationTask('OverlayAppFirst/Android.mk'))
- tasks.append(CompilationTask('OverlayAppSecond/Android.mk'))
- tasks.append(CompilationTask('OverlayAppFiltered/Android.mk'))
-
- # remount filesystem, install test project
- tasks.append(RootTask())
- tasks.append(RemountTask())
- tasks.append(PushTask('/system/app/OverlayTest/OverlayTest.apk', '/system/app/OverlayTest.apk'))
-
- # test idmap
- if opts.test_idmap:
- idmaps='/data/local/tmp/idmaps'
- symlinks='/data/local/tmp/symlinks'
-
- # idmap --path
- tasks.append(StopTask())
- tasks.append(_create_setup_idmap_path_task(idmaps, symlinks))
- tasks.append(StartTask())
- tasks.append(IdmapPathTask('/vendor/overlay/framework_a.apk', '/system/framework/framework-res.apk', idmaps + '/a.idmap'))
- tasks.append(FileExistsTest(idmaps + '/a.idmap'))
- tasks.append(GrepIdmapTest(idmaps + '/a.idmap', 'bool/config_annoy_dianne', 1))
-
- # idmap --scan
- tasks.append(StopTask())
- tasks.append(_create_setup_idmap_scan_task(idmaps, symlinks))
- tasks.append(StartTask())
- tasks.append(IdmapScanTask('/vendor/overlay', 'android', '/system/framework/framework-res.apk', idmaps, symlinks))
- tasks.append(FileExistsTest(idmaps + '/vendor@overlay@framework_b.apk@idmap'))
- tasks.append(GrepIdmapTest(idmaps + '/vendor@overlay@framework_b.apk@idmap', 'bool/config_annoy_dianne', 1))
-
-
- # overlays.list
- overlays_list_path = idmaps + '/overlays.list'
- expected_content = '''\
-/vendor/overlay/framework_b.apk /data/local/tmp/idmaps/vendor@overlay@framework_b.apk@idmap
-'''
- tasks.append(FileExistsTest(overlays_list_path))
- tasks.append(Md5Test(overlays_list_path, expected_content))
-
- # idmap cleanup
- tasks.append(RmTask(symlinks))
- tasks.append(RmTask(idmaps))
-
- # test no overlay: all overlays cleared
- if opts.test_no_overlay:
- tasks.append(StopTask())
- tasks.append(_create_disable_overlays_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithoutOverlayTest'))
-
- # test single overlay: one overlay (a)
- if opts.test_single_overlay:
- tasks.append(StopTask())
- tasks.append(_create_enable_single_overlay_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithOverlayTest'))
-
- # test multiple overlays: all overlays - including 'disabled' filtered
- # overlay (system property unset) so expect 'b[p=2]' overrides 'a[p=1]' but
- # 'c[p=3]' should be ignored
- if opts.test_multiple_overlays:
- tasks.append(StopTask())
- tasks.append(_create_enable_multiple_overlays_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithMultipleOverlaysTest'))
-
- # test filtered overlays: all overlays - including 'enabled' filtered
- # overlay (system property set/matched) so expect c[p=3] to override both a
- # & b where applicable
- if opts.test_filtered_overlays:
- tasks.append(StopTask())
- tasks.append(_create_enable_filtered_overlays_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithFilteredOverlaysTest'))
-
- ignored_errors = 0
- for t in tasks:
- type = t.get_type()
- name = t.get_name()
- if type == TASK_INSTRUMENTATION:
- # InstrumentationTask will run several tests, but we want it
- # to appear as if each test was run individually. Calling
- # "am instrument" with a single test method is prohibitively
- # expensive, so let's instead post-process the output to
- # emulate individual calls.
- retcode, stdout, stderr = t.execute()
- if retcode != 0:
- printer.begin(TASK_INSTRUMENTATION, name)
- printer.end_fail(TASK_INSTRUMENTATION, name, stderr)
- sys.exit(retcode)
- retcode = _handle_instrumentation_task_output(stdout, printer)
- if retcode != 0:
- if not opts.do_continue:
- sys.exit(retcode)
- else:
- ignored_errors += retcode
- else:
- printer.begin(type, name)
- retcode, stdout, stderr = t.execute()
- if retcode == 0:
- printer.end_pass(type, name)
- if retcode != 0:
- if len(stderr) == 0:
- # hope for output from stdout instead (true for eg adb shell rm)
- stderr = stdout
- printer.end_fail(type, name, stderr)
- if not opts.do_continue:
- sys.exit(retcode)
- else:
- ignored_errors += retcode
- sys.exit(ignored_errors)
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8addffbb02db..a678c4d49092 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -157,6 +157,7 @@ applications that come with the platform
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
@@ -227,6 +228,7 @@ applications that come with the platform
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING" />
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index dad24da6ad98..22867df76aea 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -333,6 +333,9 @@
<family lang="und-Cari">
<font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
</family>
+ <family lang="und-Cakm">
+ <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+ </family>
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
@@ -429,6 +432,9 @@
<family lang="und-Orkh">
<font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
</family>
+ <family lang="und-Osge">
+ <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
+ </family>
<family lang="und-Osma">
<font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
</family>
diff --git a/docs/html/reference/images/text/style/drawablemarginspan.png b/docs/html/reference/images/text/style/drawablemarginspan.png
new file mode 100644
index 000000000000..edf926d80130
--- /dev/null
+++ b/docs/html/reference/images/text/style/drawablemarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/dynamicdrawablespan.png b/docs/html/reference/images/text/style/dynamicdrawablespan.png
new file mode 100644
index 000000000000..8776b0387278
--- /dev/null
+++ b/docs/html/reference/images/text/style/dynamicdrawablespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/iconmarginspan.png b/docs/html/reference/images/text/style/iconmarginspan.png
new file mode 100644
index 000000000000..8ec39be039f6
--- /dev/null
+++ b/docs/html/reference/images/text/style/iconmarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/imagespan.png b/docs/html/reference/images/text/style/imagespan.png
new file mode 100644
index 000000000000..c03e6bb68bd5
--- /dev/null
+++ b/docs/html/reference/images/text/style/imagespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/maskfilterspan.png b/docs/html/reference/images/text/style/maskfilterspan.png
new file mode 100644
index 000000000000..6e55dbc1509c
--- /dev/null
+++ b/docs/html/reference/images/text/style/maskfilterspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/stylespan.png b/docs/html/reference/images/text/style/stylespan.png
new file mode 100644
index 000000000000..9ffa05b5ac43
--- /dev/null
+++ b/docs/html/reference/images/text/style/stylespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/tabstopspan.png b/docs/html/reference/images/text/style/tabstopspan.png
new file mode 100644
index 000000000000..89a1121d0745
--- /dev/null
+++ b/docs/html/reference/images/text/style/tabstopspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/typefacespan.png b/docs/html/reference/images/text/style/typefacespan.png
new file mode 100644
index 000000000000..67e2cf9b0468
--- /dev/null
+++ b/docs/html/reference/images/text/style/typefacespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/urlspan.png b/docs/html/reference/images/text/style/urlspan.png
new file mode 100644
index 000000000000..11345206e594
--- /dev/null
+++ b/docs/html/reference/images/text/style/urlspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 627d5515b77a..69a5874e9381 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -541,10 +541,19 @@ public abstract class BaseCanvas {
return mAllowHwBitmapsInSwMode;
}
+ /**
+ * @hide
+ */
+ protected void onHwBitmapInSwMode() {
+ if (!mAllowHwBitmapsInSwMode) {
+ throw new IllegalArgumentException(
+ "Software rendering doesn't support hardware bitmaps");
+ }
+ }
+
private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
- if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
- && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
- throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+ if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+ onHwBitmapInSwMode();
}
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 0072012f69ad..44e7066c5c66 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -29,6 +29,10 @@ import android.os.StrictMode;
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+
import libcore.util.NativeAllocationRegistry;
import java.io.OutputStream;
@@ -1171,6 +1175,82 @@ public final class Bitmap implements Parcelable {
}
/**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
+ * width and height the same as the Picture's width and height and a Config.HARDWARE
+ * config.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
+ return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
+ }
+
+ /**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * The bitmap will be immutable with the given width and height. If the width and height
+ * are not the same as the Picture's width & height, the Picture will be scaled to
+ * fit the given width and height.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @param width The width of the bitmap to create. The picture's width will be
+ * scaled to match if necessary.
+ * @param height The height of the bitmap to create. The picture's height will be
+ * scaled to match if necessary.
+ * @param config The {@link Config} of the created bitmap. If this is null then
+ * the bitmap will be {@link Config#HARDWARE}.
+ *
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
+ @NonNull Config config) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width & height must be > 0");
+ }
+ if (config == null) {
+ throw new IllegalArgumentException("Config must not be null");
+ }
+ if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
+ StrictMode.noteSlowCall("GPU readback");
+ }
+ if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
+ final RenderNode node = RenderNode.create("BitmapTemporary", null);
+ node.setLeftTopRightBottom(0, 0, width, height);
+ node.setClipToBounds(false);
+ final DisplayListCanvas canvas = node.start(width, height);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ node.end(canvas);
+ Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+ if (config != Config.HARDWARE) {
+ bitmap = bitmap.copy(config, false);
+ }
+ return bitmap;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+ Canvas canvas = new Canvas(bitmap);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ canvas.setBitmap(null);
+ bitmap.makeImmutable();
+ return bitmap;
+ }
+ }
+
+ /**
* Returns an optional array of private data, used by the UI system for
* some bitmaps. Not intended to be called by applications.
*/
@@ -1259,6 +1339,12 @@ public final class Bitmap implements Parcelable {
return mIsMutable;
}
+ /** @hide */
+ public final void makeImmutable() {
+ // todo mIsMutable = false;
+ // todo nMakeImmutable();
+ }
+
/**
* <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
* When a pixel is pre-multiplied, the RGB components have been multiplied by
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index d77e6012fb46..fe2b52341063 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -160,25 +160,6 @@ public class FontFamily {
isItalic);
}
- /**
- * Allow creating unsupported FontFamily.
- *
- * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
- * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
- * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
- * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
- * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
- * the top of the fallback chain but is never used. if we don't create this empty FontFamily
- * and put it at top, bad things (performance regressions, unexpected glyph selection) will
- * happen.
- */
- public void allowUnsupportedFont() {
- if (mBuilderPtr == 0) {
- throw new IllegalStateException("Unable to allow unsupported font.");
- }
- nAllowUnsupportedFont(mBuilderPtr);
- }
-
// TODO: Remove once internal user stop using private API.
private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -190,9 +171,6 @@ public class FontFamily {
private static native long nCreateFamily(long mBuilderPtr);
@CriticalNative
- private static native void nAllowUnsupportedFont(long builderPtr);
-
- @CriticalNative
private static native void nAbort(long mBuilderPtr);
@CriticalNative
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index bbf214559459..acefead785c4 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -74,7 +74,7 @@ public final class ImageDecoder implements AutoCloseable {
int getDensity() { return Bitmap.DENSITY_NONE; }
/* @hide */
- int computeDstDensity() {
+ final int computeDstDensity() {
Resources res = getResources();
if (res == null) {
return Bitmap.getDefaultDensity();
@@ -122,13 +122,19 @@ public final class ImageDecoder implements AutoCloseable {
}
private static class ContentResolverSource extends Source {
- ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
+ ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri,
+ @Nullable Resources res) {
mResolver = resolver;
mUri = uri;
+ mResources = res;
}
private final ContentResolver mResolver;
private final Uri mUri;
+ private final Resources mResources;
+
+ @Nullable
+ Resources getResources() { return mResources; }
@Override
public ImageDecoder createImageDecoder() throws IOException {
@@ -438,6 +444,7 @@ public final class ImageDecoder implements AutoCloseable {
private boolean mPreferRamOverQuality = false;
private boolean mAsAlphaMask = false;
private Rect mCropRect;
+ private Rect mOutPaddingRect;
private Source mSource;
private PostProcessor mPostProcessor;
@@ -511,7 +518,18 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull
public static Source createSource(@NonNull ContentResolver cr,
@NonNull Uri uri) {
- return new ContentResolverSource(cr, uri);
+ return new ContentResolverSource(cr, uri, null);
+ }
+
+ /**
+ * Provide Resources for density scaling.
+ *
+ * @hide
+ */
+ @NonNull
+ public static Source createSource(@NonNull ContentResolver cr,
+ @NonNull Uri uri, @Nullable Resources res) {
+ return new ContentResolverSource(cr, uri, res);
}
/**
@@ -765,6 +783,18 @@ public final class ImageDecoder implements AutoCloseable {
}
/**
+ * Set a Rect for retrieving nine patch padding.
+ *
+ * If the image is a nine patch, this Rect will be set to the padding
+ * rectangle during decode. Otherwise it will not be modified.
+ *
+ * @hide
+ */
+ public void setOutPaddingRect(@NonNull Rect outPadding) {
+ mOutPaddingRect = outPadding;
+ }
+
+ /**
* Specify whether the {@link Bitmap} should be mutable.
*
* <p>By default, a {@link Bitmap} created will be immutable, but that can
@@ -875,7 +905,6 @@ public final class ImageDecoder implements AutoCloseable {
postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mRequireUnpremultiplied,
mPreferRamOverQuality, mAsAlphaMask);
-
}
private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -948,7 +977,10 @@ public final class ImageDecoder implements AutoCloseable {
if (np != null && NinePatch.isNinePatchChunk(np)) {
Rect opticalInsets = new Rect();
bm.getOpticalInsets(opticalInsets);
- Rect padding = new Rect();
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding == null) {
+ padding = new Rect();
+ }
nGetPadding(decoder.mNativePtr, padding);
return new NinePatchDrawable(res, bm, np, padding,
opticalInsets, null);
@@ -991,6 +1023,15 @@ public final class ImageDecoder implements AutoCloseable {
final int srcDensity = computeDensity(src, decoder);
Bitmap bm = decoder.decodeBitmap();
bm.setDensity(srcDensity);
+
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding != null) {
+ byte[] np = bm.getNinePatchChunk();
+ if (np != null && NinePatch.isNinePatchChunk(np)) {
+ nGetPadding(decoder.mNativePtr, padding);
+ }
+ }
+
return bm;
}
}
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 08eeaff69f9b..9ac94d895a37 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -31,8 +31,9 @@ import java.io.OutputStream;
* be replayed on a hardware accelerated canvas.</p>
*/
public class Picture {
- private Canvas mRecordingCanvas;
+ private PictureCanvas mRecordingCanvas;
private long mNativePicture;
+ private boolean mRequiresHwAcceleration;
private static final int WORKING_STREAM_STORAGE = 16 * 1024;
@@ -78,8 +79,12 @@ public class Picture {
* into it.
*/
public Canvas beginRecording(int width, int height) {
+ if (mRecordingCanvas != null) {
+ throw new IllegalStateException("Picture already recording, must call #endRecording()");
+ }
long ni = nativeBeginRecording(mNativePicture, width, height);
- mRecordingCanvas = new RecordingCanvas(this, ni);
+ mRecordingCanvas = new PictureCanvas(this, ni);
+ mRequiresHwAcceleration = false;
return mRecordingCanvas;
}
@@ -91,6 +96,7 @@ public class Picture {
*/
public void endRecording() {
if (mRecordingCanvas != null) {
+ mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
mRecordingCanvas = null;
nativeEndRecording(mNativePicture);
}
@@ -113,6 +119,18 @@ public class Picture {
}
/**
+ * Indicates whether or not this Picture contains recorded commands that only work when
+ * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
+ * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
+ *
+ * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
+ * false otherwise.
+ */
+ public boolean requiresHardwareAcceleration() {
+ return mRequiresHwAcceleration;
+ }
+
+ /**
* Draw this picture on the canvas.
* <p>
* Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could
@@ -129,6 +147,9 @@ public class Picture {
if (mRecordingCanvas != null) {
endRecording();
}
+ if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
+ canvas.onHwBitmapInSwMode();
+ }
nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
}
@@ -164,8 +185,7 @@ public class Picture {
if (stream == null) {
throw new NullPointerException();
}
- if (!nativeWriteToStream(mNativePicture, stream,
- new byte[WORKING_STREAM_STORAGE])) {
+ if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
throw new RuntimeException();
}
}
@@ -182,10 +202,11 @@ public class Picture {
OutputStream stream, byte[] storage);
private static native void nativeDestructor(long nativePicture);
- private static class RecordingCanvas extends Canvas {
+ private static class PictureCanvas extends Canvas {
private final Picture mPicture;
+ boolean mHoldsHwBitmap;
- public RecordingCanvas(Picture pict, long nativeCanvas) {
+ public PictureCanvas(Picture pict, long nativeCanvas) {
super(nativeCanvas);
mPicture = pict;
}
@@ -202,5 +223,10 @@ public class Picture {
}
super.drawPicture(picture);
}
+
+ @Override
+ protected void onHwBitmapInSwMode() {
+ mHoldsHwBitmap = true;
+ }
}
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index ef4150763139..04c5295539ac 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -818,12 +818,9 @@ public class Typeface {
if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
null /* axes */)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback
- // font selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return Typeface.DEFAULT;
+ }
final FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
@@ -870,12 +867,9 @@ public class Typeface {
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback font
- // selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return Typeface.DEFAULT;
+ }
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index bd49b87ec200..27c8fda0d6e9 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -348,7 +348,9 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
if (mState == null) {
throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
}
- nStop(mState.mNativePtr);
+ if (nStop(mState.mNativePtr)) {
+ postOnAnimationEnd();
+ }
}
// Animatable2 overrides
@@ -365,21 +367,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
nSetOnAnimationEndListener(mState.mNativePtr, this);
}
- mAnimationCallbacks.add(callback);
+ if (!mAnimationCallbacks.contains(callback)) {
+ mAnimationCallbacks.add(callback);
+ }
}
@Override
public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
- if (callback == null || mAnimationCallbacks == null) {
+ if (callback == null || mAnimationCallbacks == null
+ || !mAnimationCallbacks.remove(callback)) {
return false;
}
- return mAnimationCallbacks.remove(callback);
+ if (mAnimationCallbacks.isEmpty()) {
+ clearAnimationCallbacks();
+ }
+
+ return true;
}
@Override
public void clearAnimationCallbacks() {
- mAnimationCallbacks = null;
+ if (mAnimationCallbacks != null) {
+ mAnimationCallbacks = null;
+ nSetOnAnimationEndListener(mState.mNativePtr, null);
+ }
}
private void postOnAnimationStart() {
@@ -413,6 +425,21 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
return mHandler;
}
+ /**
+ * Called by JNI.
+ *
+ * The JNI code has already posted this to the thread that created the
+ * callback, so no need to post.
+ */
+ @SuppressWarnings("unused")
+ private void onAnimationEnd() {
+ if (mAnimationCallbacks != null) {
+ for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+ callback.onAnimationEnd(this);
+ }
+ }
+ }
+
private static native long nCreate(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
@@ -432,7 +459,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
@FastNative
private static native boolean nStart(long nativePtr);
@FastNative
- private static native void nStop(long nativePtr);
+ private static native boolean nStop(long nativePtr);
@FastNative
private static native void nSetLoopCount(long nativePtr, int loopCount);
// Pass the drawable down to native so it can call onAnimationEnd.
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 7ad062a6f6f9..44b783bb6e63 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Outline;
@@ -49,6 +50,7 @@ import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable {
*/
@Deprecated
public BitmapDrawable() {
- mBitmapState = new BitmapState((Bitmap) null);
+ init(new BitmapState((Bitmap) null), null);
}
/**
@@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable {
@SuppressWarnings("unused")
@Deprecated
public BitmapDrawable(Resources res) {
- mBitmapState = new BitmapState((Bitmap) null);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState((Bitmap) null), res);
}
/**
@@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable {
*/
@Deprecated
public BitmapDrawable(Bitmap bitmap) {
- this(new BitmapState(bitmap), null);
+ init(new BitmapState(bitmap), null);
}
/**
@@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable {
* the display metrics of the resources.
*/
public BitmapDrawable(Resources res, Bitmap bitmap) {
- this(new BitmapState(bitmap), res);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState(bitmap), res);
}
/**
@@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable {
*/
@Deprecated
public BitmapDrawable(String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
- }
+ this(null, filepath);
}
/**
@@ -165,10 +162,21 @@ public class BitmapDrawable extends Drawable {
*/
@SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ Bitmap bitmap = null;
+ try (FileInputStream stream = new FileInputStream(filepath)) {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+ (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeFile()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ }
}
}
@@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable {
*/
@Deprecated
public BitmapDrawable(java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
- }
+ this(null, is);
}
/**
@@ -190,10 +195,21 @@ public class BitmapDrawable extends Drawable {
*/
@SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ Bitmap bitmap = null;
+ try {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+ (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeStream()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ }
}
}
@@ -812,9 +828,19 @@ public class BitmapDrawable extends Drawable {
}
}
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+
Bitmap bitmap = null;
try (InputStream is = r.openRawResource(srcResId, value)) {
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+ ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+ bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
} catch (Exception e) {
// Do nothing and pick up the error below.
}
@@ -1013,14 +1039,21 @@ public class BitmapDrawable extends Drawable {
}
}
+ private BitmapDrawable(BitmapState state, Resources res) {
+ init(state, res);
+ }
+
/**
- * The one constructor to rule them all. This is called by all public
+ * The one helper to rule them all. This is called by all public & private
* constructors to set the state and initialize local properties.
*/
- private BitmapDrawable(BitmapState state, Resources res) {
+ private void init(BitmapState state, Resources res) {
mBitmapState = state;
-
updateLocalState(res);
+
+ if (mBitmapState != null && res != null) {
+ mBitmapState.mTargetDensity = mTargetDensity;
+ }
}
/**
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index f17cd768c386..36a4d26d62bb 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
@@ -50,11 +51,13 @@ import android.graphics.Xfermode;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.View;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -1175,6 +1178,10 @@ public abstract class Drawable {
return null;
}
+ if (opts == null) {
+ return getBitmapDrawable(res, value, is);
+ }
+
/* ugh. The decodeStream contract is that we have already allocated
the pad rect, but if the bitmap does not had a ninepatch chunk,
then the pad will be ignored. If we could change this to lazily
@@ -1207,6 +1214,33 @@ public abstract class Drawable {
return null;
}
+ private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+ try {
+ ImageDecoder.Source source = null;
+ if (value != null) {
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+ source = ImageDecoder.createSource(res, is, density);
+ } else {
+ source = ImageDecoder.createSource(res, is);
+ }
+
+ return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException e) {
+ /* do nothing.
+ If the exception happened on decode, the drawable will be null.
+ */
+ Log.e("Drawable", "Unable to decode stream: " + e);
+ }
+ return null;
+ }
+
/**
* Create a drawable from an XML document. For more information on how to
* create resources in XML, see
@@ -1306,11 +1340,10 @@ public abstract class Drawable {
}
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
- try {
- Bitmap bm = BitmapFactory.decodeFile(pathName);
- if (bm != null) {
- return drawableFromBitmap(null, bm, null, null, null, pathName);
- }
+ try (FileInputStream stream = new FileInputStream(pathName)) {
+ return getBitmapDrawable(null, null, stream);
+ } catch(IOException e) {
+ // Do nothing; we will just return null if the FileInputStream had an error
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 17900204fa22..a56e8d1b25ed 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -24,9 +24,9 @@ import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
@@ -211,7 +211,8 @@ public class NinePatchDrawable extends Drawable {
restoreAlpha = -1;
}
- final boolean needsDensityScaling = canvas.getDensity() == 0;
+ final boolean needsDensityScaling = canvas.getDensity() == 0
+ && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity();
if (needsDensityScaling) {
restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();
@@ -421,10 +422,6 @@ public class NinePatchDrawable extends Drawable {
final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
if (srcResId != 0) {
- final BitmapFactory.Options options = new BitmapFactory.Options();
- options.inDither = !state.mDither;
- options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
-
final Rect padding = new Rect();
final Rect opticalInsets = new Rect();
Bitmap bitmap = null;
@@ -433,7 +430,17 @@ public class NinePatchDrawable extends Drawable {
final TypedValue value = new TypedValue();
final InputStream is = r.openRawResource(srcResId, value);
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+ ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+ bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+ decoder.setOutPaddingRect(padding);
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
is.close();
} catch (IOException e) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index f721ed3af7ba..09b3b9b523b4 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -248,7 +248,8 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
spec.getUserAuthenticationValidityDurationSeconds(),
spec.isUserAuthenticationValidWhileOnBody(),
spec.isInvalidatedByBiometricEnrollment(),
- GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+ GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+ spec.isUserConfirmationRequired());
} catch (IllegalStateException | IllegalArgumentException e) {
throw new InvalidAlgorithmParameterException(e);
}
@@ -289,7 +290,8 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
spec.getUserAuthenticationValidityDurationSeconds(),
spec.isUserAuthenticationValidWhileOnBody(),
spec.isInvalidatedByBiometricEnrollment(),
- GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+ GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+ spec.isUserConfirmationRequired());
if (spec.isTrustedUserPresenceRequired()) {
args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index d1eb6888bbfd..e33e3cd4e92b 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -349,7 +349,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mSpec.getUserAuthenticationValidityDurationSeconds(),
mSpec.isUserAuthenticationValidWhileOnBody(),
mSpec.isInvalidatedByBiometricEnrollment(),
- GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+ GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+ mSpec.isUserConfirmationRequired());
} catch (IllegalArgumentException | IllegalStateException e) {
throw new InvalidAlgorithmParameterException(e);
}
@@ -545,7 +546,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mSpec.getUserAuthenticationValidityDurationSeconds(),
mSpec.isUserAuthenticationValidWhileOnBody(),
mSpec.isInvalidatedByBiometricEnrollment(),
- GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+ GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
+ mSpec.isUserConfirmationRequired());
args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
mSpec.getKeyValidityForOriginationEnd());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 9df37f58fd09..7bbc09964584 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -190,6 +190,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
&& !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
}
+ boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+
return new KeyInfo(entryAlias,
insideSecureHardware,
origin,
@@ -207,7 +209,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
userAuthenticationRequirementEnforcedBySecureHardware,
userAuthenticationValidWhileOnBody,
trustedUserPresenceRequred,
- invalidatedByBiometricEnrollment);
+ invalidatedByBiometricEnrollment,
+ userConfirmationRequired);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 440e0863fbb1..05cc74a0bec9 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -502,7 +502,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
spec.getUserAuthenticationValidityDurationSeconds(),
spec.isUserAuthenticationValidWhileOnBody(),
spec.isInvalidatedByBiometricEnrollment(),
- spec.getBoundToSpecificSecureUserId());
+ spec.getBoundToSpecificSecureUserId(),
+ spec.isUserConfirmationRequired());
importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
spec.getKeyValidityStart());
importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -704,7 +705,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
params.getUserAuthenticationValidityDurationSeconds(),
params.isUserAuthenticationValidWhileOnBody(),
params.isInvalidatedByBiometricEnrollment(),
- params.getBoundToSpecificSecureUserId());
+ params.getBoundToSpecificSecureUserId(),
+ params.isUserConfirmationRequired());
KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
args,
keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index a896c72463fb..da23c70f58bb 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -264,6 +264,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
private final boolean mUserAuthenticationValidWhileOnBody;
private final boolean mInvalidatedByBiometricEnrollment;
private final boolean mIsStrongBoxBacked;
+ private final boolean mUserConfirmationRequired;
/**
* @hide should be built with Builder
@@ -293,7 +294,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
boolean uniqueIdIncluded,
boolean userAuthenticationValidWhileOnBody,
boolean invalidatedByBiometricEnrollment,
- boolean isStrongBoxBacked) {
+ boolean isStrongBoxBacked,
+ boolean userConfirmationRequired) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -341,6 +343,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mIsStrongBoxBacked = isStrongBoxBacked;
+ mUserConfirmationRequired = userConfirmationRequired;
}
/**
@@ -547,6 +550,26 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
}
/**
+ * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+ * user.
+ *
+ * Confirmation is separate from user authentication (see
+ * {@link Builder#setUserAuthenticationRequired(boolean)}). Keys can be created that require
+ * confirmation but not user authentication, or user authentication but not confirmation, or
+ * both. Confirmation verifies that some user with physical possession of the device has
+ * approved a displayed message. User authentication verifies that the correct user is present
+ * and has authenticated.
+ *
+ * <p>This authorization applies only to secret key and private key operations. Public key
+ * operations are not restricted.
+ *
+ * @see Builder#setUserConfirmationRequired(boolean)
+ */
+ public boolean isUserConfirmationRequired() {
+ return mUserConfirmationRequired;
+ }
+
+ /**
* Gets the duration of time (seconds) for which this key is authorized to be used after the
* user is successfully authenticated. This has effect only if user authentication is required
* (see {@link #isUserAuthenticationRequired()}).
@@ -675,6 +698,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
private boolean mUserAuthenticationValidWhileOnBody;
private boolean mInvalidatedByBiometricEnrollment = true;
private boolean mIsStrongBoxBacked = false;
+ private boolean mUserConfirmationRequired;
/**
* Creates a new instance of the {@code Builder}.
@@ -1063,6 +1087,29 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
}
/**
+ * Sets whether this key is authorized to be used only for messages confirmed by the
+ * user.
+ *
+ * Confirmation is separate from user authentication (see
+ * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
+ * confirmation but not user authentication, or user authentication but not confirmation,
+ * or both. Confirmation verifies that some user with physical possession of the device has
+ * approved a displayed message. User authentication verifies that the correct user is
+ * present and has authenticated.
+ *
+ * <p>This authorization applies only to secret key and private key operations. Public key
+ * operations are not restricted.
+ *
+ * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
+ * more details about user confirmations.
+ */
+ @NonNull
+ public Builder setUserConfirmationRequired(boolean required) {
+ mUserConfirmationRequired = required;
+ return this;
+ }
+
+ /**
* Sets the duration of time (seconds) for which this key is authorized to be used after the
* user is successfully authenticated. This has effect if the key requires user
* authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}).
@@ -1249,7 +1296,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
mUniqueIdIncluded,
mUserAuthenticationValidWhileOnBody,
mInvalidatedByBiometricEnrollment,
- mIsStrongBoxBacked);
+ mIsStrongBoxBacked,
+ mUserConfirmationRequired);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 864f62a6de04..0a75cd5268b7 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -82,6 +82,7 @@ public class KeyInfo implements KeySpec {
private final boolean mUserAuthenticationValidWhileOnBody;
private final boolean mTrustedUserPresenceRequired;
private final boolean mInvalidatedByBiometricEnrollment;
+ private final boolean mUserConfirmationRequired;
/**
* @hide
@@ -103,7 +104,8 @@ public class KeyInfo implements KeySpec {
boolean userAuthenticationRequirementEnforcedBySecureHardware,
boolean userAuthenticationValidWhileOnBody,
boolean trustedUserPresenceRequired,
- boolean invalidatedByBiometricEnrollment) {
+ boolean invalidatedByBiometricEnrollment,
+ boolean userConfirmationRequired) {
mKeystoreAlias = keystoreKeyAlias;
mInsideSecureHardware = insideSecureHardware;
mOrigin = origin;
@@ -125,6 +127,7 @@ public class KeyInfo implements KeySpec {
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
mTrustedUserPresenceRequired = trustedUserPresenceRequired;
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
+ mUserConfirmationRequired = userConfirmationRequired;
}
/**
@@ -260,6 +263,27 @@ public class KeyInfo implements KeySpec {
}
/**
+ * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+ * user.
+ *
+ * Confirmation is separate from user authentication (see
+ * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
+ * not user authentication, or user authentication but not confirmation, or both. Confirmation
+ * verifies that some user with physical possession of the device has approved a displayed
+ * message. User authentication verifies that the correct user is present and has
+ * authenticated.
+ *
+ * <p>This authorization applies only to secret key and private key operations. Public key
+ * operations are not restricted.
+ *
+ * @see KeyGenParameterSpec.Builder#setUserConfirmationRequired(boolean)
+ * @see KeyProtection.Builder#setUserConfirmationRequired(boolean)
+ */
+ public boolean isUserConfirmationRequired() {
+ return mUserConfirmationRequired;
+ }
+
+ /**
* Gets the duration of time (seconds) for which this key is authorized to be used after the
* user is successfully authenticated. This has effect only if user authentication is required
* (see {@link #isUserAuthenticationRequired()}).
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index dbacb9c53dd6..b5b328192f21 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -228,6 +228,7 @@ public final class KeyProtection implements ProtectionParameter {
private final boolean mInvalidatedByBiometricEnrollment;
private final long mBoundToSecureUserId;
private final boolean mCriticalToDeviceEncryption;
+ private final boolean mUserConfirmationRequired;
private KeyProtection(
Date keyValidityStart,
@@ -244,7 +245,8 @@ public final class KeyProtection implements ProtectionParameter {
boolean userAuthenticationValidWhileOnBody,
boolean invalidatedByBiometricEnrollment,
long boundToSecureUserId,
- boolean criticalToDeviceEncryption) {
+ boolean criticalToDeviceEncryption,
+ boolean userConfirmationRequired) {
mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -262,6 +264,7 @@ public final class KeyProtection implements ProtectionParameter {
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mBoundToSecureUserId = boundToSecureUserId;
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+ mUserConfirmationRequired = userConfirmationRequired;
}
/**
@@ -396,6 +399,26 @@ public final class KeyProtection implements ProtectionParameter {
}
/**
+ * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+ * user.
+ *
+ * Confirmation is separate from user authentication (see
+ * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
+ * not user authentication, or user authentication but not confirmation, or both. Confirmation
+ * verifies that some user with physical possession of the device has approved a displayed
+ * message. User authentication verifies that the correct user is present and has
+ * authenticated.
+ *
+ * <p>This authorization applies only to secret key and private key operations. Public key
+ * operations are not restricted.
+ *
+ * @see Builder#setUserConfirmationRequired(boolean)
+ */
+ public boolean isUserConfirmationRequired() {
+ return mUserConfirmationRequired;
+ }
+
+ /**
* Gets the duration of time (seconds) for which this key is authorized to be used after the
* user is successfully authenticated. This has effect only if user authentication is required
* (see {@link #isUserAuthenticationRequired()}).
@@ -488,6 +511,7 @@ public final class KeyProtection implements ProtectionParameter {
private int mUserAuthenticationValidityDurationSeconds = -1;
private boolean mUserAuthenticationValidWhileOnBody;
private boolean mInvalidatedByBiometricEnrollment = true;
+ private boolean mUserConfirmationRequired;
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
private boolean mCriticalToDeviceEncryption = false;
@@ -719,6 +743,29 @@ public final class KeyProtection implements ProtectionParameter {
}
/**
+ * Sets whether this key is authorized to be used only for messages confirmed by the
+ * user.
+ *
+ * Confirmation is separate from user authentication (see
+ * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
+ * confirmation but not user authentication, or user authentication but not confirmation,
+ * or both. Confirmation verifies that some user with physical possession of the device has
+ * approved a displayed message. User authentication verifies that the correct user is
+ * present and has authenticated.
+ *
+ * <p>This authorization applies only to secret key and private key operations. Public key
+ * operations are not restricted.
+ *
+ * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
+ * more details about user confirmations.
+ */
+ @NonNull
+ public Builder setUserConfirmationRequired(boolean required) {
+ mUserConfirmationRequired = required;
+ return this;
+ }
+
+ /**
* Sets the duration of time (seconds) for which this key is authorized to be used after the
* user is successfully authenticated. This has effect if the key requires user
* authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}).
@@ -866,7 +913,8 @@ public final class KeyProtection implements ProtectionParameter {
mUserAuthenticationValidWhileOnBody,
mInvalidatedByBiometricEnrollment,
mBoundToSecureUserId,
- mCriticalToDeviceEncryption);
+ mCriticalToDeviceEncryption,
+ mUserConfirmationRequired);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 34c8d1f75f60..4e28601f17a1 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -16,6 +16,7 @@
package android.security.keystore;
+import android.util.Log;
import android.hardware.fingerprint.FingerprintManager;
import android.security.GateKeeper;
import android.security.KeyStore;
@@ -93,6 +94,8 @@ public abstract class KeymasterUtils {
* overriding the default logic in this method where the key is bound to either the root
* SID of the current user, or the fingerprint SID if explicit fingerprint authorization
* is requested.
+ * @param userConfirmationRequired whether user confirmation is required to authorize the use
+ * of the key.
* @throws IllegalStateException if user authentication is required but the system is in a wrong
* state (e.g., secure lock screen not set up) for generating or importing keys that
* require user authentication.
@@ -102,7 +105,12 @@ public abstract class KeymasterUtils {
int userAuthenticationValidityDurationSeconds,
boolean userAuthenticationValidWhileOnBody,
boolean invalidatedByBiometricEnrollment,
- long boundToSpecificSecureUserId) {
+ long boundToSpecificSecureUserId,
+ boolean userConfirmationRequired) {
+ if (userConfirmationRequired) {
+ args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+ }
+
if (!userAuthenticationRequired) {
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
return;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 3323bce8b5ad..24d819e93ff2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -134,6 +134,8 @@ cc_defaults {
name: "libhwui_defaults",
defaults: ["hwui_defaults"],
+ shared_libs: ["libstatslog"],
+
whole_static_libs: ["libskia"],
srcs: [
@@ -318,7 +320,10 @@ cc_test {
"libgmock",
"libhwui_static_debug",
],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
cflags: [
"-include debug/wrap_gles.h",
"-DHWUI_NULL_GPU",
@@ -383,7 +388,10 @@ cc_benchmark {
// set to libhwui_static_debug to skip actual GL commands
whole_static_libs: ["libhwui"],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
srcs: [
"tests/macrobench/TestSceneRunner.cpp",
@@ -405,7 +413,10 @@ cc_benchmark {
],
whole_static_libs: ["libhwui_static_debug"],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
srcs: [
"tests/microbench/main.cpp",
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index f41956cdebeb..ab27a0d00246 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -18,6 +18,7 @@
#include <errno.h>
#include <inttypes.h>
+#include <statslog.h>
#include <sys/mman.h>
#include <algorithm>
@@ -164,6 +165,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
ALOGI("%s", ss.str().c_str());
// Just so we have something that counts up, the value is largely irrelevant
ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
+ android::util::stats_write(android::util::DAVEY_OCCURRED, ns2ms(totalDuration));
}
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 3d2c2520624b..55f4d895265c 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -507,12 +507,20 @@ public:
getOutline().getAlpha() != 0.0f;
}
- SkColor getShadowColor() const {
- return mPrimitiveFields.mShadowColor;
+ SkColor getSpotShadowColor() const {
+ return mPrimitiveFields.mSpotShadowColor;
}
- bool setShadowColor(SkColor shadowColor) {
- return RP_SET(mPrimitiveFields.mShadowColor, shadowColor);
+ bool setSpotShadowColor(SkColor shadowColor) {
+ return RP_SET(mPrimitiveFields.mSpotShadowColor, shadowColor);
+ }
+
+ SkColor getAmbientShadowColor() const {
+ return mPrimitiveFields.mAmbientShadowColor;
+ }
+
+ bool setAmbientShadowColor(SkColor shadowColor) {
+ return RP_SET(mPrimitiveFields.mAmbientShadowColor, shadowColor);
}
bool fitsOnLayer() const {
@@ -538,7 +546,8 @@ private:
int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0;
int mWidth = 0, mHeight = 0;
int mClippingFlags = CLIP_TO_BOUNDS;
- SkColor mShadowColor = SK_ColorBLACK;
+ SkColor mSpotShadowColor = SK_ColorBLACK;
+ SkColor mAmbientShadowColor = SK_ColorBLACK;
float mAlpha = 1;
float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0;
float mElevation = 0;
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 5356d3bfc7c9..2bded9b3a2d8 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -48,8 +48,10 @@ bool AnimatedImageDrawable::start() {
return true;
}
-void AnimatedImageDrawable::stop() {
+bool AnimatedImageDrawable::stop() {
+ bool wasRunning = mRunning;
mRunning = false;
+ return wasRunning;
}
bool AnimatedImageDrawable::isRunning() {
@@ -180,7 +182,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
if (finalFrame) {
if (mEndListener) {
mEndListener->onAnimationEnd();
- mEndListener = nullptr;
}
}
}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 9d84ed5f4470..2fd6f40b71b5 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -68,7 +68,9 @@ public:
// Returns true if the animation was started; false otherwise (e.g. it was
// already running)
bool start();
- void stop();
+ // Returns true if the animation was stopped; false otherwise (e.g. it was
+ // already stopped)
+ bool stop();
bool isRunning();
void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 43f46ef758ca..a0d000dd2179 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -44,7 +44,6 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
minikinPaint.familyVariant = paint->getFamilyVariant();
minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
- minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
return minikinPaint;
}
@@ -55,18 +54,23 @@ minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFla
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
minikin::Layout layout;
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
if (mt == nullptr) {
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf,range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
- if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize),
- minikin::Range(start, start + count),
- minikinPaint, bidiFlags, mtOffset, &layout)) {
+ if (mt->buildLayout(textBuf, range, minikinPaint, bidiFlags, mtOffset, startHyphen, endHyphen,
+ &layout)) {
return layout;
}
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf, range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
@@ -74,8 +78,14 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize, float* advances) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
- return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
- advances, nullptr /* extent */);
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
+ return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
+ endHyphen, advances, nullptr /* extent */);
}
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index ebc14c8b675b..091b5267881d 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -182,10 +182,11 @@ void Typeface::setRobotoTypefaceForTest() {
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
- std::shared_ptr<minikin::FontCollection> collection =
- std::make_shared<minikin::FontCollection>(std::move(family));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+
+ std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
+ std::make_shared<minikin::FontFamily>(std::move(fonts)));
Typeface* hwTypeface = new Typeface();
hwTypeface->fFontCollection = collection;
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 7b59ccf3eec7..25c51f2716e6 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -111,6 +111,10 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
}
}
+static SkColor multiplyAlpha(SkColor color, float alpha) {
+ return SkColorSetA(color, alpha * SkColorGetA(color));
+}
+
// copied from FrameBuilder::deferShadow
void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
const RenderProperties& casterProperties = caster->getNodeProperties();
@@ -187,9 +191,11 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
} else {
zParams = SkPoint3::Make(0, 0, casterProperties.getZ());
}
+ SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha);
+ SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha);
SkShadowUtils::DrawShadow(
canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(),
- ambientAlpha, spotAlpha, casterProperties.getShadowColor(),
+ ambientColor, spotColor,
casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 66d6f527e604..2232c25de345 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -56,8 +56,9 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- return std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+ return std::make_shared<minikin::FontFamily>(std::move(fonts));
}
std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 018db9a7fc9d..fa3f99a90cc8 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -88,11 +88,6 @@ interface ILocationManager
boolean providerMeetsCriteria(String provider, in Criteria criteria);
ProviderProperties getProviderProperties(String provider);
String getNetworkProviderPackage();
- boolean isProviderEnabled(String provider);
- boolean isProviderEnabledForUser(String provider, int userId);
- boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
- boolean isLocationEnabledForUser(int userId);
- void setLocationEnabledForUser(boolean enabled, int userId);
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b07d04220046..f98480b28001 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -265,6 +265,12 @@ public final class AudioFormat implements Parcelable {
public static final int ENCODING_AAC_XHE = 16;
/** Audio data format: AC-4 sync frame transport format */
public static final int ENCODING_AC4 = 17;
+ /** Audio data format: E-AC-3-JOC compressed
+ * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}.
+ * Use {@link #ENCODING_E_AC3} as the AudioTrack encoding when the downstream device
+ * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
+ **/
+ public static final int ENCODING_E_AC3_JOC = 18;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -512,6 +518,7 @@ public final class AudioFormat implements Parcelable {
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -537,6 +544,7 @@ public final class AudioFormat implements Parcelable {
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -564,6 +572,7 @@ public final class AudioFormat implements Parcelable {
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -593,6 +602,7 @@ public final class AudioFormat implements Parcelable {
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -829,6 +839,7 @@ public final class AudioFormat implements Parcelable {
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -1044,6 +1055,7 @@ public final class AudioFormat implements Parcelable {
ENCODING_PCM_FLOAT,
ENCODING_AC3,
ENCODING_E_AC3,
+ ENCODING_E_AC3_JOC,
ENCODING_DTS,
ENCODING_DTS_HD,
ENCODING_IEC61937,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 22fa6202df5c..bf51d97f6b2f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -48,11 +48,13 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.view.KeyEvent;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -4578,6 +4580,51 @@ public class AudioManager {
}
}
+ /**
+ * Set port id for microphones by matching device type and address.
+ * @hide
+ */
+ public static void setPortIdForMicrophones(ArrayList<MicrophoneInfo> microphones) {
+ AudioDeviceInfo[] devices = getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
+ for (int i = microphones.size() - 1; i >= 0; i--) {
+ boolean foundPortId = false;
+ for (AudioDeviceInfo device : devices) {
+ if (device.getPort().type() == microphones.get(i).getInternalDeviceType()
+ && TextUtils.equals(device.getAddress(), microphones.get(i).getAddress())) {
+ microphones.get(i).setId(device.getId());
+ foundPortId = true;
+ break;
+ }
+ }
+ if (!foundPortId) {
+ Log.i(TAG, "Failed to find port id for device with type:"
+ + microphones.get(i).getType() + " address:"
+ + microphones.get(i).getAddress());
+ microphones.remove(i);
+ }
+ }
+ }
+
+ /**
+ * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
+ * of all available microphones. The list is empty when no microphones are available
+ * on the device. An error during the query will result in an IOException being thrown.
+ *
+ * @return a list that contains all microphones' characteristics
+ * @throws IOException if an error occurs.
+ */
+ public List<MicrophoneInfo> getMicrophones() throws IOException {
+ ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
+ int status = AudioSystem.getMicrophones(microphones);
+ if (status != AudioManager.SUCCESS) {
+ // fail and bail!
+ Log.e(TAG, "getMicrophones failed:" + status);
+ return new ArrayList<MicrophoneInfo>(); // Always return a list.
+ }
+ setPortIdForMicrophones(microphones);
+ return microphones;
+ }
+
// Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
// (unpredictable) last time updateAudioPortCache() was called by someone, keep a list
// of the ports that exist at the time of the last notification.
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
new file mode 100644
index 000000000000..4652c180936c
--- /dev/null
+++ b/media/java/android/media/AudioPresentation.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * The AudioPresentation class encapsulates the information that describes an audio presentation
+ * which is available in next generation audio content.
+ *
+ * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and
+ * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available
+ * presentations and to select one.
+ *
+ * A list of available audio presentations in a media source can be queried using
+ * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for
+ * selection.
+ * An AudioPresentation can be passed to an offloaded audio decoder via
+ * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected
+ * presentation. An audio stream may contain multiple presentations that differ by language,
+ * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have
+ * a set of description labels in different languages to help the user to make an informed
+ * selection.
+ */
+public final class AudioPresentation {
+ private final int mPresentationId;
+ private final int mProgramId;
+ private final Map<String, String> mLabels;
+ private final String mLanguage;
+
+ /** @hide */
+ @IntDef(
+ value = {
+ MASTERING_NOT_INDICATED,
+ MASTERED_FOR_STEREO,
+ MASTERED_FOR_SURROUND,
+ MASTERED_FOR_3D,
+ MASTERED_FOR_HEADPHONE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MasteringIndicationType {}
+
+ private final @MasteringIndicationType int mMasteringIndication;
+ private final boolean mAudioDescriptionAvailable;
+ private final boolean mSpokenSubtitlesAvailable;
+ private final boolean mDialogueEnhancementAvailable;
+
+ /**
+ * No preferred reproduction channel layout.
+ */
+ public static final int MASTERING_NOT_INDICATED = 0;
+ /**
+ * Stereo speaker layout.
+ */
+ public static final int MASTERED_FOR_STEREO = 1;
+ /**
+ * Two-dimensional (e.g. 5.1) speaker layout.
+ */
+ public static final int MASTERED_FOR_SURROUND = 2;
+ /**
+ * Three-dimensional (e.g. 5.1.2) speaker layout.
+ */
+ public static final int MASTERED_FOR_3D = 3;
+ /**
+ * Prerendered for headphone playback.
+ */
+ public static final int MASTERED_FOR_HEADPHONE = 4;
+
+ AudioPresentation(int presentationId,
+ int programId,
+ Map<String, String> labels,
+ String language,
+ @MasteringIndicationType int masteringIndication,
+ boolean audioDescriptionAvailable,
+ boolean spokenSubtitlesAvailable,
+ boolean dialogueEnhancementAvailable) {
+ this.mPresentationId = presentationId;
+ this.mProgramId = programId;
+ this.mLanguage = language;
+ this.mMasteringIndication = masteringIndication;
+ this.mAudioDescriptionAvailable = audioDescriptionAvailable;
+ this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
+ this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+
+ this.mLabels = new HashMap<String, String>(labels);
+ }
+
+ /**
+ * The framework uses this presentation id to select an audio presentation rendered by a
+ * decoder. Presentation id is typically sequential, but does not have to be.
+ * @hide
+ */
+ public int getPresentationId() {
+ return mPresentationId;
+ }
+
+ /**
+ * The framework uses this program id to select an audio presentation rendered by a decoder.
+ * Program id can be used to further uniquely identify the presentation to a decoder.
+ * @hide
+ */
+ public int getProgramId() {
+ return mProgramId;
+ }
+
+ /**
+ * @return a map of available text labels for this presentation. Each label is indexed by its
+ * locale corresponding to the language code as specified by ISO 639-2 [42]. Either ISO 639-2/B
+ * or ISO 639-2/T could be used.
+ */
+ public Map<Locale, String> getLabels() {
+ Map<Locale, String> localeLabels = new HashMap<>();
+ for (Map.Entry<String, String> entry : mLabels.entrySet()) {
+ localeLabels.put(new Locale(entry.getKey()), entry.getValue());
+ }
+ return localeLabels;
+ }
+
+ /**
+ * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
+ */
+ public Locale getLocale() {
+ return new Locale(mLanguage);
+ }
+
+ /**
+ * @return the mastering indication of the audio presentation.
+ * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO},
+ * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE}
+ */
+ @MasteringIndicationType
+ public int getMasteringIndication() {
+ return mMasteringIndication;
+ }
+
+ /**
+ * Indicates whether an audio description for the visually impaired is available.
+ * @return {@code true} if audio description is available.
+ */
+ public boolean hasAudioDescription() {
+ return mAudioDescriptionAvailable;
+ }
+
+ /**
+ * Indicates whether spoken subtitles for the visually impaired are available.
+ * @return {@code true} if spoken subtitles are available.
+ */
+ public boolean hasSpokenSubtitles() {
+ return mSpokenSubtitlesAvailable;
+ }
+
+ /**
+ * Indicates whether dialogue enhancement is available.
+ * @return {@code true} if dialogue enhancement is available.
+ */
+ public boolean hasDialogueEnhancement() {
+ return mDialogueEnhancementAvailable;
+ }
+}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index eb6e83065fe9..d0963cbcb87f 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,12 +16,15 @@
package android.media;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -35,6 +38,7 @@ import android.os.Message;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -1601,6 +1605,32 @@ public class AudioRecord implements AudioRouting
}
}
+ //--------------------------------------------------------------------------
+ // Microphone information
+ //--------------------
+ /**
+ * Returns a lists of {@link MicrophoneInfo} representing the active microphones.
+ * By querying channel mapping for each active microphone, developer can know how
+ * the microphone is used by each channels or a capture stream.
+ * Note that the information about the active microphones may change during a recording.
+ * See {@link AudioManager#registerAudioDeviceCallback} to be notified of changes
+ * in the audio devices, querying the active microphones then will return the latest
+ * information.
+ *
+ * @return a lists of {@link MicrophoneInfo} representing the active microphones.
+ * @throws IOException if an error occurs
+ */
+ public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
+ ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
+ int status = native_get_active_microphones(activeMicrophones);
+ if (status != AudioManager.SUCCESS) {
+ Log.e(TAG, "getActiveMicrophones failed:" + status);
+ return new ArrayList<MicrophoneInfo>();
+ }
+ AudioManager.setPortIdForMicrophones(activeMicrophones);
+ return activeMicrophones;
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -1746,6 +1776,9 @@ public class AudioRecord implements AudioRouting
private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp,
@AudioTimestamp.Timebase int timebase);
+ private native final int native_get_active_microphones(
+ ArrayList<MicrophoneInfo> activeMicrophones);
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index dcd37dafd495..be9fcb8fae83 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -827,6 +827,8 @@ public class AudioSystem
private static native boolean native_is_offload_supported(int encoding, int sampleRate,
int channelMask, int channelIndexMask);
+ public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 4e9ce8e23e9c..8e822a5d7a37 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2008,6 +2008,25 @@ public class AudioTrack extends PlayerBase
}
/**
+ * Sets the audio presentation.
+ * If the audio presentation is invalid then {@link #ERROR_BAD_VALUE} will be returned.
+ * If a multi-stream decoder (MSD) is not present, or the format does not support
+ * multiple presentations, then {@link #ERROR_INVALID_OPERATION} will be returned.
+ * @param presentation see {@link AudioPresentation}. In particular, id should be set.
+ * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_INVALID_OPERATION}
+ * @throws IllegalArgumentException if the audio presentation is null.
+ * @throws IllegalStateException if track is not initialized.
+ */
+ public int setPresentation(@NonNull AudioPresentation presentation) {
+ if (presentation == null) {
+ throw new IllegalArgumentException("audio presentation is null");
+ }
+ return native_setPresentation(presentation.getPresentationId(),
+ presentation.getProgramId());
+ }
+
+ /**
* Sets the initialization state of the instance. This method was originally intended to be used
* in an AudioTrack subclass constructor to set a subclass-specific post-initialization state.
* However, subclasses of AudioTrack are no longer recommended, so this method is obsolete.
@@ -3245,6 +3264,7 @@ public class AudioTrack extends PlayerBase
@NonNull VolumeShaper.Operation operation);
private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
+ private native final int native_setPresentation(int presentationId, int programId);
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index 5ad4313c0332..5cb831356472 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -19,7 +19,6 @@ package android.media;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.MediaBrowser2Provider;
@@ -41,14 +40,14 @@ public class MediaBrowser2 extends MediaController2 {
*/
public static class BrowserCallback extends MediaController2.ControllerCallback {
/**
- * Called with the result of {@link #getBrowserRoot(Bundle)}.
+ * Called with the result of {@link #getLibraryRoot(Bundle)}.
* <p>
- * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't
+ * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the library root isn't
* available.
*
* @param rootHints rootHints that you previously requested.
- * @param rootMediaId media id of the browser root. Can be {@code null}
- * @param rootExtra extra of the browser root. Can be {@code null}
+ * @param rootMediaId media id of the library root. Can be {@code null}
+ * @param rootExtra extra of the library root. Can be {@code null}
*/
public void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId,
@Nullable Bundle rootExtra) { }
@@ -114,8 +113,15 @@ public class MediaBrowser2 extends MediaController2 {
.createMediaBrowser2(context, this, token, executor, (BrowserCallback) callback);
}
- public void getBrowserRoot(Bundle rootHints) {
- mProvider.getBrowserRoot_impl(rootHints);
+ /**
+ * Get the library root. Result would be sent back asynchronously with the
+ * {@link BrowserCallback#onGetRootResult(Bundle, String, Bundle)}.
+ *
+ * @param rootHints hint for the root
+ * @see BrowserCallback#onGetRootResult(Bundle, String, Bundle)
+ */
+ public void getLibraryRoot(Bundle rootHints) {
+ mProvider.getLibraryRoot_impl(rootHints);
}
/**
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index b32e5398d0de..fcdecbad908d 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -172,6 +172,14 @@ public class MediaController2 implements AutoCloseable {
}
/**
+ * @hide
+ */
+ @SystemApi
+ public PlaybackInfoProvider getProvider() {
+ return mProvider;
+ }
+
+ /**
* Get the type of playback which affects volume handling. One of:
* <ul>
* <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
@@ -199,9 +207,9 @@ public class MediaController2 implements AutoCloseable {
/**
* Get the type of volume control that can be used. One of:
* <ul>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li>
* </ul>
*
* @return The type of volume control that may be used with this session.
@@ -472,7 +480,7 @@ public class MediaController2 implements AutoCloseable {
/**
* Set the volume of the output this session is playing on. The command will be ignored if it
- * does not support {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+ * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
* <p>
* If the session is local playback, this changes the device's volume with the stream that
* session's player is using. Flags will be specified for the {@link AudioManager}.
@@ -494,8 +502,8 @@ public class MediaController2 implements AutoCloseable {
* must be one of {@link AudioManager#ADJUST_LOWER},
* {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
* The command will be ignored if the session does not support
- * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
- * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+ * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or
+ * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
* <p>
* If the session is local playback, this changes the device's volume with the stream that
* session's player is using. Flags will be specified for the {@link AudioManager}.
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 174d6a3b879c..4919eeb4dacb 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.AudioPresentation;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaHTTPService;
@@ -40,6 +41,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -396,6 +398,17 @@ final public class MediaExtractor {
}
/**
+ * Get the list of available audio presentations for the track.
+ * @param trackIndex index of the track.
+ * @return a list of available audio presentations for a given valid audio track index.
+ * The list will be empty if the source does not contain any audio presentations.
+ */
+ @NonNull
+ public List<AudioPresentation> getAudioPresentations(int trackIndex) {
+ return new ArrayList<AudioPresentation>();
+ }
+
+ /**
* Get the PSSH info if present.
* @return a map of uuid-to-bytes, with the uuid specifying
* the crypto scheme, and the bytes being the data specific to that scheme.
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index a901c6895d9f..a11768e69b9d 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -25,8 +25,8 @@ import android.content.Context;
import android.media.MediaSession2.BuilderBase;
import android.media.MediaSession2.ControllerInfo;
import android.media.update.ApiLoader;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
-import android.media.update.MediaSession2Provider;
import android.media.update.MediaSessionService2Provider;
import android.os.Bundle;
@@ -62,7 +62,8 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
/**
- * Session for the media library service.
+ * Session for the {@link MediaLibraryService2}. Build this object with
+ * {@link MediaLibrarySessionBuilder} and return in {@link #onCreateSession(String)}.
*/
public static class MediaLibrarySession extends MediaSession2 {
private final MediaLibrarySessionProvider mProvider;
@@ -100,6 +101,9 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
}
}
+ /**
+ * Callback for the {@link MediaLibrarySession}.
+ */
public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
public MediaLibrarySessionCallback(Context context) {
@@ -116,15 +120,15 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
*
* @param controllerInfo information of the controller requesting access to browse media.
* @param rootHints An optional bundle of service-specific arguments to send
- * to the media browser service when connecting and retrieving the
+ * to the media library service when connecting and retrieving the
* root id for browsing, or null if none. The contents of this
* bundle may affect the information returned when browsing.
- * @return The {@link BrowserRoot} for accessing this app's content or null.
- * @see BrowserRoot#EXTRA_RECENT
- * @see BrowserRoot#EXTRA_OFFLINE
- * @see BrowserRoot#EXTRA_SUGGESTED
+ * @return The {@link LibraryRoot} for accessing this app's content or null.
+ * @see LibraryRoot#EXTRA_RECENT
+ * @see LibraryRoot#EXTRA_OFFLINE
+ * @see LibraryRoot#EXTRA_SUGGESTED
*/
- public @Nullable BrowserRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
+ public @Nullable LibraryRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
@Nullable Bundle rootHints) {
return null;
}
@@ -199,6 +203,8 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
/**
* Builder for {@link MediaLibrarySession}.
*/
+ // Override all methods just to show them with the type instead of generics in Javadoc.
+ // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySession,
MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
public MediaLibrarySessionBuilder(
@@ -209,6 +215,38 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
context, (MediaLibrarySessionBuilder) instance, player, callbackExecutor,
callback));
}
+
+ @Override
+ public MediaLibrarySessionBuilder setVolumeProvider(
+ @Nullable VolumeProvider2 volumeProvider) {
+ return super.setVolumeProvider(volumeProvider);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setRatingType(int type) {
+ return super.setRatingType(type);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setSessionActivity(@Nullable PendingIntent pi) {
+ return super.setSessionActivity(pi);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setId(String id) {
+ return super.setId(id);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setSessionCallback(
+ @NonNull Executor executor, @NonNull MediaLibrarySessionCallback callback) {
+ return super.setSessionCallback(executor, callback);
+ }
+
+ @Override
+ public MediaLibrarySession build() {
+ return super.build();
+ }
}
@Override
@@ -228,7 +266,7 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
* This method will be called on the main thread.
*
* @param sessionId session id written in the AndroidManifest.xml.
- * @return a new browser session
+ * @return a new library session
* @see MediaLibrarySessionBuilder
* @see #getSession()
* @throws RuntimeException if returned session is invalid
@@ -237,17 +275,17 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
/**
- * Contains information that the browser service needs to send to the client
- * when first connected.
+ * Contains information that the library service needs to send to the client when
+ * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called.
*/
- public static final class BrowserRoot {
+ public static final class LibraryRoot {
/**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for recently played media items.
+ * The lookup key for a boolean that indicates whether the library service should return a
+ * librar root for recently played media items.
*
- * <p>When creating a media browser for a given media browser service, this key can be
+ * <p>When creating a media browser for a given media library service, this key can be
* supplied as a root hint for retrieving media items that are recently played.
- * If the media browser service can provide such media items, the implementation must return
+ * If the media library service can provide such media items, the implementation must return
* the key in the root hint when
* {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
*
@@ -259,13 +297,13 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
public static final String EXTRA_RECENT = "android.media.extra.RECENT";
/**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for offline media items.
+ * The lookup key for a boolean that indicates whether the library service should return a
+ * library root for offline media items.
*
- * <p>When creating a media browser for a given media browser service, this key can be
+ * <p>When creating a media browser for a given media library service, this key can be
* supplied as a root hint for retrieving media items that are can be played without an
* internet connection.
- * If the media browser service can provide such media items, the implementation must return
+ * If the media library service can provide such media items, the implementation must return
* the key in the root hint when
* {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
*
@@ -277,14 +315,14 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
/**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for suggested media items.
+ * The lookup key for a boolean that indicates whether the library service should return a
+ * library root for suggested media items.
*
- * <p>When creating a media browser for a given media browser service, this key can be
- * supplied as a root hint for retrieving the media items suggested by the media browser
+ * <p>When creating a media browser for a given media library service, this key can be
+ * supplied as a root hint for retrieving the media items suggested by the media library
* service. The list of media items is considered ordered by relevance, first being the top
* suggestion.
- * If the media browser service can provide such media items, the implementation must return
+ * If the media library service can provide such media items, the implementation must return
* the key in the root hint when
* {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
*
@@ -295,35 +333,31 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
*/
public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
- final private String mRootId;
- final private Bundle mExtras;
+ private final LibraryRootProvider mProvider;
/**
- * Constructs a browser root.
+ * Constructs a library root.
* @param rootId The root id for browsing.
- * @param extras Any extras about the browser service.
+ * @param extras Any extras about the library service.
*/
- public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
- if (rootId == null) {
- throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
- "Use null for BrowserRoot instead.");
- }
- mRootId = rootId;
- mExtras = extras;
+ public LibraryRoot(@NonNull Context context,
+ @NonNull String rootId, @Nullable Bundle extras) {
+ mProvider = ApiLoader.getProvider(context).createMediaLibraryService2LibraryRoot(
+ context, this, rootId, extras);
}
/**
* Gets the root id for browsing.
*/
public String getRootId() {
- return mRootId;
+ return mProvider.getRootId_impl();
}
/**
- * Gets any extras about the browser service.
+ * Gets any extras about the library service.
*/
public Bundle getExtras() {
- return mExtras;
+ return mProvider.getExtras_impl();
}
}
}
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d84eedf94820..e331b2cbf645 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1671,35 +1671,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener
public abstract void deselectTrack(int index);
/**
- * Sets the target UDP re-transmit endpoint for the low level player.
- * Generally, the address portion of the endpoint is an IP multicast
- * address, although a unicast address would be equally valid. When a valid
- * retransmit endpoint has been set, the media player will not decode and
- * render the media presentation locally. Instead, the player will attempt
- * to re-multiplex its media data using the Android@Home RTP profile and
- * re-transmit to the target endpoint. Receiver devices (which may be
- * either the same as the transmitting device or different devices) may
- * instantiate, prepare, and start a receiver player using a setDataSource
- * URL of the form...
- *
- * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
- *
- * to receive, decode and render the re-transmitted content.
- *
- * setRetransmitEndpoint may only be called before setDataSource has been
- * called; while the player is in the Idle state.
- *
- * @param endpoint the address and UDP port of the re-transmission target or
- * null if no re-transmission is to be performed.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the retransmit endpoint is supplied,
- * but invalid.
- *
- * {@hide} pending API council
- */
- public void setRetransmitEndpoint(InetSocketAddress endpoint) { }
-
- /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 222c66ea4551..e3d5ac07665e 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -2964,53 +2964,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
}
/**
- * Sets the target UDP re-transmit endpoint for the low level player.
- * Generally, the address portion of the endpoint is an IP multicast
- * address, although a unicast address would be equally valid. When a valid
- * retransmit endpoint has been set, the media player will not decode and
- * render the media presentation locally. Instead, the player will attempt
- * to re-multiplex its media data using the Android@Home RTP profile and
- * re-transmit to the target endpoint. Receiver devices (which may be
- * either the same as the transmitting device or different devices) may
- * instantiate, prepare, and start a receiver player using a setDataSource
- * URL of the form...
- *
- * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
- *
- * to receive, decode and render the re-transmitted content.
- *
- * setRetransmitEndpoint may only be called before setDataSource has been
- * called; while the player is in the Idle state.
- *
- * @param endpoint the address and UDP port of the re-transmission target or
- * null if no re-transmission is to be performed.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the retransmit endpoint is supplied,
- * but invalid.
- *
- * {@hide} pending API council
- */
- @Override
- public void setRetransmitEndpoint(InetSocketAddress endpoint)
- throws IllegalStateException, IllegalArgumentException
- {
- String addrString = null;
- int port = 0;
-
- if (null != endpoint) {
- addrString = endpoint.getAddress().getHostAddress();
- port = endpoint.getPort();
- }
-
- int ret = native_setRetransmitEndpoint(addrString, port);
- if (ret != 0) {
- throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
- }
- }
-
- private native final int native_setRetransmitEndpoint(String addrString, int port);
-
- /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 78477f757e2a..62240cedf0be 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Surface;
@@ -34,6 +35,8 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
import com.android.internal.annotations.GuardedBy;
@@ -1406,6 +1409,31 @@ public class MediaRecorder implements AudioRouting
private native final int native_getRoutedDeviceId();
private native final void native_enableDeviceCallback(boolean enabled);
+ //--------------------------------------------------------------------------
+ // Microphone information
+ //--------------------
+ /**
+ * Return A lists of {@link MicrophoneInfo} representing the active microphones.
+ * By querying channel mapping for each active microphone, developer can know how
+ * the microphone is used by each channels or a capture stream.
+ *
+ * @return a lists of {@link MicrophoneInfo} representing the active microphones
+ * @throws IOException if an error occurs
+ */
+ public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
+ ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
+ int status = native_getActiveMicrophones(activeMicrophones);
+ if (status != AudioManager.SUCCESS) {
+ Log.e(TAG, "getActiveMicrophones failed:" + status);
+ return new ArrayList<MicrophoneInfo>();
+ }
+ AudioManager.setPortIdForMicrophones(activeMicrophones);
+ return activeMicrophones;
+ }
+
+ private native final int native_getActiveMicrophones(
+ ArrayList<MicrophoneInfo> activeMicrophones);
+
/**
* Called from native code when an interesting event happens. This method
* just uses the EventHandler system to post the event back to the main app thread.
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index acf9615404d7..ae2649a70a55 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -31,6 +31,7 @@ import android.media.session.PlaybackState;
import android.media.update.ApiLoader;
import android.media.update.MediaSession2Provider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
import android.media.update.MediaSession2Provider.CommandGroupProvider;
import android.media.update.MediaSession2Provider.CommandProvider;
import android.media.update.MediaSession2Provider.ControllerInfoProvider;
@@ -579,8 +580,20 @@ public class MediaSession2 implements AutoCloseable {
};
/**
- * Base builder class for MediaSession2 and its subclass.
- *
+ * Base builder class for MediaSession2 and its subclass. Any change in this class should be
+ * also applied to the subclasses {@link MediaSession2.Builder} and
+ * {@link MediaLibraryService2.MediaLibrarySessionBuilder}.
+ * <p>
+ * APIs here should be package private, but should have documentations for developers.
+ * Otherwise, javadoc will generate documentation with the generic types such as follows.
+ * <pre>U extends BuilderBase<T, U, C> setSessionCallback(Executor executor, C callback)</pre>
+ * <p>
+ * This class is hidden to prevent from generating test stub, which fails with
+ * 'unexpected bound' because it tries to auto generate stub class as follows.
+ * <pre>abstract static class BuilderBase<
+ * T extends android.media.MediaSession2,
+ * U extends android.media.MediaSession2.BuilderBase<
+ * T, U, C extends android.media.MediaSession2.SessionCallback>, C></pre>
* @hide
*/
static abstract class BuilderBase
@@ -598,9 +611,9 @@ public class MediaSession2 implements AutoCloseable {
* <p>
* Set {@code null} to reset.
*
- * @param volumeProvider The provider that will handle volume changes. Can be {@code null}
+ * @param volumeProvider The provider that will handle volume changes. Can be {@code null}.
*/
- public U setVolumeProvider(@Nullable VolumeProvider volumeProvider) {
+ U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
mProvider.setVolumeProvider_impl(volumeProvider);
return (U) this;
}
@@ -618,7 +631,7 @@ public class MediaSession2 implements AutoCloseable {
* <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
* </ul>
*/
- public U setRatingType(@Rating2.Style int type) {
+ U setRatingType(@Rating2.Style int type) {
mProvider.setRatingType_impl(type);
return (U) this;
}
@@ -630,7 +643,7 @@ public class MediaSession2 implements AutoCloseable {
*
* @param pi The intent to launch to show UI for this session.
*/
- public U setSessionActivity(@Nullable PendingIntent pi) {
+ U setSessionActivity(@Nullable PendingIntent pi) {
mProvider.setSessionActivity_impl(pi);
return (U) this;
}
@@ -645,7 +658,7 @@ public class MediaSession2 implements AutoCloseable {
* @throws IllegalArgumentException if id is {@code null}
* @return
*/
- public U setId(@NonNull String id) {
+ U setId(@NonNull String id) {
mProvider.setId_impl(id);
return (U) this;
}
@@ -657,7 +670,7 @@ public class MediaSession2 implements AutoCloseable {
* @param callback session callback.
* @return
*/
- public U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
+ U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull C callback) {
mProvider.setSessionCallback_impl(executor, callback);
return (U) this;
@@ -670,7 +683,7 @@ public class MediaSession2 implements AutoCloseable {
* @throws IllegalStateException if the session with the same id is already exists for the
* package.
*/
- public T build() {
+ T build() {
return mProvider.build_impl();
}
}
@@ -681,13 +694,44 @@ public class MediaSession2 implements AutoCloseable {
* Any incoming event from the {@link MediaController2} will be handled on the thread
* that created session with the {@link Builder#build()}.
*/
- // TODO(jaewan): Add setRatingType()
- // TODO(jaewan): Add setSessionActivity()
+ // Override all methods just to show them with the type instead of generics in Javadoc.
+ // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
public Builder(Context context, @NonNull MediaPlayerInterface player) {
super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
context, (Builder) instance, player));
}
+
+ @Override
+ public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+ return super.setVolumeProvider(volumeProvider);
+ }
+
+ @Override
+ public Builder setRatingType(@Rating2.Style int type) {
+ return super.setRatingType(type);
+ }
+
+ @Override
+ public Builder setSessionActivity(@Nullable PendingIntent pi) {
+ return super.setSessionActivity(pi);
+ }
+
+ @Override
+ public Builder setId(@NonNull String id) {
+ return super.setId(id);
+ }
+
+ @Override
+ public Builder setSessionCallback(@NonNull Executor executor,
+ @Nullable SessionCallback callback) {
+ return super.setSessionCallback(executor, callback);
+ }
+
+ @Override
+ public MediaSession2 build() {
+ return super.build();
+ }
}
/**
@@ -768,32 +812,15 @@ public class MediaSession2 implements AutoCloseable {
* <p>
* It's up to the controller's decision to respect or ignore this customization request.
*/
- // TODO(jaewan): Move this to updatable.
public static class CommandButton {
- private static final String KEY_COMMAND
- = "android.media.media_session2.command_button.command";
- private static final String KEY_ICON_RES_ID
- = "android.media.media_session2.command_button.icon_res_id";
- private static final String KEY_DISPLAY_NAME
- = "android.media.media_session2.command_button.display_name";
- private static final String KEY_EXTRA
- = "android.media.media_session2.command_button.extra";
- private static final String KEY_ENABLED
- = "android.media.media_session2.command_button.enabled";
-
- private Command mCommand;
- private int mIconResId;
- private String mDisplayName;
- private Bundle mExtra;
- private boolean mEnabled;
-
- private CommandButton(@Nullable Command command, int iconResId,
- @Nullable String displayName, Bundle extra, boolean enabled) {
- mCommand = command;
- mIconResId = iconResId;
- mDisplayName = displayName;
- mExtra = extra;
- mEnabled = enabled;
+ private final CommandButtonProvider mProvider;
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public CommandButton(CommandButtonProvider provider) {
+ mProvider = provider;
}
/**
@@ -803,7 +830,7 @@ public class MediaSession2 implements AutoCloseable {
* @return command or {@code null}
*/
public @Nullable Command getCommand() {
- return mCommand;
+ return mProvider.getCommand_impl();
}
/**
@@ -813,7 +840,7 @@ public class MediaSession2 implements AutoCloseable {
* @return resource id of the icon. Can be {@code 0}.
*/
public int getIconResId() {
- return mIconResId;
+ return mProvider.getIconResId_impl();
}
/**
@@ -823,7 +850,7 @@ public class MediaSession2 implements AutoCloseable {
* @return custom display name. Can be {@code null} or empty.
*/
public @Nullable String getDisplayName() {
- return mDisplayName;
+ return mProvider.getDisplayName_impl();
}
/**
@@ -832,7 +859,7 @@ public class MediaSession2 implements AutoCloseable {
* @return
*/
public @Nullable Bundle getExtra() {
- return mExtra;
+ return mProvider.getExtra_impl();
}
/**
@@ -841,92 +868,50 @@ public class MediaSession2 implements AutoCloseable {
* @return {@code true} if enabled. {@code false} otherwise.
*/
public boolean isEnabled() {
- return mEnabled;
- }
-
- /**
- * @hide
- */
- // TODO(jaewan): @SystemApi
- public @NonNull Bundle toBundle() {
- Bundle bundle = new Bundle();
- bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
- bundle.putInt(KEY_ICON_RES_ID, mIconResId);
- bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
- bundle.putBundle(KEY_EXTRA, mExtra);
- bundle.putBoolean(KEY_ENABLED, mEnabled);
- return bundle;
+ return mProvider.isEnabled_impl();
}
/**
* @hide
*/
- // TODO(jaewan): @SystemApi
- public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) {
- Builder builder = new Builder();
- builder.setCommand(Command.fromBundle(context, bundle.getBundle(KEY_COMMAND)));
- builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
- builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
- builder.setExtra(bundle.getBundle(KEY_EXTRA));
- builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
- try {
- return builder.build();
- } catch (IllegalStateException e) {
- // Malformed or version mismatch. Return null for now.
- return null;
- }
+ @SystemApi
+ public CommandButtonProvider getProvider() {
+ return mProvider;
}
/**
* Builder for {@link CommandButton}.
*/
public static class Builder {
- private Command mCommand;
- private int mIconResId;
- private String mDisplayName;
- private Bundle mExtra;
- private boolean mEnabled;
-
- public Builder() {
- mEnabled = true;
+ private final CommandButtonProvider.BuilderProvider mProvider;
+
+ public Builder(@NonNull Context context) {
+ mProvider = ApiLoader.getProvider(context)
+ .createMediaSession2CommandButtonBuilder(context, this);
}
public Builder setCommand(Command command) {
- mCommand = command;
- return this;
+ return mProvider.setCommand_impl(command);
}
public Builder setIconResId(int resId) {
- mIconResId = resId;
- return this;
+ return mProvider.setIconResId_impl(resId);
}
public Builder setDisplayName(String displayName) {
- mDisplayName = displayName;
- return this;
+ return mProvider.setDisplayName_impl(displayName);
}
public Builder setEnabled(boolean enabled) {
- mEnabled = enabled;
- return this;
+ return mProvider.setEnabled_impl(enabled);
}
public Builder setExtra(Bundle extra) {
- mExtra = extra;
- return this;
+ return mProvider.setExtra_impl(extra);
}
public CommandButton build() {
- if (mEnabled && mCommand == null) {
- throw new IllegalStateException("Enabled button needs Command"
- + " for controller to invoke the command");
- }
- if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
- && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
- throw new IllegalStateException("Custom commands needs icon and"
- + " and name to display");
- }
- return new CommandButton(mCommand, mIconResId, mDisplayName, mExtra, mEnabled);
+ return mProvider.build_impl();
}
}
}
@@ -1093,8 +1078,8 @@ public class MediaSession2 implements AutoCloseable {
* If the new player is successfully set, {@link PlaybackListener}
* will be called to tell the current playback state of the new player.
* <p>
- * You can also specify a volume provider. If so, playback in the player is considered as
- * remote playback.
+ * For the remote playback case which you want to handle volume by yourself, use
+ * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}.
*
* @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
* @throws IllegalArgumentException if the player is {@code null}.
@@ -1109,10 +1094,10 @@ public class MediaSession2 implements AutoCloseable {
* @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
* @param volumeProvider a volume provider
* @see #setPlayer(MediaPlayerInterface)
- * @see Builder#setVolumeProvider(VolumeProvider)
+ * @see Builder#setVolumeProvider(VolumeProvider2)
*/
public void setPlayer(@NonNull MediaPlayerInterface player,
- @NonNull VolumeProvider volumeProvider) {
+ @NonNull VolumeProvider2 volumeProvider) {
mProvider.setPlayer_impl(player, volumeProvider);
}
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 6b2de06942db..0b5dddf92af5 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -21,10 +21,12 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.media.MediaSession2.ControllerInfo;
import android.media.update.ApiLoader;
import android.media.update.MediaSessionService2Provider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.os.IBinder;
/**
@@ -165,7 +167,6 @@ public abstract class MediaSessionService2 extends Service {
* @param state playback state
* @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
*/
- // TODO(jaewan): Also add metadata
public MediaNotification onUpdateNotification(PlaybackState2 state) {
return mProvider.onUpdateNotification_impl(state);
}
@@ -204,31 +205,31 @@ public abstract class MediaSessionService2 extends Service {
* foreground service to keep playback running in the background. It's highly recommended to
* show media style notification here.
*/
- // TODO(jaewan): Should we also move this to updatable?
public static class MediaNotification {
- public final int id;
- public final Notification notification;
-
- private MediaNotification(int id, @NonNull Notification notification) {
- this.id = id;
- this.notification = notification;
- }
+ private final MediaNotificationProvider mProvider;
/**
- * Create a {@link MediaNotification}.
+ * Default constructor
*
+ * @param context context
* @param notificationId notification id to be used for
* {@link android.app.NotificationManager#notify(int, Notification)}.
* @param notification a notification to make session service foreground service. Media
* style notification is recommended here.
- * @return
*/
- public static MediaNotification create(int notificationId,
- @NonNull Notification notification) {
- if (notification == null) {
- throw new IllegalArgumentException("Notification cannot be null");
- }
- return new MediaNotification(notificationId, notification);
+ public MediaNotification(@NonNull Context context,
+ int notificationId, @NonNull Notification notification) {
+ mProvider = ApiLoader.getProvider(context)
+ .createMediaSessionService2MediaNotification(
+ context, this, notificationId, notification);
+ }
+
+ public int getNotificationId() {
+ return mProvider.getNotificationId_impl();
+ }
+
+ public Notification getNotification() {
+ return mProvider.getNotification_impl();
}
}
}
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
new file mode 100644
index 000000000000..131e37bd6646
--- /dev/null
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.util.Pair;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Class providing information on a microphone. It indicates the location and orientation of the
+ * microphone on the device as well as useful information like frequency response and sensitivity.
+ * It can be used by applications implementing special pre processing effects like noise suppression
+ * of beam forming that need to know about precise microphone characteristics in order to adapt
+ * their algorithms.
+ */
+public final class MicrophoneInfo {
+
+ /**
+ * A microphone that the location is unknown.
+ */
+ public static final int LOCATION_UNKNOWN = 0;
+
+ /**
+ * A microphone that locate on main body of the device.
+ */
+ public static final int LOCATION_MAINBODY = 1;
+
+ /**
+ * A microphone that locate on a movable main body of the device.
+ */
+ public static final int LOCATION_MAINBODY_MOVABLE = 2;
+
+ /**
+ * A microphone that locate on a peripheral.
+ */
+ public static final int LOCATION_PERIPHERAL = 3;
+
+ /**
+ * Unknown microphone directionality.
+ */
+ public static final int DIRECTIONALITY_UNKNOWN = 0;
+
+ /**
+ * Microphone directionality type: omni.
+ */
+ public static final int DIRECTIONALITY_OMNI = 1;
+
+ /**
+ * Microphone directionality type: bi-directional.
+ */
+ public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2;
+
+ /**
+ * Microphone directionality type: cardioid.
+ */
+ public static final int DIRECTIONALITY_CARDIOID = 3;
+
+ /**
+ * Microphone directionality type: hyper cardioid.
+ */
+ public static final int DIRECTIONALITY_HYPER_CARDIOID = 4;
+
+ /**
+ * Microphone directionality type: super cardioid.
+ */
+ public static final int DIRECTIONALITY_SUPER_CARDIOID = 5;
+
+ /**
+ * The channel contains raw audio from this microphone.
+ */
+ public static final int CHANNEL_MAPPING_DIRECT = 1;
+
+ /**
+ * The channel contains processed audio from this microphone and possibly another microphone.
+ */
+ public static final int CHANNEL_MAPPING_PROCESSED = 2;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "LOCATION_" }, value = {
+ LOCATION_UNKNOWN,
+ LOCATION_MAINBODY,
+ LOCATION_MAINBODY_MOVABLE,
+ LOCATION_PERIPHERAL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MicrophoneLocation {}
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "DIRECTIONALITY_" }, value = {
+ DIRECTIONALITY_UNKNOWN,
+ DIRECTIONALITY_OMNI,
+ DIRECTIONALITY_BI_DIRECTIONAL,
+ DIRECTIONALITY_CARDIOID,
+ DIRECTIONALITY_HYPER_CARDIOID,
+ DIRECTIONALITY_SUPER_CARDIOID,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MicrophoneDirectionality {}
+
+ private Coordinate3F mPosition;
+ private Coordinate3F mOrientation;
+ private String mDeviceId;
+ private String mAddress;
+ private List<Pair<Float, Float>> mFrequencyResponse;
+ private List<Pair<Integer, Integer>> mChannelMapping;
+ private float mMaxSpl;
+ private float mMinSpl;
+ private float mSensitivity;
+ private int mLocation;
+ private int mGroup; /* Usually 0 will be used for main body. */
+ private int mIndexInTheGroup;
+ private int mPortId; /* mPortId will correspond to the id in AudioPort */
+ private int mType;
+ private int mDirectionality;
+
+ MicrophoneInfo(String deviceId, int type, String address, int location,
+ int group, int indexInTheGroup, Coordinate3F position,
+ Coordinate3F orientation, List<Pair<Float, Float>> frequencyResponse,
+ List<Pair<Integer, Integer>> channelMapping, float sensitivity, float maxSpl,
+ float minSpl, int directionality) {
+ mDeviceId = deviceId;
+ mType = type;
+ mAddress = address;
+ mLocation = location;
+ mGroup = group;
+ mIndexInTheGroup = indexInTheGroup;
+ mPosition = position;
+ mOrientation = orientation;
+ mFrequencyResponse = frequencyResponse;
+ mChannelMapping = channelMapping;
+ mSensitivity = sensitivity;
+ mMaxSpl = maxSpl;
+ mMinSpl = minSpl;
+ mDirectionality = directionality;
+ }
+
+ /**
+ * Returns alphanumeric code that uniquely identifies the device.
+ *
+ * @return the description of the microphone
+ */
+ public String getDescription() {
+ return mDeviceId;
+ }
+
+ /**
+ * Returns The system unique device ID that corresponds to the id
+ * returned by {@link AudioDeviceInfo#getId()}.
+ *
+ * @return the microphone's id
+ */
+ public int getId() {
+ return mPortId;
+ }
+
+ /**
+ * @hide
+ * Returns the internal device type (e.g AudioSystem.DEVICE_IN_BUILTIN_MIC).
+ * The internal device type could be used when getting microphone's port id
+ * by matching type and address.
+ *
+ * @return the internal device type
+ */
+ public int getInternalDeviceType() {
+ return mType;
+ }
+
+ /**
+ * Returns the device type identifier of the microphone (e.g AudioDeviceInfo.TYPE_BUILTIN_MIC).
+ *
+ * @return the device type of the microphone
+ */
+ public int getType() {
+ return AudioDeviceInfo.convertInternalDeviceToDeviceType(mType);
+ }
+
+ /**
+ * @hide
+ * Returns The "address" string of the microphone that corresponds to the
+ * address returned by {@link AudioDeviceInfo#getAddress()}
+ * @return the address of the microphone
+ */
+ public String getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * Returns the location of the microphone. The return value is
+ * one of {@link #LOCATION_UNKNOWN}, {@link #LOCATION_MAINBODY},
+ * {@link #LOCATION_MAINBODY_MOVABLE}, or {@link #LOCATION_PERIPHERAL}.
+ *
+ * @return the location of the microphone
+ */
+ public @MicrophoneLocation int getLocation() {
+ return mLocation;
+ }
+
+ /**
+ * Returns A device group id that can be used to group together microphones on the same
+ * peripheral, attachments or logical groups. Main body is usually group 0.
+ *
+ * @return the group of the microphone
+ */
+ public int getGroup() {
+ return mGroup;
+ }
+
+ /**
+ * Returns unique index for device within its group.
+ *
+ * @return the microphone's index in its group
+ */
+ public int getIndexInTheGroup() {
+ return mIndexInTheGroup;
+ }
+
+ /**
+ * Returns A {@link Coordinate3F} object that represents the geometric location of microphone
+ * in meters, from botton-left-back corner of appliance. X-axis, Y-axis and Z-axis show
+ * as the x, y, z values.
+ *
+ * @return the geometric location of the microphone
+ */
+ public Coordinate3F getPosition() {
+ return mPosition;
+ }
+
+ /**
+ * Returns A {@link Coordinate3F} object that represents the orientation of microphone.
+ * X-axis, Y-axis and Z-axis show as the x, y, z value. The orientation will be normalized
+ * such as sqrt(x^2 + y^2 + z^2) equals 1.
+ *
+ * @return the orientation of the microphone
+ */
+ public Coordinate3F getOrientation() {
+ return mOrientation;
+ }
+
+ /**
+ * Returns a {@link android.util.Pair} list of frequency responses.
+ * For every {@link android.util.Pair} in the list, the first value represents frequency in Hz,
+ * and the second value represents response in dB.
+ *
+ * @return the frequency response of the microphone
+ */
+ public List<Pair<Float, Float>> getFrequencyResponse() {
+ return mFrequencyResponse;
+ }
+
+ /**
+ * Returns a {@link android.util.Pair} list for channel mapping, which indicating how this
+ * microphone is used by each channels or a capture stream. For each {@link android.util.Pair},
+ * the first value is channel index, the second value is channel mapping type, which could be
+ * either {@link #CHANNEL_MAPPING_DIRECT} or {@link #CHANNEL_MAPPING_PROCESSED}.
+ * If a channel has contributions from more than one microphone, it is likely the HAL
+ * did some extra processing to combine the sources, but this is to be inferred by the user.
+ * Empty list when the MicrophoneInfo is returned by AudioManager.getMicrophones().
+ * At least one entry when the MicrophoneInfo is returned by AudioRecord.getActiveMicrophones().
+ *
+ * @return a {@link android.util.Pair} list for channel mapping
+ */
+ public List<Pair<Integer, Integer>> getChannelMapping() {
+ return mChannelMapping;
+ }
+
+ /**
+ * Returns the level in dBFS produced by a 1000Hz tone at 94 dB SPL.
+ *
+ * @return the sensitivity of the microphone
+ */
+ public float getSensitivity() {
+ return mSensitivity;
+ }
+
+ /**
+ * Returns the level in dB of the maximum SPL supported by the device at 1000Hz.
+ *
+ * @return the maximum level in dB
+ */
+ public float getMaxSpl() {
+ return mMaxSpl;
+ }
+
+ /**
+ * Returns the level in dB of the minimum SPL that can be registered by the device at 1000Hz.
+ *
+ * @return the minimum level in dB
+ */
+ public float getMinSpl() {
+ return mMinSpl;
+ }
+
+ /**
+ * Returns the directionality of microphone. The return value is one of
+ * {@link #DIRECTIONALITY_UNKNOWN}, {@link #DIRECTIONALITY_OMNI},
+ * {@link #DIRECTIONALITY_BI_DIRECTIONAL}, {@link #DIRECTIONALITY_CARDIOID},
+ * {@link #DIRECTIONALITY_HYPER_CARDIOID}, or {@link #DIRECTIONALITY_SUPER_CARDIOID}.
+ *
+ * @return the directionality of microphone
+ */
+ public @MicrophoneDirectionality int getDirectionality() {
+ return mDirectionality;
+ }
+
+ /**
+ * Set the port id for the device.
+ * @hide
+ */
+ public void setId(int portId) {
+ mPortId = portId;
+ }
+
+ /* A class containing three float value to represent a 3D coordinate */
+ public class Coordinate3F {
+ public final float x;
+ public final float y;
+ public final float z;
+
+ Coordinate3F(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+ }
+}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 3eb9d529b756..fefa1ede849e 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -28,11 +28,13 @@ import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -47,22 +49,17 @@ import android.util.Log;
import com.android.internal.database.SortCursor;
-import libcore.io.Streams;
-
import java.io.Closeable;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
-import static android.content.ContentProvider.maybeAddUserId;
-import static android.content.pm.PackageManager.NameNotFoundException;
-
/**
* RingtoneManager provides access to ringtones, notification, and other types
* of sounds. It manages querying the different media providers and combines the
@@ -855,7 +852,7 @@ public class RingtoneManager {
final Uri cacheUri = getCacheForType(type, context.getUserId());
try (InputStream in = openRingtone(context, ringtoneUri);
OutputStream out = resolver.openOutputStream(cacheUri)) {
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + e);
}
@@ -960,7 +957,7 @@ public class RingtoneManager {
// Copy contents to external ringtone storage. Throws IOException if the copy fails.
try (final InputStream input = mContext.getContentResolver().openInputStream(fileUri);
final OutputStream output = new FileOutputStream(outFile)) {
- Streams.copy(input, output);
+ FileUtils.copy(input, output);
}
// Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}.
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 00746e25c247..53ba4663aaf6 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -32,7 +32,7 @@ import java.lang.annotation.RetentionPolicy;
* {@link #setCurrentVolume(int)} each time the volume being provided changes.
* <p>
* You can set a volume provider on a session by calling
- * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider)}.
+ * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider2)}.
*
* @hide
*/
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 0fe7246e85c4..f2b4fe093248 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -546,22 +546,39 @@ public class Visualizer {
/**
* Method called when a new waveform capture is available.
* <p>Data in the waveform buffer is valid only within the scope of the callback.
- * Applications which needs access to the waveform data after returning from the callback
+ * Applications which need access to the waveform data after returning from the callback
* should make a copy of the data instead of holding a reference.
* @param visualizer Visualizer object on which the listener is registered.
* @param waveform array of bytes containing the waveform representation.
- * @param samplingRate sampling rate of the audio visualized.
+ * @param samplingRate sampling rate of the visualized audio.
*/
void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
/**
* Method called when a new frequency capture is available.
* <p>Data in the fft buffer is valid only within the scope of the callback.
- * Applications which needs access to the fft data after returning from the callback
+ * Applications which need access to the fft data after returning from the callback
* should make a copy of the data instead of holding a reference.
+ *
+ * <p>In order to obtain magnitude and phase values the following formulas can
+ * be used:
+ * <pre class="prettyprint">
+ * for (int i = 0; i &lt; fft.size(); i += 2) {
+ * float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
+ * float phase = (float)Math.atan2(fft[i + 1], fft[i]);
+ * }</pre>
* @param visualizer Visualizer object on which the listener is registered.
* @param fft array of bytes containing the frequency representation.
- * @param samplingRate sampling rate of the audio visualized.
+ * The fft array only contains the first half of the actual
+ * FFT spectrum (frequencies up to Nyquist frequency), exploiting
+ * the symmetry of the spectrum. For each frequencies bin <code>i</code>:
+ * <ul>
+ * <li>the element at index <code>2*i</code> in the array contains
+ * the real part of a complex number,</li>
+ * <li>the element at index <code>2*i+1</code> contains the imaginary
+ * part of the complex number.</li>
+ * </ul>
+ * @param samplingRate sampling rate of the visualized audio.
*/
void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
}
diff --git a/media/java/android/media/update/FrameLayoutHelper.java b/media/java/android/media/update/FrameLayoutHelper.java
deleted file mode 100644
index 983dc703a9b5..000000000000
--- a/media/java/android/media/update/FrameLayoutHelper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-/**
- * Helper class for connecting the public API to an updatable implementation.
- *
- * @see ViewProvider
- *
- * @hide
- */
-public abstract class FrameLayoutHelper<T extends ViewProvider> extends FrameLayout {
- /** @hide */
- final public T mProvider;
-
- /** @hide */
- public FrameLayoutHelper(ProviderCreator<T> creator,
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- mProvider = creator.createProvider(this, new SuperProvider());
- }
-
- /** @hide */
- // TODO @SystemApi
- public T getProvider() {
- return mProvider;
- }
-
- @Override
- public CharSequence getAccessibilityClassName() {
- return mProvider.getAccessibilityClassName_impl();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return mProvider.onTouchEvent_impl(ev);
- }
-
- @Override
- public boolean onTrackballEvent(MotionEvent ev) {
- return mProvider.onTrackballEvent_impl(ev);
- }
-
- @Override
- public void onFinishInflate() {
- mProvider.onFinishInflate_impl();
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- mProvider.setEnabled_impl(enabled);
- }
-
- @Override
- protected void onAttachedToWindow() {
- mProvider.onAttachedToWindow_impl();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- mProvider.onDetachedFromWindow_impl();
- }
-
- /** @hide */
- public class SuperProvider implements ViewProvider {
- @Override
- public CharSequence getAccessibilityClassName_impl() {
- return FrameLayoutHelper.super.getAccessibilityClassName();
- }
-
- @Override
- public boolean onTouchEvent_impl(MotionEvent ev) {
- return FrameLayoutHelper.super.onTouchEvent(ev);
- }
-
- @Override
- public boolean onTrackballEvent_impl(MotionEvent ev) {
- return FrameLayoutHelper.super.onTrackballEvent(ev);
- }
-
- @Override
- public void onFinishInflate_impl() {
- FrameLayoutHelper.super.onFinishInflate();
- }
-
- @Override
- public void setEnabled_impl(boolean enabled) {
- FrameLayoutHelper.super.setEnabled(enabled);
- }
-
- @Override
- public void onAttachedToWindow_impl() {
- FrameLayoutHelper.super.onAttachedToWindow();
- }
-
- @Override
- public void onDetachedFromWindow_impl() {
- FrameLayoutHelper.super.onDetachedFromWindow();
- }
- }
-
- /** @hide */
- @FunctionalInterface
- public interface ProviderCreator<U extends ViewProvider> {
- U createProvider(FrameLayoutHelper<U> instance, ViewProvider superProvider);
- }
-}
diff --git a/media/java/android/media/update/MediaBrowser2Provider.java b/media/java/android/media/update/MediaBrowser2Provider.java
index 67680c75c2b2..17256a891277 100644
--- a/media/java/android/media/update/MediaBrowser2Provider.java
+++ b/media/java/android/media/update/MediaBrowser2Provider.java
@@ -23,7 +23,7 @@ import android.os.Bundle;
* @hide
*/
public interface MediaBrowser2Provider extends MediaController2Provider {
- void getBrowserRoot_impl(Bundle rootHints);
+ void getLibraryRoot_impl(Bundle rootHints);
void subscribe_impl(String parentId, Bundle options);
void unsubscribe_impl(String parentId, Bundle options);
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index 95fe36317164..e155e5f0d8b9 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -18,6 +18,7 @@ package android.media.update;
import android.annotation.SystemApi;
import android.media.session.MediaController;
+import android.util.AttributeSet;
import android.view.View;
/**
@@ -34,12 +35,12 @@ import android.view.View;
* @hide
*/
// TODO @SystemApi
-public interface MediaControlView2Provider extends ViewProvider {
+public interface MediaControlView2Provider extends ViewGroupProvider {
+ void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
void setController_impl(MediaController controller);
- boolean isShowing_impl();
void setButtonVisibility_impl(int button, int visibility);
void requestPlayButtonFocus_impl();
- void onVisibilityAggregated_impl(boolean isVisible);
void setTimeout_impl(long timeout);
long getTimeout_impl();
}
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
index 87f509a932c4..923551a226b6 100644
--- a/media/java/android/media/update/MediaLibraryService2Provider.java
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -33,4 +33,9 @@ public interface MediaLibraryService2Provider extends MediaSessionService2Provid
void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, Bundle options);
void notifyChildrenChanged_impl(String parentId, Bundle options);
}
+
+ interface LibraryRootProvider {
+ String getRootId_impl();
+ Bundle getExtras_impl();
+ }
}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index da4d0c74b30a..41162e0a15ea 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -24,12 +24,13 @@ import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.SessionToken2;
-import android.media.VolumeProvider;
+import android.media.VolumeProvider2;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -43,7 +44,7 @@ import java.util.concurrent.Executor;
public interface MediaSession2Provider extends TransportControlProvider {
void close_impl();
void setPlayer_impl(MediaPlayerInterface player);
- void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider);
+ void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider);
MediaPlayerInterface getPlayer_impl();
SessionToken2 getToken_impl();
List<ControllerInfo> getConnectedControllers_impl();
@@ -82,6 +83,23 @@ public interface MediaSession2Provider extends TransportControlProvider {
Bundle toBundle_impl();
}
+ interface CommandButtonProvider {
+ Command getCommand_impl();
+ int getIconResId_impl();
+ String getDisplayName_impl();
+ Bundle getExtra_impl();
+ boolean isEnabled_impl();
+
+ interface BuilderProvider {
+ Builder setCommand_impl(Command command);
+ Builder setIconResId_impl(int resId);
+ Builder setDisplayName_impl(String displayName);
+ Builder setEnabled_impl(boolean enabled);
+ Builder setExtra_impl(Bundle extra);
+ CommandButton build_impl();
+ }
+ }
+
interface ControllerInfoProvider {
String getPackageName_impl();
int getUid_impl();
@@ -98,7 +116,7 @@ public interface MediaSession2Provider extends TransportControlProvider {
}
interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
- void setVolumeProvider_impl(VolumeProvider volumeProvider);
+ void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
void setRatingType_impl(int type);
void setSessionActivity_impl(PendingIntent pi);
void setId_impl(String id);
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
index 9455da7d0bc7..42e75871e12f 100644
--- a/media/java/android/media/update/MediaSessionService2Provider.java
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -17,6 +17,7 @@
package android.media.update;
import android.annotation.SystemApi;
+import android.app.Notification;
import android.content.Intent;
import android.media.MediaSession2;
import android.media.MediaSessionService2.MediaNotification;
@@ -33,4 +34,9 @@ public interface MediaSessionService2Provider {
// Service
void onCreate_impl();
IBinder onBind_impl(Intent intent);
+
+ interface MediaNotificationProvider {
+ int getNotificationId_impl();
+ Notification getNotification_impl();
+ }
}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 922b452a2edb..57f04cc88ff5 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,6 +17,7 @@
package android.media.update;
import android.annotation.Nullable;
+import android.app.Notification;
import android.content.Context;
import android.media.DataSourceDesc;
import android.media.MediaBrowser2;
@@ -25,25 +26,31 @@ import android.media.MediaController2;
import android.media.MediaController2.ControllerCallback;
import android.media.MediaItem2;
import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
+import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
+import android.media.MediaSessionService2.MediaNotification;
import android.media.PlaybackState2;
import android.media.Rating2;
import android.media.SessionPlayer2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
import android.media.update.MediaSession2Provider.CommandGroupProvider;
import android.media.update.MediaSession2Provider.CommandProvider;
import android.media.update.MediaSession2Provider.ControllerInfoProvider;
import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.os.Bundle;
import android.os.IInterface;
import android.util.AttributeSet;
@@ -60,10 +67,11 @@ import java.util.concurrent.Executor;
* @hide
*/
public interface StaticProvider {
- MediaControlView2Provider createMediaControlView2(
- MediaControlView2 instance, ViewProvider superProvider);
- VideoView2Provider createVideoView2(
- VideoView2 instance, ViewProvider superProvider,
+ MediaControlView2Provider createMediaControlView2(MediaControlView2 instance,
+ ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
+ @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+ VideoView2Provider createVideoView2(VideoView2 instance,
+ ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
CommandProvider createMediaSession2Command(MediaSession2.Command instance,
@@ -79,6 +87,7 @@ public interface StaticProvider {
PlaylistParams playlistParams, int repeatMode, int shuffleMode,
MediaMetadata2 playlistMetadata);
PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle);
+ BuilderProvider createMediaSession2CommandButtonBuilder(Context context, Builder builder);
BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
Context context, MediaSession2.Builder instance, MediaPlayerInterface player);
@@ -89,12 +98,16 @@ public interface StaticProvider {
SessionToken2 token, Executor executor, BrowserCallback callback);
MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
+ MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+ MediaNotification mediaNotification, int notificationId, Notification notification);
MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
createMediaLibraryService2Builder(
Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
Executor callbackExecutor, MediaLibrarySessionCallback callback);
+ LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
+ String rootId, Bundle extras);
SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
String packageName, String serviceName, int uid);
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 10f03d223d9f..7251180cb6a9 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -16,12 +16,14 @@
package android.media.update;
+import android.annotation.SystemApi;
import android.media.AudioAttributes;
import android.media.MediaPlayerInterface;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
import android.net.Uri;
+import android.util.AttributeSet;
import android.widget.MediaControlView2;
import android.widget.VideoView2;
@@ -43,7 +45,9 @@ import java.util.concurrent.Executor;
* @hide
*/
// TODO @SystemApi
-public interface VideoView2Provider extends ViewProvider {
+public interface VideoView2Provider extends ViewGroupProvider {
+ void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
void setMediaControlView2_impl(MediaControlView2 mediaControlView);
MediaController getMediaController_impl();
MediaControlView2 getMediaControlView2_impl();
@@ -52,6 +56,9 @@ public interface VideoView2Provider extends ViewProvider {
void setSpeed_impl(float speed);
void setAudioFocusRequest_impl(int focusGain);
void setAudioAttributes_impl(AudioAttributes attributes);
+ /**
+ * @hide
+ */
void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player);
// TODO: remove setRouteAttributes_impl with MediaSession.Callback once MediaSession2 is ready.
void setRouteAttributes_impl(List<String> routeCategories, MediaSession.Callback sessionPlayer);
diff --git a/media/java/android/media/update/ViewGroupHelper.java b/media/java/android/media/update/ViewGroupHelper.java
new file mode 100644
index 000000000000..2c4f9b92bdac
--- /dev/null
+++ b/media/java/android/media/update/ViewGroupHelper.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Helper class for connecting the public API to an updatable implementation.
+ *
+ * @see ViewGroupProvider
+ *
+ * @hide
+ */
+public abstract class ViewGroupHelper<T extends ViewGroupProvider> extends ViewGroup {
+ /** @hide */
+ final public T mProvider;
+
+ /** @hide */
+ public ViewGroupHelper(ProviderCreator<T> creator,
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mProvider = creator.createProvider(this, new SuperProvider(),
+ new PrivateProvider());
+ }
+
+ /** @hide */
+ // TODO @SystemApi
+ public T getProvider() {
+ return mProvider;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ mProvider.onAttachedToWindow_impl();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ mProvider.onDetachedFromWindow_impl();
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return mProvider.getAccessibilityClassName_impl();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mProvider.onTouchEvent_impl(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ return mProvider.onTrackballEvent_impl(ev);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ mProvider.onFinishInflate_impl();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mProvider.setEnabled_impl(enabled);
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ mProvider.onVisibilityAggregated_impl(isVisible);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mProvider.onLayout_impl(changed, left, top, right, bottom);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected int getSuggestedMinimumWidth() {
+ return mProvider.getSuggestedMinimumWidth_impl();
+ }
+
+ @Override
+ protected int getSuggestedMinimumHeight() {
+ return mProvider.getSuggestedMinimumHeight_impl();
+ }
+
+ // setMeasuredDimension is final
+
+ @Override
+ protected boolean checkLayoutParams(LayoutParams p) {
+ return mProvider.checkLayoutParams_impl(p);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return mProvider.generateDefaultLayoutParams_impl();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return mProvider.generateLayoutParams_impl(attrs);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(LayoutParams lp) {
+ return mProvider.generateLayoutParams_impl(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return mProvider.shouldDelayChildPressedState_impl();
+ }
+
+ @Override
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ mProvider.measureChildWithMargins_impl(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+
+ /** @hide */
+ public class SuperProvider implements ViewGroupProvider {
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return ViewGroupHelper.super.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.super.onTrackballEvent(ev);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ ViewGroupHelper.super.onFinishInflate();
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ ViewGroupHelper.super.setEnabled(enabled);
+ }
+
+ @Override
+ public void onAttachedToWindow_impl() {
+ ViewGroupHelper.super.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ ViewGroupHelper.super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onVisibilityAggregated_impl(boolean isVisible) {
+ ViewGroupHelper.super.onVisibilityAggregated(isVisible);
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ // abstract method; no super
+ }
+
+ @Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ ViewGroupHelper.super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public int getSuggestedMinimumWidth_impl() {
+ return ViewGroupHelper.super.getSuggestedMinimumWidth();
+ }
+
+ @Override
+ public int getSuggestedMinimumHeight_impl() {
+ return ViewGroupHelper.super.getSuggestedMinimumHeight();
+ }
+
+ @Override
+ public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+ ViewGroupHelper.super.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ public boolean checkLayoutParams_impl(LayoutParams p) {
+ return ViewGroupHelper.super.checkLayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateDefaultLayoutParams_impl() {
+ return ViewGroupHelper.super.generateDefaultLayoutParams();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+ return ViewGroupHelper.super.generateLayoutParams(attrs);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+ return ViewGroupHelper.super.generateLayoutParams(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState_impl() {
+ return ViewGroupHelper.super.shouldDelayChildPressedState();
+ }
+
+ @Override
+ public void measureChildWithMargins_impl(View child,
+ int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ ViewGroupHelper.super.measureChildWithMargins(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+ }
+
+ /** @hide */
+ public class PrivateProvider implements ViewGroupProvider {
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return ViewGroupHelper.this.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.this.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.this.onTrackballEvent(ev);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ ViewGroupHelper.this.onFinishInflate();
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ ViewGroupHelper.this.setEnabled(enabled);
+ }
+
+ @Override
+ public void onAttachedToWindow_impl() {
+ ViewGroupHelper.this.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ ViewGroupHelper.this.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onVisibilityAggregated_impl(boolean isVisible) {
+ ViewGroupHelper.this.onVisibilityAggregated(isVisible);
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ ViewGroupHelper.this.onLayout(changed, left, top, right, bottom);
+ }
+
+ @Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ ViewGroupHelper.this.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public int getSuggestedMinimumWidth_impl() {
+ return ViewGroupHelper.this.getSuggestedMinimumWidth();
+ }
+
+ @Override
+ public int getSuggestedMinimumHeight_impl() {
+ return ViewGroupHelper.this.getSuggestedMinimumHeight();
+ }
+
+ @Override
+ public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+ ViewGroupHelper.this.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ public boolean checkLayoutParams_impl(LayoutParams p) {
+ return ViewGroupHelper.this.checkLayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateDefaultLayoutParams_impl() {
+ return ViewGroupHelper.this.generateDefaultLayoutParams();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+ return ViewGroupHelper.this.generateLayoutParams(attrs);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+ return ViewGroupHelper.this.generateLayoutParams(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState_impl() {
+ return ViewGroupHelper.this.shouldDelayChildPressedState();
+ }
+
+ @Override
+ public void measureChildWithMargins_impl(View child,
+ int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ ViewGroupHelper.this.measureChildWithMargins(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+ }
+
+ /** @hide */
+ @FunctionalInterface
+ public interface ProviderCreator<T extends ViewGroupProvider> {
+ T createProvider(ViewGroupHelper<T> instance, ViewGroupProvider superProvider,
+ ViewGroupProvider privateProvider);
+ }
+}
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewGroupProvider.java
index 0dd8f388a8fe..5f125298cd49 100644
--- a/media/java/android/media/update/ViewProvider.java
+++ b/media/java/android/media/update/ViewGroupProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,10 @@
package android.media.update;
import android.annotation.SystemApi;
+import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
/**
* Interface for connecting the public API to an updatable implementation.
@@ -33,8 +36,8 @@ import android.view.MotionEvent;
* @hide
*/
// TODO @SystemApi
-public interface ViewProvider {
- // TODO Add more (all?) methods from View
+public interface ViewGroupProvider {
+ // View methods
void onAttachedToWindow_impl();
void onDetachedFromWindow_impl();
CharSequence getAccessibilityClassName_impl();
@@ -42,4 +45,22 @@ public interface ViewProvider {
boolean onTrackballEvent_impl(MotionEvent ev);
void onFinishInflate_impl();
void setEnabled_impl(boolean enabled);
+ void onVisibilityAggregated_impl(boolean isVisible);
+ void onLayout_impl(boolean changed, int left, int top, int right, int bottom);
+ void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec);
+ int getSuggestedMinimumWidth_impl();
+ int getSuggestedMinimumHeight_impl();
+ void setMeasuredDimension_impl(int measuredWidth, int measuredHeight);
+
+ // ViewGroup methods
+ boolean checkLayoutParams_impl(LayoutParams p);
+ LayoutParams generateDefaultLayoutParams_impl();
+ LayoutParams generateLayoutParams_impl(AttributeSet attrs);
+ LayoutParams generateLayoutParams_impl(LayoutParams lp);
+ boolean shouldDelayChildPressedState_impl();
+ void measureChildWithMargins_impl(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed);
+
+ // ViewManager methods
+ // ViewParent methods
}
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
new file mode 100644
index 000000000000..71b8dacfbdfa
--- /dev/null
+++ b/media/jni/android_media_AudioPresentation.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+
+#include "jni.h"
+
+#include <media/AudioPresentationInfo.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+struct JAudioPresentationInfo {
+ struct fields_t {
+ jclass clazz;
+ jmethodID constructID;
+
+ // list parameters
+ jclass listclazz;
+ jmethodID listConstructId;
+ jmethodID listAddId;
+
+ void init(JNIEnv *env) {
+ jclass lclazz = env->FindClass("android/media/AudioPresentation");
+ if (lclazz == NULL) {
+ return;
+ }
+
+ clazz = (jclass)env->NewGlobalRef(lclazz);
+ if (clazz == NULL) {
+ return;
+ }
+
+ constructID = env->GetMethodID(clazz, "<init>",
+ "(IILjava/util/Map;Ljava/lang/String;IZZZ)V");
+ env->DeleteLocalRef(lclazz);
+
+ // list objects
+ jclass llistclazz = env->FindClass("java/util/ArrayList");
+ CHECK(llistclazz != NULL);
+ listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
+ CHECK(listclazz != NULL);
+ listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+ CHECK(listConstructId != NULL);
+ listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+ CHECK(listAddId != NULL);
+ env->DeleteLocalRef(llistclazz);
+ }
+
+ void exit(JNIEnv *env) {
+ env->DeleteGlobalRef(clazz);
+ clazz = NULL;
+ env->DeleteGlobalRef(listclazz);
+ listclazz = NULL;
+ }
+ };
+
+ static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+ ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
+
+ if (hashMapClazz.get() == NULL) {
+ return -EINVAL;
+ }
+ jmethodID hashMapConstructID =
+ env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
+
+ if (hashMapConstructID == NULL) {
+ return -EINVAL;
+ }
+ jmethodID hashMapPutID =
+ env->GetMethodID(
+ hashMapClazz.get(),
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+ if (hashMapPutID == NULL) {
+ return -EINVAL;
+ }
+
+ jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
+
+ for (size_t i = 0; i < msg->countEntries(); ++i) {
+ AMessage::Type valueType;
+ const char *key = msg->getEntryNameAt(i, &valueType);
+
+ if (!strncmp(key, "android._", 9)) {
+ // don't expose private keys (starting with android._)
+ continue;
+ }
+
+ jobject valueObj = NULL;
+
+ AString val;
+ CHECK(msg->findString(key, &val));
+
+ valueObj = env->NewStringUTF(val.c_str());
+
+ if (valueObj != NULL) {
+ jstring keyObj = env->NewStringUTF(key);
+
+ env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
+
+ env->DeleteLocalRef(keyObj); keyObj = NULL;
+ env->DeleteLocalRef(valueObj); valueObj = NULL;
+ }
+ }
+
+ *map = hashMap;
+
+ return OK;
+ }
+
+ jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
+ jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
+
+ for (size_t i = 0; i < info.countPresentations(); ++i) {
+ const sp<AudioPresentation> &ap = info.getPresentation(i);
+ jobject jLabelObject;
+
+ sp<AMessage> labelMessage = new AMessage();
+ for (size_t i = 0; i < ap->mLabels.size(); ++i) {
+ labelMessage->setString(ap->mLabels.keyAt(i).string(),
+ ap->mLabels.valueAt(i).string());
+ }
+ if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
+ return NULL;
+ }
+ jstring jLanguage = env->NewStringUTF(ap->mLanguage.string());
+
+ jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
+ static_cast<jint>(ap->mPresentationId),
+ static_cast<jint>(ap->mProgramId),
+ jLabelObject,
+ jLanguage,
+ static_cast<jint>(ap->mMasteringIndication),
+ static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
+ 1 : 0),
+ static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
+ 1 : 0),
+ static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
+ 1 : 0));
+ if (jValueObj == NULL) {
+ env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+ return NULL;
+ }
+
+ env->CallBooleanMethod(list, fields.listAddId, jValueObj);
+ env->DeleteLocalRef(jValueObj); jValueObj = NULL;
+ env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+ }
+ return list;
+ }
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 90ee8a6814d5..27eaed05b04d 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -19,12 +19,16 @@
#define LOG_TAG "MediaPlayer2-JNI"
#include "utils/Log.h"
+#include <sys/stat.h>
+
#include <media/mediaplayer2.h>
#include <media/AudioResamplerPublic.h>
+#include <media/DataSourceDesc.h>
#include <media/MediaHTTPService.h>
#include <media/MediaPlayer2Interface.h>
#include <media/MediaAnalyticsItem.h>
#include <media/NdkWrapper.h>
+#include <media/stagefright/Utils.h>
#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
#include <stdio.h>
#include <assert.h>
@@ -234,7 +238,8 @@ static sp<MediaPlayer2> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<Media
// event to the client application; otherwise, if exception is not NULL and
// opStatus is not OK, this method throws the given exception to the client
// application.
-static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
+static void process_media_player_call(
+ JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {
@@ -268,7 +273,7 @@ android_media_MediaPlayer2_setDataSourceAndHeaders(
jobjectArray keys, jobjectArray values) {
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
+ if (mp == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
@@ -282,16 +287,25 @@ android_media_MediaPlayer2_setDataSourceAndHeaders(
if (tmp == NULL) { // Out of memory
return;
}
- ALOGV("setDataSource: path %s", tmp);
+ ALOGV("setDataSourceAndHeaders: path %s", tmp);
+
+ if (strncmp(tmp, "content://", 10) == 0) {
+ ALOGE("setDataSourceAndHeaders: content scheme is not supported in native code");
+ jniThrowException(env, "java/io/IOException",
+ "content scheme is not supported in native code");
+ return;
+ }
+
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_URL;
+ dsd->mUrl = tmp;
- String8 pathStr(tmp);
env->ReleaseStringUTFChars(path, tmp);
tmp = NULL;
// We build a KeyedVector out of the key and val arrays
- KeyedVector<String8, String8> headersVector;
if (!ConvertKeyValueArraysToKeyedVector(
- env, keys, values, &headersVector)) {
+ env, keys, values, &dsd->mHeaders)) {
return;
}
@@ -299,20 +313,16 @@ android_media_MediaPlayer2_setDataSourceAndHeaders(
if (httpServiceObj != NULL) {
httpService = new JMedia2HTTPService(env, httpServiceObj);
}
-
- status_t opStatus =
- mp->setDataSource(
- httpService,
- pathStr,
- headersVector.size() > 0? &headersVector : NULL);
+ dsd->mHttpService = httpService;
process_media_player_call(
- env, thiz, opStatus, "java/io/IOException",
- "setDataSource failed." );
+ env, thiz, mp->setDataSource(dsd), "java/io/IOException",
+ "setDataSourceAndHeaders failed." );
}
static void
-android_media_MediaPlayer2_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
+android_media_MediaPlayer2_setDataSourceFD(
+ JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -325,12 +335,46 @@ android_media_MediaPlayer2_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fi
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- ALOGV("setDataSourceFD: fd %d", fd);
- process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
+ ALOGV("setDataSourceFD: fd=%d (%s), offset=%lld, length=%lld",
+ fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
+
+ struct stat sb;
+ int ret = fstat(fd, &sb);
+ if (ret != 0) {
+ ALOGE("setDataSourceFD: fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
+ jniThrowException(env, "java/io/IOException", "setDataSourceFD failed fstat");
+ return;
+ }
+
+ ALOGV("st_dev = %llu", static_cast<unsigned long long>(sb.st_dev));
+ ALOGV("st_mode = %u", sb.st_mode);
+ ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid));
+ ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid));
+ ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size));
+
+ if (offset >= sb.st_size) {
+ ALOGE("setDataSourceFD: offset is out of range");
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "setDataSourceFD failed, offset is out of range.");
+ return;
+ }
+ if (offset + length > sb.st_size) {
+ length = sb.st_size - offset;
+ ALOGV("setDataSourceFD: adjusted length = %lld", (long long)length);
+ }
+
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_FD;
+ dsd->mFD = fd;
+ dsd->mFDOffset = offset;
+ dsd->mFDLength = length;
+ process_media_player_call(env, thiz, mp->setDataSource(dsd),
+ "java/io/IOException", "setDataSourceFD failed." );
}
static void
-android_media_MediaPlayer2_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
+android_media_MediaPlayer2_setDataSourceCallback(
+ JNIEnv *env, jobject thiz, jobject dataSource)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -343,7 +387,11 @@ android_media_MediaPlayer2_setDataSourceCallback(JNIEnv *env, jobject thiz, jobj
return;
}
sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
- process_media_player_call(env, thiz, mp->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed." );
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_CALLBACK;
+ dsd->mCallbackSource = callbackDataSource;
+ process_media_player_call(env, thiz, mp->setDataSource(dsd),
+ "java/lang/RuntimeException", "setDataSourceCallback failed." );
}
static sp<ANativeWindowWrapper>
@@ -1099,45 +1147,6 @@ static void android_media_MediaPlayer2_attachAuxEffect(JNIEnv *env, jobject thi
process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
}
-static jint
-android_media_MediaPlayer2_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
- jstring addrString, jint port) {
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return INVALID_OPERATION;
- }
-
- const char *cAddrString = NULL;
-
- if (NULL != addrString) {
- cAddrString = env->GetStringUTFChars(addrString, NULL);
- if (cAddrString == NULL) { // Out of memory
- return NO_MEMORY;
- }
- }
- ALOGV("setRetransmitEndpoint: %s:%d",
- cAddrString ? cAddrString : "(null)", port);
-
- status_t ret;
- if (cAddrString && (port > 0xFFFF)) {
- ret = BAD_VALUE;
- } else {
- ret = mp->setRetransmitEndpoint(cAddrString,
- static_cast<uint16_t>(port));
- }
-
- if (NULL != addrString) {
- env->ReleaseStringUTFChars(addrString, cAddrString);
- }
-
- if (ret == INVALID_OPERATION ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- }
-
- return (jint) ret;
-}
-
static void
android_media_MediaPlayer2_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player)
{
@@ -1418,7 +1427,6 @@ static const JNINativeMethod gMethods[] = {
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer2_set_audio_session_id},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
- {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer2_setRetransmitEndpoint},
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer2;)V", (void *)android_media_MediaPlayer2_setNextMediaPlayer},
// Modular DRM
{ "_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index d2bc1743654f..b3a8b21147c1 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -20,6 +20,7 @@
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
+#include <vector>
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaRecorderJNI"
@@ -29,6 +30,7 @@
#include <camera/Camera.h>
#include <media/mediarecorder.h>
#include <media/MediaAnalyticsItem.h>
+#include <media/MicrophoneInfo.h>
#include <media/stagefright/PersistentSurface.h>
#include <utils/threads.h>
@@ -36,7 +38,9 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include "android_media_AudioErrors.h"
#include "android_media_MediaMetricsJNI.h"
+#include "android_media_MicrophoneInfo.h"
#include "android_runtime/AndroidRuntime.h"
#include <system/audio.h>
@@ -61,6 +65,12 @@ struct fields_t {
};
static fields_t fields;
+struct ArrayListFields {
+ jmethodID add;
+ jclass classId;
+};
+static ArrayListFields gArrayListFields;
+
static Mutex sLock;
// ----------------------------------------------------------------------------
@@ -565,6 +575,13 @@ android_media_MediaRecorder_native_init(JNIEnv *env)
if (fields.post_event == NULL) {
return;
}
+
+ clazz = env->FindClass("java/util/ArrayList");
+ if (clazz == NULL) {
+ return;
+ }
+ gArrayListFields.add = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");
+ gArrayListFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
}
@@ -707,6 +724,45 @@ android_media_MediaRecorder_enableDeviceCallback(JNIEnv *env, jobject thiz, jboo
process_media_recorder_call(env, mr->enableAudioDeviceCallback(enabled),
"java/lang/RuntimeException", "enableDeviceCallback failed.");
}
+
+static jint
+android_media_MediaRecord_getActiveMicrophones(JNIEnv *env,
+ jobject thiz, jobject jActiveMicrophones) {
+ if (jActiveMicrophones == NULL) {
+ ALOGE("jActiveMicrophones is null");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jActiveMicrophones, gArrayListFields.classId)) {
+ ALOGE("getActiveMicrophones not an arraylist");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ if (mr == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return (jint)AUDIO_JAVA_NO_INIT;
+ }
+
+ jint jStatus = AUDIO_JAVA_SUCCESS;
+ std::vector<media::MicrophoneInfo> activeMicrophones;
+ status_t status = mr->getActiveMicrophones(&activeMicrophones);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "MediaRecorder::getActiveMicrophones error %d", status);
+ jStatus = nativeToJavaStatus(status);
+ return jStatus;
+ }
+
+ for (size_t i = 0; i < activeMicrophones.size(); i++) {
+ jobject jMicrophoneInfo;
+ jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ env->CallBooleanMethod(jActiveMicrophones, gArrayListFields.add, jMicrophoneInfo);
+ env->DeleteLocalRef(jMicrophoneInfo);
+ }
+ return jStatus;
+}
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -742,7 +798,9 @@ static const JNINativeMethod gMethods[] = {
{"native_setInputDevice", "(I)Z", (void *)android_media_MediaRecorder_setInputDevice},
{"native_getRoutedDeviceId", "()I", (void *)android_media_MediaRecorder_getRoutedDeviceId},
- {"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback},
+ {"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback},
+
+ {"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones},
};
// This function only registers the native methods, and is called from
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 00fe6382fc17..4fb5e748aaac 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -48,6 +48,7 @@ cc_library_shared {
"sensor.cpp",
"sharedmem.cpp",
"storage_manager.cpp",
+ "surface_texture.cpp",
"trace.cpp",
],
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3d5ee39e37d3..d6dcd723e721 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -205,6 +205,14 @@ LIBANDROID {
AStorageManager_mountObb;
AStorageManager_new;
AStorageManager_unmountObb;
+ ASurfaceTexture_acquireANativeWindow; # introduced=28
+ ASurfaceTexture_attachToGLContext; # introduced=28
+ ASurfaceTexture_detachFromGLContext; # introduced=28
+ ASurfaceTexture_fromSurfaceTexture; # introduced=28
+ ASurfaceTexture_getTimestamp; # introduced=28
+ ASurfaceTexture_getTransformMatrix; # introduced=28
+ ASurfaceTexture_release; # introduced=28
+ ASurfaceTexture_updateTexImage; # introduced=28
ATrace_beginSection; # introduced=23
ATrace_endSection; # introduced=23
ATrace_isEnabled; # introduced=23
diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp
new file mode 100644
index 000000000000..b26688190ccd
--- /dev/null
+++ b/native/android/surface_texture.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/surface_texture.h>
+#include <android/surface_texture_jni.h>
+
+#define LOG_TAG "ASurfaceTexture"
+
+#include <utils/Log.h>
+
+#include <gui/GLConsumer.h>
+#include <gui/Surface.h>
+
+#include <android_runtime/android_graphics_SurfaceTexture.h>
+
+using namespace android;
+
+struct ASurfaceTexture {
+ sp<GLConsumer> consumer;
+ sp<IGraphicBufferProducer> producer;
+};
+
+ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+ if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) {
+ return nullptr;
+ }
+ ASurfaceTexture* ast = new ASurfaceTexture;
+ ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture);
+ ast->producer = SurfaceTexture_getProducer(env, surfacetexture);
+ return ast;
+}
+
+ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) {
+ sp<Surface> surface = new Surface(st->producer);
+ ANativeWindow* win(surface.get());
+ ANativeWindow_acquire(win);
+ return win;
+}
+
+void ASurfaceTexture_release(ASurfaceTexture* st) {
+ delete st;
+}
+
+int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) {
+ return st->consumer->attachToContext(tex);
+}
+
+int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) {
+ return st->consumer->detachFromContext();
+}
+
+int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) {
+ return st->consumer->updateTexImage();
+}
+
+void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+ st->consumer->getTransformMatrix(mtx);
+}
+
+int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) {
+ return st->consumer->getTimestamp();
+}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 8b01aef81c67..9f165bc97768 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@ import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
import android.os.Binder;
import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -43,7 +44,6 @@ import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
-import libcore.io.Streams;
import java.io.File;
import java.io.FileInputStream;
@@ -260,7 +260,7 @@ public class DefaultContainerService extends IntentService {
in = new FileInputStream(sourcePath);
out = new ParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 6fe8975577a4..9a66b07fb74f 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -94,7 +94,7 @@ public class Assistant extends NotificationAssistantService {
infile = mFile.openRead();
readXml(infile);
} catch (FileNotFoundException e) {
- // No data yet
+ Log.d(TAG, "File doesn't exist or isn't readable yet");
} catch (IOException e) {
Log.e(TAG, "Unable to read channel impressions", e);
} catch (NumberFormatException | XmlPullParserException e) {
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 7c35b48310f2..db48f610471d 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -325,7 +325,8 @@ public class AssistantTest extends ServiceTestCase<Assistant> {
int dismiss2 = 777;
String key2 = mAssistant.getKey("pkg2", 2, "channel2");
- String xml = "<assistant version=\"1\">\n"
+ String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<assistant version=\"1\">\n"
+ "<impression-set key=\"" + key1 + "\" "
+ "dismisses=\"" + dismiss1 + "\" views=\"" + views1
+ "\" streak=\"" + streak1 + "\"/>\n"
@@ -377,7 +378,6 @@ public class AssistantTest extends ServiceTestCase<Assistant> {
mAssistant.insertImpressions(key2, ci2);
mAssistant.insertImpressions(key3, ci3);
-
XmlSerializer serializer = new FastXmlSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index 79838962ef1e..c23f22648764 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -17,21 +17,20 @@
package com.android.settingslib.core.instrumentation;
import android.app.Activity;
-import android.content.Context;
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Intent;
import android.os.SystemClock;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
/**
* Logs visibility change of a fragment.
*/
-public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause {
+public class VisibilityLoggerMixin implements LifecycleObserver {
private static final String TAG = "VisibilityLoggerMixin";
@@ -55,7 +54,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau
mMetricsFeature = metricsFeature;
}
- @Override
+ @OnLifecycleEvent(Event.ON_RESUME)
public void onResume() {
mVisibleTimestamp = SystemClock.elapsedRealtime();
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
@@ -63,7 +62,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau
}
}
- @Override
+ @OnLifecycleEvent(Event.ON_PAUSE)
public void onPause() {
mVisibleTimestamp = 0;
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index e11017ce3449..f69944006a87 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -622,6 +622,19 @@ public class AccessPoint implements Comparable<AccessPoint> {
return builder.toString();
}
+ public static String getKey(WifiConfiguration config) {
+ StringBuilder builder = new StringBuilder();
+
+ if (TextUtils.isEmpty(config.SSID)) {
+ builder.append(config.BSSID);
+ } else {
+ builder.append(removeDoubleQuotes(config.SSID));
+ }
+
+ builder.append(',').append(getSecurity(config));
+ return builder.toString();
+ }
+
public String getKey() {
return mKey;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 1ac56a9de98f..fac585e06306 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -92,8 +92,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
* and used so as to assist with in-the-field WiFi connectivity debugging */
public static boolean sVerboseLogging;
- // TODO(b/36733768): Remove flag includeSaved
-
// TODO: Allow control of this?
// Combo scans can take 5-6s to complete - set to 10s.
private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
@@ -106,8 +104,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
private final NetworkRequest mNetworkRequest;
private final AtomicBoolean mConnected = new AtomicBoolean(false);
private final WifiListener mListener;
- private final boolean mIncludeSaved;
- private final boolean mIncludeScans;
@VisibleForTesting MainHandler mMainHandler;
@VisibleForTesting WorkHandler mWorkHandler;
private HandlerThread mWorkThread;
@@ -150,7 +146,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
// TODO(sghuman): Change this to be keyed on AccessPoint.getKey
private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>();
- private Integer mScanId = 0;
private NetworkInfo mLastNetworkInfo;
private WifiInfo mLastInfo;
@@ -189,16 +184,18 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
@Deprecated
public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, includeSaved, includeScans,
+ this(context, wifiListener,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
newIntentFilter());
}
+ // TODO(Sghuman): Clean up includeSaved and includeScans from all constructors and linked
+ // calling apps once IC window is complete
public WifiTracker(Context context, WifiListener wifiListener,
@NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, includeSaved, includeScans,
+ this(context, wifiListener,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
@@ -208,19 +205,13 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
@VisibleForTesting
WifiTracker(Context context, WifiListener wifiListener,
- boolean includeSaved, boolean includeScans,
WifiManager wifiManager, ConnectivityManager connectivityManager,
NetworkScoreManager networkScoreManager,
IntentFilter filter) {
- if (!includeSaved && !includeScans) {
- throw new IllegalArgumentException("Must include either saved or scans");
- }
mContext = context;
mMainHandler = new MainHandler(Looper.getMainLooper());
mWifiManager = wifiManager;
- mIncludeSaved = includeSaved;
- mIncludeScans = includeScans;
- mListener = wifiListener;
+ mListener = new WifiListenerWrapper(wifiListener);
mConnectivityManager = connectivityManager;
// check if verbose logging has been turned on or off
@@ -458,7 +449,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
private void handleResume() {
mScanResultCache.clear();
mSeenBssids.clear();
- mScanId = 0;
}
private Collection<ScanResult> updateScanResultCache(final List<ScanResult> newResults) {
@@ -533,7 +523,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
/**
* Update the internal list of access points.
*
- * <p>Do not called directly (except for forceUpdate), use {@link #updateAccessPoints()} which
+ * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which
* respects {@link #mStaleScanResults}.
*/
@GuardedBy("mLock")
@@ -542,7 +532,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(
- mLastInfo.getNetworkId(), mWifiManager.getConfiguredNetworks());
+ mLastInfo.getNetworkId(), configs);
}
// Swap the current access points into a cached list.
@@ -554,43 +544,12 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
accessPoint.clearConfig();
}
- /* Lookup table to more quickly update AccessPoints by only considering objects with the
- * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */
- Multimap<String, AccessPoint> existingApMap = new Multimap<String, AccessPoint>();
-
final Collection<ScanResult> results = updateScanResultCache(newScanResults);
- // TODO(sghuman): This entire block only exists to populate the WifiConfiguration for
- // APs, remove and refactor
+ final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
if (configs != null) {
for (WifiConfiguration config : configs) {
- if (config.selfAdded && config.numAssociation == 0) {
- continue;
- }
- AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints);
- if (mLastInfo != null && mLastNetworkInfo != null) {
- accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
- }
- if (mIncludeSaved) {
- // If saved network not present in scan result then set its Rssi to
- // UNREACHABLE_RSSI
- boolean apFound = false;
- for (ScanResult result : results) {
- if (result.SSID.equals(accessPoint.getSsidStr())) {
- apFound = true;
- break;
- }
- }
- if (!apFound) {
- accessPoint.setUnreachable();
- }
- accessPoints.add(accessPoint);
- existingApMap.put(accessPoint.getSsidStr(), accessPoint);
- } else {
- // If we aren't using saved networks, drop them into the cache so that
- // we have access to their saved info.
- cachedAccessPoints.add(accessPoint);
- }
+ configsByKey.put(AccessPoint.getKey(config), config);
}
}
@@ -626,40 +585,20 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) {
// List can not be empty as it is dynamically constructed on each iteration
ScanResult firstResult = entry.getValue().get(0);
- boolean found = false;
- for (AccessPoint accessPoint : existingApMap.getAll(firstResult.SSID)) {
- accessPoint.setScanResults(entry.getValue());
- found = true;
- break;
- }
- // Only create a new AP / add to the list if it wasn't already in the saved configs
- if (!found) {
- AccessPoint accessPoint =
- getCachedOrCreate(entry.getValue(), cachedAccessPoints);
- if (mLastInfo != null && mLastNetworkInfo != null) {
- accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
- }
-
- // TODO(sghuman): Move isPasspointNetwork logic into AccessPoint.java
- if (firstResult.isPasspointNetwork()) {
- // Retrieve a WifiConfiguration for a Passpoint provider that matches
- // the given ScanResult. This is used for showing that a given AP
- // (ScanResult) is available via a Passpoint provider (provider friendly
- // name).
- try {
- WifiConfiguration config =
- mWifiManager.getMatchingWifiConfig(firstResult);
- if (config != null) {
- accessPoint.update(config);
- }
- } catch (UnsupportedOperationException e) {
- // Passpoint not supported on the device.
- }
- }
+ AccessPoint accessPoint =
+ getCachedOrCreate(entry.getValue(), cachedAccessPoints);
+ if (mLastInfo != null && mLastNetworkInfo != null) {
+ accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+ }
- accessPoints.add(accessPoint);
+ // Update the matching config if there is one, to populate saved network info
+ WifiConfiguration config = configsByKey.get(entry.getKey());
+ if (config != null) {
+ accessPoint.update(config);
}
+
+ accessPoints.add(accessPoint);
}
}
@@ -1052,6 +991,39 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
}
+ /**
+ * Wraps the given {@link WifiListener} instance and executes it's methods on the Main Thread.
+ *
+ * <p>This mechanism allows us to no longer need a separate MainHandler and WorkHandler, which
+ * were previously both performing work, while avoiding errors which occur from executing
+ * callbacks which manipulate UI elements from a different thread than the MainThread.
+ */
+ private static class WifiListenerWrapper implements WifiListener {
+
+ private final Handler mHandler;
+ private final WifiListener mDelegatee;
+
+ public WifiListenerWrapper(WifiListener listener) {
+ mHandler = new Handler(Looper.getMainLooper());
+ mDelegatee = listener;
+ }
+
+ @Override
+ public void onWifiStateChanged(int state) {
+ mHandler.post(() -> mDelegatee.onWifiStateChanged(state));
+ }
+
+ @Override
+ public void onConnectedChanged() {
+ mHandler.post(() -> mDelegatee.onConnectedChanged());
+ }
+
+ @Override
+ public void onAccessPointsChanged() {
+ mHandler.post(() -> mDelegatee.onAccessPointsChanged());
+ }
+ }
+
public interface WifiListener {
/**
* Called when the state of Wifi has changed, the state will be one of
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index b36dda9deecf..6be4936413b7 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -281,8 +281,6 @@ public class WifiTrackerTest {
final WifiTracker wifiTracker = new WifiTracker(
mContext,
mockWifiListener,
- true,
- true,
mockWifiManager,
mockConnectivityManager,
mockNetworkScoreManager,
@@ -687,6 +685,7 @@ public class WifiTrackerTest {
*/
@Test
public void trackPasspointApWithPasspointDisabled() throws Exception {
+ // TODO(sghuman): Delete this test and replace with a passpoint test
WifiTracker tracker = createMockedWifiTracker();
// Add a Passpoint AP to the scan results.
@@ -709,10 +708,7 @@ public class WifiTrackerTest {
when(mockWifiManager.getConfiguredNetworks())
.thenReturn(new ArrayList<WifiConfiguration>());
when(mockWifiManager.getScanResults()).thenReturn(results);
- doThrow(new UnsupportedOperationException())
- .when(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class));
tracker.forceUpdate();
- verify(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class));
}
@Test
@@ -758,7 +754,7 @@ public class WifiTrackerTest {
tracker.forceUpdate();
verify(mockWifiManager).getConnectionInfo();
- verify(mockWifiManager, times(2)).getConfiguredNetworks();
+ verify(mockWifiManager, times(1)).getConfiguredNetworks();
verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
verify(mockWifiListener, never()).onAccessPointsChanged(); // mStaleAccessPoints is true
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index a2648861d1d8..1ab6afe5b99d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -18,6 +18,7 @@ package com.android.settingslib.core.instrumentation;
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -30,6 +31,8 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
@@ -39,6 +42,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
@@ -110,6 +115,30 @@ public class VisibilityLoggerMixinTest {
.hidden(nullable(Context.class), anyInt());
}
+ @Test
+ public void activityShouldBecomeVisibleAndHide() {
+ ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
+ TestActivity testActivity = ac.get();
+ MockitoAnnotations.initMocks(testActivity);
+ ac.create().start().resume();
+ verify(testActivity.mMetricsFeatureProvider, times(1)).visible(any(), anyInt(), anyInt());
+ ac.pause().stop().destroy();
+ verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt());
+ }
+
+ public static class TestActivity extends FragmentActivity {
+ @Mock
+ MetricsFeatureProvider mMetricsFeatureProvider;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ VisibilityLoggerMixin mixin = new VisibilityLoggerMixin(
+ TestInstrumentable.TEST_METRIC, mMetricsFeatureProvider);
+ getLifecycle().addObserver(mixin);
+ super.onCreate(savedInstanceState);
+ }
+ }
+
private final class TestInstrumentable implements Instrumentable {
public static final int TEST_METRIC = 12345;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 742814924137..1dc8e46a694a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -87,7 +87,6 @@ class DatabaseHelper extends SQLiteOpenHelper {
private static final HashSet<String> mValidTables = new HashSet<String>();
- private static final String DATABASE_JOURNAL_SUFFIX = "-journal";
private static final String DATABASE_BACKUP_SUFFIX = "-backup";
private static final String TABLE_SYSTEM = "system";
@@ -148,12 +147,7 @@ class DatabaseHelper extends SQLiteOpenHelper {
}
File databaseFile = mContext.getDatabasePath(getDatabaseName());
if (databaseFile.exists()) {
- databaseFile.delete();
- }
- File databaseJournalFile = mContext.getDatabasePath(getDatabaseName()
- + DATABASE_JOURNAL_SUFFIX);
- if (databaseJournalFile.exists()) {
- databaseJournalFile.delete();
+ SQLiteDatabase.deleteDatabase(databaseFile);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index b286f89d0049..537e8dca39c8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -523,9 +523,6 @@ class SettingsProtoDumpUtil {
Settings.Global.WIFI_WAKEUP_ENABLED,
GlobalSettingsProto.WIFI_WAKEUP_ENABLED);
dumpSetting(s, p,
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
- GlobalSettingsProto.WIFI_WAKEUP_AVAILABLE);
- dumpSetting(s, p,
Settings.Global.NETWORK_SCORING_UI_ENABLED,
GlobalSettingsProto.NETWORK_SCORING_UI_ENABLED);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d1459bba90c1..adb4dbf98dd0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2328,7 +2328,9 @@ public class SettingsProvider extends ContentProvider {
// Get all uids for the user's packages.
final List<PackageInfo> packages;
try {
- packages = mPackageManager.getInstalledPackages(0, user.id).getList();
+ packages = mPackageManager.getInstalledPackages(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ user.id).getList();
} catch (RemoteException e) {
throw new IllegalStateException("Package manager not available");
}
@@ -3015,7 +3017,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 152;
+ private static final int SETTINGS_VERSION = 153;
private final int mUserId;
@@ -3401,7 +3403,9 @@ public class SettingsProvider extends ContentProvider {
// Fill each uid with the legacy ssaid to be backwards compatible.
final List<PackageInfo> packages;
try {
- packages = mPackageManager.getInstalledPackages(0, userId).getList();
+ packages = mPackageManager.getInstalledPackages(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ userId).getList();
} catch (RemoteException e) {
throw new IllegalStateException("Package manager not available");
}
@@ -3416,6 +3420,9 @@ public class SettingsProvider extends ContentProvider {
// Android Id doesn't exist for this package so create it.
ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true,
info.packageName);
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Keep the legacy ssaid for uid=" + uid);
+ }
}
}
}
@@ -3540,21 +3547,9 @@ public class SettingsProvider extends ContentProvider {
}
if (currentVersion == 146) {
- // Version 147: Set the default value for WIFI_WAKEUP_AVAILABLE.
- if (userId == UserHandle.USER_SYSTEM) {
- final SettingsState globalSettings = getGlobalSettingsLocked();
- final Setting currentSetting = globalSettings.getSettingLocked(
- Settings.Global.WIFI_WAKEUP_AVAILABLE);
- if (currentSetting.getValue() == null) {
- final int defaultValue = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_wifi_wakeup_available);
- globalSettings.insertSettingLocked(
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
- String.valueOf(defaultValue),
- null, true, SettingsState.SYSTEM_PACKAGE_NAME);
- }
- }
-
+ // Version 147: Removed. (This version previously allowed showing the
+ // "wifi_wakeup_available" setting).
+ // The setting that was added here is deleted in 153.
currentVersion = 147;
}
@@ -3621,18 +3616,17 @@ public class SettingsProvider extends ContentProvider {
}
if (currentVersion == 151) {
- // Version 152: Reset wifi wake available for upgrading users
- final SettingsState globalSettings = getGlobalSettingsLocked();
- final int defaultValue = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_wifi_wakeup_available);
- globalSettings.insertSettingLocked(
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
- String.valueOf(defaultValue),
- null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-
+ // Version 152: Removed. (This version made the setting for wifi_wakeup enabled
+ // by default but it is now no longer configurable).
+ // The setting updated here is deleted in 153.
currentVersion = 152;
}
+ if (currentVersion == 152) {
+ getGlobalSettingsLocked().deleteSettingLocked("wifi_wakeup_available");
+ currentVersion = 153;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
new file mode 100644
index 000000000000..509cd1fb5db7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle" >
+ <solid
+ android:color="#e5e5e5" />
+ <corners android:radius="2dp" />
+</shape>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 9f6a946dea2e..100c2aa51b4d 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -25,87 +25,121 @@
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingTop="0dp"
- android:gravity="center_vertical"
- android:orientation="horizontal">
+ android:clipToPadding="false">
+
+ <View
+ android:id="@+id/qs_footer_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_gravity="top"
+ android:background="?android:attr/dividerHorizontal"/>
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="1dp"
+ android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
- android:gravity="end">
+ android:layout_gravity="center_vertical"
+ android:gravity="end" >
- <com.android.keyguard.CarrierText
- android:id="@+id/qs_carrier_text"
+ <LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical|start"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:textDirection="locale"
- android:singleLine="true" />
+ android:layout_weight="1" >
+ <!-- Add an extra 8dp margin before carrier text without shifting it right -->
+ <android.widget.Space
+ android:layout_width="8dp"
+ android:layout_height="match_parent" />
- <com.android.systemui.statusbar.phone.MultiUserSwitch
- android:id="@+id/multi_user_switch"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:background="@drawable/ripple_drawable"
- android:focusable="true">
+ <com.android.keyguard.CarrierText
+ android:id="@+id/qs_carrier_text"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_vertical|start"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textDirection="locale"
+ android:singleLine="true" />
+ </LinearLayout>
- <ImageView
- android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_expanded_size"
- android:layout_height="@dimen/multi_user_avatar_expanded_size"
- android:layout_gravity="center"
- android:scaleType="centerInside"/>
- </com.android.systemui.statusbar.phone.MultiUserSwitch>
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="match_parent" >
+ <View
+ android:id="@+id/qs_drag_handle_view"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ android:layout_marginTop="28dp"
+ android:background="@drawable/qs_footer_drag_handle" />
+ </FrameLayout>
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@android:id/edit"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:clickable="true"
- android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_edit"
- android:focusable="true"
- android:padding="16dp"
- android:src="@drawable/ic_mode_edit"
- android:tint="?android:attr/colorForeground"/>
+ <LinearLayout
+ android:id="@+id/qs_footer_actions_container"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="end" >
+ <com.android.systemui.statusbar.phone.MultiUserSwitch
+ android:id="@+id/multi_user_switch"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentEnd="true"
+ android:background="@drawable/ripple_drawable"
+ android:focusable="true">
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/settings_button_container"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:clipChildren="false"
- android:clipToPadding="false">
+ <ImageView
+ android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_expanded_size"
+ android:layout_height="@dimen/multi_user_avatar_expanded_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside"/>
+ </com.android.systemui.statusbar.phone.MultiUserSwitch>
- <com.android.systemui.statusbar.phone.SettingsButton
- android:id="@+id/settings_button"
- style="@android:style/Widget.Material.Button.Borderless"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/ripple_drawable"
- android:contentDescription="@string/accessibility_quick_settings_settings"
- android:src="@drawable/ic_settings_16dp"
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@android:id/edit"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:focusable="true"
+ android:padding="16dp"
+ android:src="@drawable/ic_mode_edit"
android:tint="?android:attr/colorForeground"/>
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/tuner_icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="36dp"
- android:paddingEnd="4dp"
- android:src="@drawable/tuner"
- android:tint="?android:attr/textColorTertiary"
- android:visibility="invisible"/>
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/settings_button_container"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <com.android.systemui.statusbar.phone.SettingsButton
+ android:id="@+id/settings_button"
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/accessibility_quick_settings_settings"
+ android:src="@drawable/ic_settings_16dp"
+ android:tint="?android:attr/colorForeground"/>
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/tuner_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="36dp"
+ android:paddingEnd="4dp"
+ android:src="@drawable/tuner"
+ android:tint="?android:attr/textColorTertiary"
+ android:visibility="invisible"/>
- </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+ </LinearLayout>
</LinearLayout>
</com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index c2b1009c3137..a3118b0a5d91 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -84,7 +84,25 @@
android:layout_height="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
- android:paddingEnd="@dimen/screen_pinning_request_frame_padding" >
+ android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg_light"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="matrix"
+ android:src="@drawable/screen_pinning_light_bg_circ" />
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingEnd="@dimen/screen_pinning_request_inner_padding"
+ android:paddingStart="@dimen/screen_pinning_request_inner_padding"
+ android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+ android:scaleType="matrix"
+ android:src="@drawable/screen_pinning_bg_circ" />
<ImageView
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index b5ef1d72fd8f..61fe906d65fc 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -78,7 +78,25 @@
android:id="@+id/screen_pinning_home_group"
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:layout_weight="0" >
+ android:layout_weight="0"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg_light"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:src="@drawable/screen_pinning_light_bg_circ" />
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:paddingLeft="@dimen/screen_pinning_request_inner_padding"
+ android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+ android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+ android:src="@drawable/screen_pinning_bg_circ" />
<ImageView
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
index f3a6d44f5ec7..d1ca2ce5935c 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
@@ -80,7 +80,27 @@
android:id="@+id/screen_pinning_home_group"
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:layout_weight="0" >
+ android:layout_weight="0"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" >
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg_light"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:layout_marginLeft="@dimen/screen_pinning_request_seascape_padding_negative"
+ android:src="@drawable/screen_pinning_light_bg_circ" />
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:layout_marginLeft="@dimen/screen_pinning_request_seascape_button_offset"
+ android:paddingRight="@dimen/screen_pinning_request_inner_padding"
+ android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+ android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+ android:src="@drawable/screen_pinning_bg_circ" />
<ImageView
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 117cd14f4463..36298ca0c095 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -20,14 +20,15 @@
android:background="@android:color/transparent"
android:theme="@style/qs_theme"
android:clipChildren="false" >
+ <!-- right-aligned to be physically near volume button -->
<LinearLayout
android:id="@+id/volume_dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
+ android:layout_gravity="center_vertical|right"
android:minWidth="@dimen/volume_dialog_panel_width"
android:background="@android:color/transparent"
- android:layout_margin="12dp"
+ android:layout_margin="@dimen/volume_dialog_base_margin"
android:translationZ="8dp"
android:orientation="vertical"
android:clipChildren="false" >
@@ -38,8 +39,8 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
+ android:paddingTop="10dp"
+ android:paddingBottom="10dp"
android:background="@drawable/rounded_bg_full"
android:translationZ="8dp"
android:orientation="horizontal" >
@@ -59,6 +60,7 @@
android:gravity="center"
android:layout_gravity="end"
android:translationZ="8dp"
+ android:clickable="true"
android:orientation="vertical" >
<TextView
@@ -70,13 +72,13 @@
android:maxLines="1"
android:layout_centerVertical="true"
android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="@style/TextAppearance.Volume.Header" />
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/ringer_icon"
style="@style/VolumeButtons"
android:background="?android:selectableItemBackgroundBorderless"
- android:layout_width="@dimen/volume_button_size"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="@dimen/volume_button_size"
android:tint="?android:attr/colorAccent"
android:soundEffectsEnabled="false" />
@@ -88,7 +90,7 @@
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 3e80085225e2..70654a803b44 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -29,15 +29,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
- android:padding="10dp">
+ android:padding="5dp">
<TextView
android:id="@+id/volume_row_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
+ android:maxLength="10"
android:maxLines="1"
android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="@style/TextAppearance.Volume.Header" />
<LinearLayout
android:id="@+id/output_chooser"
android:orientation="vertical"
@@ -53,9 +54,10 @@
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:maxLength="10"
android:ellipsize="end"
android:maxLines="1"
- android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
+ android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/output_chooser_button"
android:layout_width="24dp"
@@ -63,6 +65,7 @@
android:background="?android:selectableItemBackgroundBorderless"
android:contentDescription="@string/accessibility_output_chooser"
style="@style/VolumeButtons"
+ android:clickable="false"
android:layout_centerVertical="true"
android:src="@drawable/ic_swap"
android:soundEffectsEnabled="false" />
@@ -70,19 +73,20 @@
</LinearLayout>
<FrameLayout
android:id="@+id/volume_row_slider_frame"
- android:padding="10dp"
+ android:padding="0dp"
android:layout_width="@dimen/volume_dialog_panel_width"
- android:layout_height="150dp">
+ android:layoutDirection="rtl"
+ android:layout_height="@dimen/volume_dialog_panel_width">
<SeekBar
android:id="@+id/volume_row_slider"
+ android:clickable="true"
android:padding="0dp"
android:layout_margin="0dp"
- android:layout_width="150dp"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="@dimen/volume_dialog_panel_width"
+ android:layoutDirection="rtl"
android:layout_gravity="center"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:rotation="270" />
+ android:rotation="90" />
</FrameLayout>
<com.android.keyguard.AlphaOptimizedImageButton
@@ -91,6 +95,7 @@
android:padding="10dp"
android:layout_width="@dimen/volume_button_size"
android:layout_height="@dimen/volume_button_size"
+ android:background="?android:selectableItemBackgroundBorderless"
android:soundEffectsEnabled="false" />
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/config.xml b/packages/SystemUI/res/values-af/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-af/config.xml
+++ b/packages/SystemUI/res/values-af/config.xml
@@ -22,6 +22,5 @@
<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-am/config.xml b/packages/SystemUI/res/values-am/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-am/config.xml
+++ b/packages/SystemUI/res/values-am/config.xml
@@ -22,6 +22,5 @@
<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/config.xml b/packages/SystemUI/res/values-ar/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ar/config.xml
+++ b/packages/SystemUI/res/values-ar/config.xml
@@ -22,6 +22,5 @@
<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-az/config.xml b/packages/SystemUI/res/values-az/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-az/config.xml
+++ b/packages/SystemUI/res/values-az/config.xml
@@ -22,6 +22,5 @@
<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-b+sr+Latn/config.xml b/packages/SystemUI/res/values-b+sr+Latn/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/config.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/config.xml
@@ -22,6 +22,5 @@
<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-be/config.xml b/packages/SystemUI/res/values-be/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-be/config.xml
+++ b/packages/SystemUI/res/values-be/config.xml
@@ -22,6 +22,5 @@
<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-bg/config.xml b/packages/SystemUI/res/values-bg/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-bg/config.xml
+++ b/packages/SystemUI/res/values-bg/config.xml
@@ -22,6 +22,5 @@
<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-bn/config.xml b/packages/SystemUI/res/values-bn/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-bn/config.xml
+++ b/packages/SystemUI/res/values-bn/config.xml
@@ -22,6 +22,5 @@
<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-bs/config.xml b/packages/SystemUI/res/values-bs/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-bs/config.xml
+++ b/packages/SystemUI/res/values-bs/config.xml
@@ -22,6 +22,5 @@
<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-ca/config.xml b/packages/SystemUI/res/values-ca/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ca/config.xml
+++ b/packages/SystemUI/res/values-ca/config.xml
@@ -22,6 +22,5 @@
<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-cs/config.xml b/packages/SystemUI/res/values-cs/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-cs/config.xml
+++ b/packages/SystemUI/res/values-cs/config.xml
@@ -22,6 +22,5 @@
<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-da/config.xml b/packages/SystemUI/res/values-da/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-da/config.xml
+++ b/packages/SystemUI/res/values-da/config.xml
@@ -22,6 +22,5 @@
<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-de/config.xml b/packages/SystemUI/res/values-de/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-de/config.xml
+++ b/packages/SystemUI/res/values-de/config.xml
@@ -22,6 +22,5 @@
<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-el/config.xml b/packages/SystemUI/res/values-el/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-el/config.xml
+++ b/packages/SystemUI/res/values-el/config.xml
@@ -22,6 +22,5 @@
<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-en-rAU/config.xml b/packages/SystemUI/res/values-en-rAU/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-en-rAU/config.xml
+++ b/packages/SystemUI/res/values-en-rAU/config.xml
@@ -22,6 +22,5 @@
<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-en-rCA/config.xml b/packages/SystemUI/res/values-en-rCA/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-en-rCA/config.xml
+++ b/packages/SystemUI/res/values-en-rCA/config.xml
@@ -22,6 +22,5 @@
<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-en-rGB/config.xml b/packages/SystemUI/res/values-en-rGB/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-en-rGB/config.xml
+++ b/packages/SystemUI/res/values-en-rGB/config.xml
@@ -22,6 +22,5 @@
<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-en-rIN/config.xml b/packages/SystemUI/res/values-en-rIN/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-en-rIN/config.xml
+++ b/packages/SystemUI/res/values-en-rIN/config.xml
@@ -22,6 +22,5 @@
<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-en-rXC/config.xml b/packages/SystemUI/res/values-en-rXC/config.xml
index f015d9b378d4..5309563e3986 100644
--- a/packages/SystemUI/res/values-en-rXC/config.xml
+++ b/packages/SystemUI/res/values-en-rXC/config.xml
@@ -22,6 +22,5 @@
<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-es-rUS/config.xml b/packages/SystemUI/res/values-es-rUS/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-es-rUS/config.xml
+++ b/packages/SystemUI/res/values-es-rUS/config.xml
@@ -22,6 +22,5 @@
<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-es/config.xml b/packages/SystemUI/res/values-es/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-es/config.xml
+++ b/packages/SystemUI/res/values-es/config.xml
@@ -22,6 +22,5 @@
<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-et/config.xml b/packages/SystemUI/res/values-et/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-et/config.xml
+++ b/packages/SystemUI/res/values-et/config.xml
@@ -22,6 +22,5 @@
<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-eu/config.xml b/packages/SystemUI/res/values-eu/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-eu/config.xml
+++ b/packages/SystemUI/res/values-eu/config.xml
@@ -22,6 +22,5 @@
<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-fa/config.xml b/packages/SystemUI/res/values-fa/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-fa/config.xml
+++ b/packages/SystemUI/res/values-fa/config.xml
@@ -22,6 +22,5 @@
<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-fi/config.xml b/packages/SystemUI/res/values-fi/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-fi/config.xml
+++ b/packages/SystemUI/res/values-fi/config.xml
@@ -22,6 +22,5 @@
<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-fr-rCA/config.xml b/packages/SystemUI/res/values-fr-rCA/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-fr-rCA/config.xml
+++ b/packages/SystemUI/res/values-fr-rCA/config.xml
@@ -22,6 +22,5 @@
<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-fr/config.xml b/packages/SystemUI/res/values-fr/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-fr/config.xml
+++ b/packages/SystemUI/res/values-fr/config.xml
@@ -22,6 +22,5 @@
<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-gl/config.xml b/packages/SystemUI/res/values-gl/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-gl/config.xml
+++ b/packages/SystemUI/res/values-gl/config.xml
@@ -22,6 +22,5 @@
<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-gu/config.xml b/packages/SystemUI/res/values-gu/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-gu/config.xml
+++ b/packages/SystemUI/res/values-gu/config.xml
@@ -22,6 +22,5 @@
<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-hi/config.xml b/packages/SystemUI/res/values-hi/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-hi/config.xml
+++ b/packages/SystemUI/res/values-hi/config.xml
@@ -22,6 +22,5 @@
<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-hr/config.xml b/packages/SystemUI/res/values-hr/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-hr/config.xml
+++ b/packages/SystemUI/res/values-hr/config.xml
@@ -22,6 +22,5 @@
<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-hu/config.xml b/packages/SystemUI/res/values-hu/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-hu/config.xml
+++ b/packages/SystemUI/res/values-hu/config.xml
@@ -22,6 +22,5 @@
<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-hy/config.xml b/packages/SystemUI/res/values-hy/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-hy/config.xml
+++ b/packages/SystemUI/res/values-hy/config.xml
@@ -22,6 +22,5 @@
<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-in/config.xml b/packages/SystemUI/res/values-in/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-in/config.xml
+++ b/packages/SystemUI/res/values-in/config.xml
@@ -22,6 +22,5 @@
<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-is/config.xml b/packages/SystemUI/res/values-is/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-is/config.xml
+++ b/packages/SystemUI/res/values-is/config.xml
@@ -22,6 +22,5 @@
<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-it/config.xml b/packages/SystemUI/res/values-it/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-it/config.xml
+++ b/packages/SystemUI/res/values-it/config.xml
@@ -22,6 +22,5 @@
<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-iw/config.xml b/packages/SystemUI/res/values-iw/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-iw/config.xml
+++ b/packages/SystemUI/res/values-iw/config.xml
@@ -22,6 +22,5 @@
<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-ja/config.xml b/packages/SystemUI/res/values-ja/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ja/config.xml
+++ b/packages/SystemUI/res/values-ja/config.xml
@@ -22,6 +22,5 @@
<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-ka/config.xml b/packages/SystemUI/res/values-ka/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ka/config.xml
+++ b/packages/SystemUI/res/values-ka/config.xml
@@ -22,6 +22,5 @@
<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-kk/config.xml b/packages/SystemUI/res/values-kk/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-kk/config.xml
+++ b/packages/SystemUI/res/values-kk/config.xml
@@ -22,6 +22,5 @@
<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-km/config.xml b/packages/SystemUI/res/values-km/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-km/config.xml
+++ b/packages/SystemUI/res/values-km/config.xml
@@ -22,6 +22,5 @@
<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-kn/config.xml b/packages/SystemUI/res/values-kn/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-kn/config.xml
+++ b/packages/SystemUI/res/values-kn/config.xml
@@ -22,6 +22,5 @@
<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-ko/config.xml b/packages/SystemUI/res/values-ko/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ko/config.xml
+++ b/packages/SystemUI/res/values-ko/config.xml
@@ -22,6 +22,5 @@
<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-ky/config.xml b/packages/SystemUI/res/values-ky/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ky/config.xml
+++ b/packages/SystemUI/res/values-ky/config.xml
@@ -22,6 +22,5 @@
<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-lo/config.xml b/packages/SystemUI/res/values-lo/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-lo/config.xml
+++ b/packages/SystemUI/res/values-lo/config.xml
@@ -22,6 +22,5 @@
<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-lt/config.xml b/packages/SystemUI/res/values-lt/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-lt/config.xml
+++ b/packages/SystemUI/res/values-lt/config.xml
@@ -22,6 +22,5 @@
<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-lv/config.xml b/packages/SystemUI/res/values-lv/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-lv/config.xml
+++ b/packages/SystemUI/res/values-lv/config.xml
@@ -22,6 +22,5 @@
<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-mk/config.xml b/packages/SystemUI/res/values-mk/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-mk/config.xml
+++ b/packages/SystemUI/res/values-mk/config.xml
@@ -22,6 +22,5 @@
<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-ml/config.xml b/packages/SystemUI/res/values-ml/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ml/config.xml
+++ b/packages/SystemUI/res/values-ml/config.xml
@@ -22,6 +22,5 @@
<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-mn/config.xml b/packages/SystemUI/res/values-mn/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-mn/config.xml
+++ b/packages/SystemUI/res/values-mn/config.xml
@@ -22,6 +22,5 @@
<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-mr/config.xml b/packages/SystemUI/res/values-mr/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-mr/config.xml
+++ b/packages/SystemUI/res/values-mr/config.xml
@@ -22,6 +22,5 @@
<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-ms/config.xml b/packages/SystemUI/res/values-ms/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ms/config.xml
+++ b/packages/SystemUI/res/values-ms/config.xml
@@ -22,6 +22,5 @@
<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-my/config.xml b/packages/SystemUI/res/values-my/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-my/config.xml
+++ b/packages/SystemUI/res/values-my/config.xml
@@ -22,6 +22,5 @@
<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-nb/config.xml b/packages/SystemUI/res/values-nb/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-nb/config.xml
+++ b/packages/SystemUI/res/values-nb/config.xml
@@ -22,6 +22,5 @@
<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-ne/config.xml b/packages/SystemUI/res/values-ne/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ne/config.xml
+++ b/packages/SystemUI/res/values-ne/config.xml
@@ -22,6 +22,5 @@
<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-nl/config.xml b/packages/SystemUI/res/values-nl/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-nl/config.xml
+++ b/packages/SystemUI/res/values-nl/config.xml
@@ -22,6 +22,5 @@
<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-pa/config.xml b/packages/SystemUI/res/values-pa/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-pa/config.xml
+++ b/packages/SystemUI/res/values-pa/config.xml
@@ -22,6 +22,5 @@
<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-pl/config.xml b/packages/SystemUI/res/values-pl/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-pl/config.xml
+++ b/packages/SystemUI/res/values-pl/config.xml
@@ -22,6 +22,5 @@
<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-pt-rBR/config.xml b/packages/SystemUI/res/values-pt-rBR/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-pt-rBR/config.xml
+++ b/packages/SystemUI/res/values-pt-rBR/config.xml
@@ -22,6 +22,5 @@
<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-pt-rPT/config.xml b/packages/SystemUI/res/values-pt-rPT/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-pt-rPT/config.xml
+++ b/packages/SystemUI/res/values-pt-rPT/config.xml
@@ -22,6 +22,5 @@
<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-pt/config.xml b/packages/SystemUI/res/values-pt/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-pt/config.xml
+++ b/packages/SystemUI/res/values-pt/config.xml
@@ -22,6 +22,5 @@
<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-ro/config.xml b/packages/SystemUI/res/values-ro/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ro/config.xml
+++ b/packages/SystemUI/res/values-ro/config.xml
@@ -22,6 +22,5 @@
<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-ru/config.xml b/packages/SystemUI/res/values-ru/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ru/config.xml
+++ b/packages/SystemUI/res/values-ru/config.xml
@@ -22,6 +22,5 @@
<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-si/config.xml b/packages/SystemUI/res/values-si/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-si/config.xml
+++ b/packages/SystemUI/res/values-si/config.xml
@@ -22,6 +22,5 @@
<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-sk/config.xml b/packages/SystemUI/res/values-sk/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-sk/config.xml
+++ b/packages/SystemUI/res/values-sk/config.xml
@@ -22,6 +22,5 @@
<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-sl/config.xml b/packages/SystemUI/res/values-sl/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-sl/config.xml
+++ b/packages/SystemUI/res/values-sl/config.xml
@@ -22,6 +22,5 @@
<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-sq/config.xml b/packages/SystemUI/res/values-sq/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-sq/config.xml
+++ b/packages/SystemUI/res/values-sq/config.xml
@@ -22,6 +22,5 @@
<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-sr/config.xml b/packages/SystemUI/res/values-sr/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-sr/config.xml
+++ b/packages/SystemUI/res/values-sr/config.xml
@@ -22,6 +22,5 @@
<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-sv/config.xml b/packages/SystemUI/res/values-sv/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-sv/config.xml
+++ b/packages/SystemUI/res/values-sv/config.xml
@@ -22,6 +22,5 @@
<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-sw/config.xml b/packages/SystemUI/res/values-sw/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-sw/config.xml
+++ b/packages/SystemUI/res/values-sw/config.xml
@@ -22,6 +22,5 @@
<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-ta/config.xml b/packages/SystemUI/res/values-ta/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ta/config.xml
+++ b/packages/SystemUI/res/values-ta/config.xml
@@ -22,6 +22,5 @@
<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-te/config.xml b/packages/SystemUI/res/values-te/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-te/config.xml
+++ b/packages/SystemUI/res/values-te/config.xml
@@ -22,6 +22,5 @@
<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-th/config.xml b/packages/SystemUI/res/values-th/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-th/config.xml
+++ b/packages/SystemUI/res/values-th/config.xml
@@ -22,6 +22,5 @@
<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-tl/config.xml b/packages/SystemUI/res/values-tl/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-tl/config.xml
+++ b/packages/SystemUI/res/values-tl/config.xml
@@ -22,6 +22,5 @@
<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-tr/config.xml b/packages/SystemUI/res/values-tr/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-tr/config.xml
+++ b/packages/SystemUI/res/values-tr/config.xml
@@ -22,6 +22,5 @@
<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-uk/config.xml b/packages/SystemUI/res/values-uk/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-uk/config.xml
+++ b/packages/SystemUI/res/values-uk/config.xml
@@ -22,6 +22,5 @@
<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-ur/config.xml b/packages/SystemUI/res/values-ur/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-ur/config.xml
+++ b/packages/SystemUI/res/values-ur/config.xml
@@ -22,6 +22,5 @@
<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-uz/config.xml b/packages/SystemUI/res/values-uz/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-uz/config.xml
+++ b/packages/SystemUI/res/values-uz/config.xml
@@ -22,6 +22,5 @@
<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-vi/config.xml b/packages/SystemUI/res/values-vi/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-vi/config.xml
+++ b/packages/SystemUI/res/values-vi/config.xml
@@ -22,6 +22,5 @@
<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-zh-rCN/config.xml b/packages/SystemUI/res/values-zh-rCN/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-zh-rCN/config.xml
+++ b/packages/SystemUI/res/values-zh-rCN/config.xml
@@ -22,6 +22,5 @@
<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-zh-rHK/config.xml b/packages/SystemUI/res/values-zh-rHK/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-zh-rHK/config.xml
+++ b/packages/SystemUI/res/values-zh-rHK/config.xml
@@ -22,6 +22,5 @@
<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-zh-rTW/config.xml b/packages/SystemUI/res/values-zh-rTW/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-zh-rTW/config.xml
+++ b/packages/SystemUI/res/values-zh-rTW/config.xml
@@ -22,6 +22,5 @@
<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-zu/config.xml b/packages/SystemUI/res/values-zu/config.xml
index 477f2198e4a6..5309563e3986 100644
--- a/packages/SystemUI/res/values-zu/config.xml
+++ b/packages/SystemUI/res/values-zu/config.xml
@@ -22,6 +22,5 @@
<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/config.xml b/packages/SystemUI/res/values/config.xml
index 1cc1cc883268..f2e5d3b2cc3f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -46,7 +46,7 @@
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
<!-- Component name of launcher service for overview to connect to -->
- <string name="config_overviewServiceComponent" translateable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string>
+ <string name="config_overviewServiceComponent" translatable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string>
<!-- Whether or not we show the number in the bar. -->
<bool name="config_statusBarShowNumber">false</bool>
@@ -124,7 +124,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,alarm
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3db79d7556d5..e55c65a47f7b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -262,7 +262,11 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_width">120dp</dimen>
+ <!-- the amount the volume panel should be offset at the end from the view next to it (or
+ the scren edge, in portrait-->
+ <dimen name="volume_dialog_base_margin">12dp</dimen>
+
+ <dimen name="volume_dialog_panel_width">100dp</dimen>
<dimen name="output_chooser_panel_width">320dp</dimen>
@@ -332,6 +336,8 @@
<dimen name="qs_footer_padding_start">16dp</dimen>
<dimen name="qs_footer_padding_end">24dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
+ <!-- Difference between drag handle margin in QQS and expanded QS -->
+ <dimen name="qs_footer_drag_handle_offset">6dp</dimen>
<dimen name="qs_notif_collapsed_space">64dp</dimen>
@@ -902,4 +908,11 @@
<dimen name="config_batteryLevelTextSizeEnd" format="float">32.0</dimen>
<!-- Wireless Charging battery level text animation duration -->
<integer name="config_batteryLevelTextAnimationDuration">400</integer>
+
+ <!-- Wired charging on AOD, text animation duration -->
+ <integer name="wired_charging_aod_text_animation_duration_down">500</integer>
+ <!-- Wired charging on AOD, text animation duration -->
+ <integer name="wired_charging_aod_text_animation_duration_up">300</integer>
+ <!-- Wired charging on AOD, text animation distance -->
+ <integer name="wired_charging_aod_text_animation_distance">-30</integer>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 72615ce690e5..fadcbcd4f4bc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -739,6 +739,8 @@
<string name="quick_settings_wifi_on_label">Wi-Fi On</string>
<!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_detail_empty_text">No Wi-Fi networks available</string>
+ <!-- QuickSettings: Alarm title [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_alarm_title">Alarm</string>
<!-- QuickSettings: Cast title [CHAR LIMIT=NONE] -->
<string name="quick_settings_cast_title">Cast</string>
<!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] -->
@@ -1279,12 +1281,23 @@
<string name="screen_pinning_title">Screen is pinned</string>
<!-- Screen pinning dialog description. -->
<string name="screen_pinning_description">This keeps it in view until you unpin. Touch &amp; hold Back and Overview to unpin.</string>
+ <string name="screen_pinning_description_recents_invisible">This keeps it in view until you unpin. Touch &amp; hold Back and Home to unpin.</string>
<!-- Screen pinning dialog description. -->
<string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch &amp; hold Overview to unpin.</string>
+ <string name="screen_pinning_description_recents_invisible_accessible">This keeps it in view until you unpin. Touch &amp; hold Home to unpin.</string>
+ <!-- Notify use that they are in Lock-to-app -->
+ <string name="screen_pinning_toast">To unpin this screen, touch &amp; hold Back and Overview
+ buttons</string>
+ <string name="screen_pinning_toast_recents_invisible">To unpin this screen, touch &amp; hold Back
+ and Home buttons</string>
<!-- Screen pinning positive response. -->
<string name="screen_pinning_positive">Got it</string>
<!-- Screen pinning negative response. -->
<string name="screen_pinning_negative">No thanks</string>
+ <!-- Enter/Exiting screen pinning indication. -->
+ <string name="screen_pinning_start">Screen pinned</string>
+ <string name="screen_pinning_exit">Screen unpinned</string>
+
<!-- Hide quick settings tile confirmation title -->
<string name="quick_settings_reset_confirmation_title">Hide <xliff:g id="tile_label" example="Hotspot">%1$s</xliff:g>?</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2497d2080592..d2ed4d117cd8 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -410,16 +410,11 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
- <style name="TextAppearance.Volume.ZenSummary">
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <style name="TextAppearance.Volume.Header.Secondary">
+ <item name="android:textSize">12sp</item>
+ <item name="android:textColor">?android:attr/textColorTertiary</item>
</style>
- <style name="TextAppearance.Volume.ZenDetail">
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">sans-serif</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- </style>
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
<item name="android:background">@drawable/btn_borderless_rect</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index f9e1069cfe95..90e3b1e73454 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -16,6 +16,8 @@
package com.android.systemui.shared.system;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -31,6 +33,7 @@ import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration.ActivityType;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -47,6 +50,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.IRecentsAnimationController;
@@ -436,4 +440,23 @@ public class ActivityManagerWrapper {
Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
}
}
+
+ /**
+ * @return whether there is currently a locked task (ie. in screen pinning).
+ */
+ public boolean isLockToAppActive() {
+ try {
+ return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @return whether screen pinning is enabled.
+ */
+ public boolean isLockToAppEnabled() {
+ final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver();
+ return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index b8cfa3e48c20..aeef49689517 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -18,6 +18,7 @@ package com.android.systemui;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.BounceInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
@@ -43,6 +44,7 @@ public class Interpolators {
public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
public static final Interpolator PANEL_CLOSE_ACCELERATED
= new PathInterpolator(0.3f, 0, 0.5f, 1);
+ public static final Interpolator BOUNCE = new BounceInterpolator();
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 8d8b726f8fa8..b7e1d67a5b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -199,7 +199,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
launcherServiceIntent.setComponent(mLauncherComponentName);
boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
- UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser()));
+ UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
if (!bound) {
// Retry after exponential backoff timeout
final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 89bc82f87930..43b918dbea73 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -15,13 +15,19 @@
*/
package com.android.systemui;
+import android.annotation.StringRes;
import android.content.Context;
import android.view.WindowManager;
import android.widget.Toast;
+import static android.widget.Toast.Duration;
public class SysUIToast {
- public static Toast makeText(Context context, CharSequence text, int duration) {
+ public static Toast makeText(Context context, @StringRes int resId, @Duration int duration) {
+ return makeText(context, context.getString(resId), duration);
+ }
+
+ public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Toast toast = Toast.makeText(context, text, duration);
toast.getWindowParams().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8501519d26aa..eedc50f23d60 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -709,8 +709,7 @@ public class KeyguardViewMediator extends SystemUI {
mSecondaryDisplayShowing, true /* forceCallbacks */);
} else {
// The system's keyguard is disabled or missing.
- setShowingLocked(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()),
- mSecondaryDisplayShowing, true);
+ setShowingLocked(false, mSecondaryDisplayShowing, true);
}
mStatusBarKeyguardViewManager =
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index b43e99be828e..ea2a432ea6ca 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -44,6 +44,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
@@ -141,8 +142,12 @@ public class PowerUI extends SystemUI {
final int lowPowerModeTriggerLevel = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
- // NOTE: Keep the logic in sync with BatteryService.
- // TODO: Propagate this value from BatteryService to system UI, really.
+ // Note LOW_POWER_MODE_TRIGGER_LEVEL can take any value between 0 and 100, but
+ // for the UI purposes, let's cap it at 15% -- i.e. even if the trigger level is higher
+ // like 50%, let's not show the "low battery" notification until it hits
+ // config_lowBatteryWarningLevel, which is 15% by default.
+ // LOW_POWER_MODE_TRIGGER_LEVEL is still used in other places as-is. For example, if it's
+ // 50, then battery saver kicks in when the battery level hits 50%.
int warnLevel = Math.min(defWarnLevel, lowPowerModeTriggerLevel);
if (warnLevel == 0) {
@@ -242,7 +247,9 @@ public class PowerUI extends SystemUI {
}
// Show the correct version of low battery warning if needed
- maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
+ ThreadUtils.postOnBackgroundThread(() -> {
+ maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
+ });
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOffTime = SystemClock.elapsedRealtime();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index f960dc5b4a47..2a2bc0923e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -51,10 +51,11 @@ public class AutoAddTracker {
public AutoAddTracker(Context context) {
mContext = context;
mAutoAdded = new ArraySet<>(getAdded());
+ // TODO: remove migration code and shared preferences keys after P release
for (String[] convertPref : CONVERT_PREFS) {
if (Prefs.getBoolean(context, convertPref[0], false)) {
setTileAdded(convertPref[1]);
- Prefs.putBoolean(context, convertPref[0], false);
+ Prefs.remove(context, convertPref[0]);
}
}
mContext.getContentResolver().registerContentObserver(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b0d592a4d01..6ccb81772db9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -36,7 +36,6 @@ import com.android.systemui.statusbar.ExpandableOutlineView;
public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
- private final Path mClipPath = new Path();
private int mHeightOverride = -1;
protected View mQSPanel;
@@ -46,7 +45,6 @@ public class QSContainerImpl extends FrameLayout {
private QSCustomizer mQSCustomizer;
private View mQSFooter;
private View mBackground;
- private float mRadius;
private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -62,8 +60,6 @@ public class QSContainerImpl extends FrameLayout {
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
mBackground = findViewById(R.id.quick_settings_background);
- mRadius = getResources().getDimensionPixelSize(
- Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
setClickable(true);
@@ -115,18 +111,6 @@ public class QSContainerImpl extends FrameLayout {
updateExpansion();
}
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- boolean ret;
- canvas.save();
- if (child != mQSCustomizer) {
- canvas.clipPath(mClipPath);
- }
- ret = super.drawChild(canvas, child, drawingTime);
- canvas.restore();
- return ret;
- }
-
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -146,10 +130,6 @@ public class QSContainerImpl extends FrameLayout {
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
mBackground.setTop(mQSPanel.getTop());
mBackground.setBottom(height);
-
- ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
- mRadius,
- mClipPath);
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 92475da697bd..7f34acb2ef94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -21,7 +21,6 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
@@ -47,7 +46,6 @@ import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.ExpandableIndicator;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -61,6 +59,7 @@ import com.android.systemui.tuner.TunerService;
public class QSFooterImpl extends FrameLayout implements QSFooter,
OnClickListener, OnUserInfoChangedListener, EmergencyListener,
SignalCallback, CommandQueue.Callbacks {
+
private ActivityStarter mActivityStarter;
private UserInfoController mUserInfoController;
private SettingsButton mSettingsButton;
@@ -75,24 +74,32 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private boolean mListening;
private boolean mShowEmergencyCallsOnly;
+ private View mDivider;
protected MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
- protected TouchAnimator mSettingsAlpha;
+ protected TouchAnimator mFooterAnimator;
private float mExpansionAmount;
protected View mEdit;
- private TouchAnimator mAnimator;
+ private TouchAnimator mSettingsCogAnimator;
+
+ private View mActionsContainer;
+ private View mDragHandle;
+ private final int mDragHandleExpandOffset;
public QSFooterImpl(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ mDragHandleExpandOffset = getResources().
+ getDimensionPixelSize(R.dimen.qs_footer_drag_handle_offset);
+
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- Resources res = getResources();
-
+ mDivider = findViewById(R.id.qs_footer_divider);
mEdit = findViewById(android.R.id.edit);
mEdit.setOnClickListener(view ->
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
@@ -107,6 +114,9 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
+ mDragHandle = findViewById(R.id.qs_drag_handle_view);
+ mActionsContainer = findViewById(R.id.qs_footer_actions_container);
+
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
@@ -126,7 +136,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
int remaining = (width - numTiles * size) / (numTiles - 1);
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
- mAnimator = new Builder()
+ mSettingsCogAnimator = new Builder()
.addFloat(mSettingsContainer, "translationX",
isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
.addFloat(mSettingsButton, "rotation", -120, 0)
@@ -148,20 +158,20 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
private void updateResources() {
- updateSettingsAnimator();
+ updateFooterAnimator();
}
- private void updateSettingsAnimator() {
- mSettingsAlpha = createSettingsAlphaAnimator();
+ private void updateFooterAnimator() {
+ mFooterAnimator = createFooterAnimator();
}
@Nullable
- private TouchAnimator createSettingsAlphaAnimator() {
+ private TouchAnimator createFooterAnimator() {
return new TouchAnimator.Builder()
- .addFloat(mEdit, "alpha", 0, 1)
- .addFloat(mMultiUserSwitch, "alpha", 0, 1)
+ .addFloat(mDivider, "alpha", 0, 1)
.addFloat(mCarrierText, "alpha", 0, 1)
- .addFloat(mSettingsButton, "alpha", 0, 1)
+ .addFloat(mActionsContainer, "alpha", 0, 1)
+ .addFloat(mDragHandle, "translationY", 0, -mDragHandleExpandOffset)
.build();
}
@@ -180,10 +190,10 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
@Override
public void setExpansion(float headerExpansionFraction) {
mExpansionAmount = headerExpansionFraction;
- if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction);
+ if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
- if (mSettingsAlpha != null) {
- mSettingsAlpha.setPosition(headerExpansionFraction);
+ if (mFooterAnimator != null) {
+ mFooterAnimator.setPosition(headerExpansionFraction);
}
}
@@ -269,6 +279,11 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
@Override
public void onClick(View v) {
+ // Don't do anything until view are unhidden
+ if (!mExpanded) {
+ return;
+ }
+
if (v == mSettingsButton) {
if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
// If user isn't setup just unlock the device and dump them back at SUW.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 669439d7f6ca..d8e10516fe69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -69,7 +69,7 @@ public class QSFragment extends Fragment implements QS {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
- inflater =inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
+ inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
return inflater.inflate(R.layout.qs_panel, container, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 77c3bfab8de9..bf9746eafaf0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -23,6 +23,7 @@ import com.android.systemui.plugins.qs.*;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
+import com.android.systemui.qs.tiles.AlarmTile;
import com.android.systemui.qs.tiles.BatterySaverTile;
import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CastTile;
@@ -69,6 +70,7 @@ public class QSFactoryImpl implements QSFactory {
else if (tileSpec.equals("saver")) return new DataSaverTile(mHost);
else if (tileSpec.equals("night")) return new NightDisplayTile(mHost);
else if (tileSpec.equals("nfc")) return new NfcTile(mHost);
+ else if (tileSpec.equals("alarm")) return new AlarmTile(mHost);
// Intent tiles.
else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(mHost, tileSpec);
else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java
new file mode 100644
index 000000000000..ff3fe731ad4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.service.quicksettings.Tile.STATE_ACTIVE;
+import static android.service.quicksettings.Tile.STATE_UNAVAILABLE;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.QS_ALARM;
+import static com.android.systemui.keyguard.KeyguardSliceProvider.formatNextAlarm;
+
+import android.app.AlarmManager.AlarmClockInfo;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.provider.AlarmClock;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+
+public class AlarmTile extends QSTileImpl implements NextAlarmChangeCallback {
+ private final NextAlarmController mController;
+ private String mNextAlarm;
+ private PendingIntent mIntent;
+
+ public AlarmTile(QSTileHost host) {
+ super(host);
+ mController = Dependency.get(NextAlarmController.class);
+ }
+
+ @Override
+ public State newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ protected void handleClick() {
+ if (mIntent != null) {
+ Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(mIntent);
+ }
+ }
+
+ @Override
+ protected void handleUpdateState(State state, Object arg) {
+ state.state = mNextAlarm != null ? STATE_ACTIVE : STATE_UNAVAILABLE;
+ state.label = getTileLabel();
+ state.secondaryLabel = mNextAlarm;
+ state.icon = ResourceIcon.get(R.drawable.stat_sys_alarm);
+ ((BooleanState) state).value = mNextAlarm != null;
+ }
+
+ @Override
+ public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
+ if (nextAlarm != null) {
+ mNextAlarm = formatNextAlarm(mContext, nextAlarm);
+ mIntent = nextAlarm.getShowIntent();
+ } else {
+ mNextAlarm = null;
+ mIntent = null;
+ }
+ refreshState();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return QS_ALARM;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return new Intent(AlarmClock.ACTION_SET_ALARM);
+ }
+
+ @Override
+ protected void handleSetListening(boolean listening) {
+ if (listening) {
+ mController.addCallback(this);
+ } else {
+ mController.removeCallback(this);
+ }
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.status_bar_alarm);
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index aaf6ef52d761..6205e9afcb03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -19,8 +19,6 @@ package com.android.systemui.qs.tiles;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
-import android.app.AlarmManager;
-import android.app.AlarmManager.AlarmClockInfo;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -30,11 +28,9 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.net.Uri;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.service.notification.ScheduleCalendar;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.quicksettings.Tile;
@@ -56,7 +52,6 @@ import com.android.systemui.R;
import com.android.systemui.SysUIToast;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -197,7 +192,8 @@ public class DndTile extends QSTileImpl<BooleanState> {
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.slash.isSlashed = !state.value;
state.label = getTileLabel();
- state.secondaryLabel = getSecondaryLabel(zen != Global.ZEN_MODE_OFF);
+ state.secondaryLabel = ZenModeConfig.getDescription(mContext,zen != Global.ZEN_MODE_OFF,
+ mController.getConfig());
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
switch (zen) {
@@ -226,102 +222,6 @@ public class DndTile extends QSTileImpl<BooleanState> {
state.expandedAccessibilityClassName = Switch.class.getName();
}
- /**
- * Returns the secondary label to use for the given instance of do not disturb.
- * - If turned on manually and end time is known, returns end time.
- * - If turned on by an automatic rule, returns the automatic rule name.
- * - If on due to an app, returns the app name.
- * - If there's a combination of rules/apps that trigger, then shows the one that will
- * last the longest if applicable.
- * @return null if do not disturb is off.
- */
- private String getSecondaryLabel(boolean zenOn) {
- if (!zenOn) {
- return null;
- }
-
- ZenModeConfig config = mController.getConfig();
- String secondaryText = "";
- long latestEndTime = -1;
-
- // DND turned on by manual rule
- if (config.manualRule != null) {
- final Uri id = config.manualRule.conditionId;
- if (config.manualRule.enabler != null) {
- // app triggered manual rule
- String appName = ZenModeConfig.getOwnerCaption(mContext, config.manualRule.enabler);
- if (!appName.isEmpty()) {
- secondaryText = appName;
- }
- } else {
- if (id == null) {
- // Do not disturb manually triggered to remain on forever until turned off
- // No subtext
- return null;
- } else {
- latestEndTime = ZenModeConfig.tryParseCountdownConditionId(id);
- if (latestEndTime > 0) {
- final CharSequence formattedTime = ZenModeConfig.getFormattedTime(mContext,
- latestEndTime, ZenModeConfig.isToday(latestEndTime),
- mContext.getUserId());
- secondaryText = mContext.getString(R.string.qs_dnd_until, formattedTime);
- }
- }
- }
- }
-
- // DND turned on by an automatic rule
- for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
- if (automaticRule.isAutomaticActive()) {
- if (ZenModeConfig.isValidEventConditionId(automaticRule.conditionId) ||
- ZenModeConfig.isValidScheduleConditionId(automaticRule.conditionId)) {
- // set text if automatic rule end time is the latest active rule end time
- long endTime = parseAutomaticRuleEndTime(automaticRule.conditionId);
- if (endTime > latestEndTime) {
- latestEndTime = endTime;
- secondaryText = automaticRule.name;
- }
- } else {
- // set text if 3rd party rule
- return automaticRule.name;
- }
- }
- }
-
- return !secondaryText.equals("") ? secondaryText : null;
- }
-
- private long parseAutomaticRuleEndTime(Uri id) {
- if (ZenModeConfig.isValidEventConditionId(id)) {
- // cannot look up end times for events
- return Long.MAX_VALUE;
- }
-
- if (ZenModeConfig.isValidScheduleConditionId(id)) {
- ScheduleCalendar schedule = ZenModeConfig.toScheduleCalendar(id);
- long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
-
- // check if automatic rule will end on next alarm
- if (schedule.exitAtAlarm()) {
- long nextAlarm = getNextAlarm(mContext);
- schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
- if (schedule.shouldExitForAlarm(endTimeMs)) {
- return nextAlarm;
- }
- }
-
- return endTimeMs;
- }
-
- return -1;
- }
-
- private long getNextAlarm(Context context) {
- final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- final AlarmClockInfo info = alarms.getNextAlarmClock(mContext.getUserId());
- return info != null ? info.getTriggerTime() : 0;
- }
-
@Override
public int getMetricsCategory() {
return MetricsEvent.QS_DND;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 8bdbf28b5cf1..d7f2a2642180 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -102,7 +102,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
state.value = locationEnabled;
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION);
if (state.disabledByPolicy == false) {
- checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION_MODE);
+ checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION);
}
state.icon = mIcon;
state.slash.isSlashed = !state.value;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 1da4deb61176..409c753c147c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -415,7 +415,7 @@ public class Recents extends SystemUI
final int activityType = runningTask != null
? runningTask.configuration.windowConfiguration.getActivityType()
: ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = sSystemServicesProxy.isScreenPinningActive();
+ boolean screenPinningActive = ActivityManagerWrapper.getInstance().isLockToAppActive();
boolean isRunningTaskInHomeOrRecentsStack =
activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index ee1b09109d38..3f6f30bba8c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -386,8 +386,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
public void toggleRecents(int growTarget) {
// Skip preloading if the task is locked
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isScreenPinningActive()) {
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
return;
}
@@ -409,8 +408,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
MutableBoolean isHomeStackVisible = new MutableBoolean(true);
long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!launchState.launchedWithAltTab) {
@@ -466,8 +465,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
public void preloadRecents() {
// Skip preloading if the task is locked
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isScreenPinningActive()) {
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
return;
}
@@ -481,6 +479,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// RecentsActivity) only if there is a task to animate to. Post this to ensure that we
// don't block the touch feedback on the nav bar button which triggers this.
mHandler.post(() -> {
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (!ssp.isRecentsActivityVisible(null)) {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 316ad1661898..57f7818eae58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -23,15 +23,12 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Binder;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Gravity;
-import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -43,6 +40,9 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.phone.NavigationBarView;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
@@ -233,11 +233,30 @@ public class ScreenPinningRequest implements View.OnClickListener {
.setVisibility(View.INVISIBLE);
}
+ StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+ NavigationBarView navigationBarView = statusBar.getNavigationBarView();
+ final boolean recentsVisible = navigationBarView != null
+ && navigationBarView.isRecentsButtonVisible();
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
+ int descriptionStringResId;
+ if (recentsVisible) {
+ mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE);
+ mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE);
+ mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE);
+ descriptionStringResId = touchExplorationEnabled
+ ? R.string.screen_pinning_description_accessible
+ : R.string.screen_pinning_description;
+ } else {
+ mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(INVISIBLE);
+ mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(VISIBLE);
+ mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(VISIBLE);
+ descriptionStringResId = touchExplorationEnabled
+ ? R.string.screen_pinning_description_recents_invisible_accessible
+ : R.string.screen_pinning_description_recents_invisible;
+ }
+
((TextView) mLayout.findViewById(R.id.screen_pinning_description))
- .setText(touchExplorationEnabled
- ? R.string.screen_pinning_description_accessible
- : R.string.screen_pinning_description);
+ .setText(descriptionStringResId);
final int backBgVisibility = touchExplorationEnabled ? View.INVISIBLE : View.VISIBLE;
mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
mLayout.findViewById(R.id.screen_pinning_back_bg_light).setVisibility(backBgVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 613d9fbb985c..93fd34aa0519 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -379,29 +379,6 @@ public class SystemServicesProxy {
}
/**
- * Returns a global setting.
- */
- public int getGlobalSetting(Context context, String setting) {
- ContentResolver cr = context.getContentResolver();
- return Settings.Global.getInt(cr, setting, 0);
- }
-
- /**
- * Returns a system setting.
- */
- public int getSystemSetting(Context context, String setting) {
- ContentResolver cr = context.getContentResolver();
- return Settings.System.getInt(cr, setting, 0);
- }
-
- /**
- * Returns a system property.
- */
- public String getSystemProperty(String key) {
- return SystemProperties.get(key);
- }
-
- /**
* Returns the smallest width/height.
*/
public int getDeviceSmallestWidth() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 5be2900831b3..3cc3273c0db4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -95,6 +95,7 @@ import com.android.systemui.recents.views.grid.GridTaskView;
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2187,8 +2188,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
private void readSystemFlags() {
SystemServicesProxy ssp = Recents.getSystemServices();
mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
- Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isLockToAppEnabled();
}
private void updateStackActionButtonVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 79e9f7b45d8d..11bdf6b3c72e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -24,6 +24,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.support.annotation.VisibleForTesting;
import android.util.Pair;
@@ -90,6 +91,8 @@ public class CommandQueue extends IStatusBar.Stub {
private static final int MSG_FINGERPRINT_ERROR = 42 << MSG_SHIFT;
private static final int MSG_FINGERPRINT_HIDE = 43 << MSG_SHIFT;
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
+ private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
+ private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -148,6 +151,8 @@ public class CommandQueue extends IStatusBar.Stub {
default void clickTile(ComponentName tile) { }
default void handleSystemKey(int arg1) { }
+ default void showPinningEnterExitToast(boolean entering) { }
+ default void showPinningEscapeToast() { }
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
@@ -453,6 +458,21 @@ public class CommandQueue extends IStatusBar.Stub {
}
@Override
+ public void showPinningEnterExitToast(boolean entering) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ENTER_EXIT, entering).sendToTarget();
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ESCAPE).sendToTarget();
+ }
+ }
+
+
+ @Override
public void showGlobalActionsMenu() {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_GLOBAL_ACTIONS);
@@ -767,6 +787,16 @@ public class CommandQueue extends IStatusBar.Stub {
mCallbacks.get(i).showChargingAnimation(msg.arg1);
}
break;
+ case MSG_SHOW_PINNING_TOAST_ENTER_EXIT:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showPinningEnterExitToast((Boolean) msg.obj);
+ }
+ break;
+ case MSG_SHOW_PINNING_TOAST_ESCAPE:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showPinningEscapeToast();
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0a12be4eac7f..b7a15005b170 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -44,6 +46,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.LockIcon;
@@ -192,7 +195,7 @@ public class KeyguardIndicationController {
if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
hideTransientIndication();
}
- updateIndication();
+ updateIndication(false);
} else if (!visible) {
// If we unlock and return to keyguard quickly, previous error should not be shown
hideTransientIndication();
@@ -204,7 +207,7 @@ public class KeyguardIndicationController {
*/
public void setRestingIndication(String restingIndication) {
mRestingIndication = restingIndication;
- updateIndication();
+ updateIndication(false);
}
/**
@@ -265,7 +268,8 @@ public class KeyguardIndicationController {
mWakeLock.setAcquired(true);
hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
}
- updateIndication();
+
+ updateIndication(false);
}
/**
@@ -275,11 +279,11 @@ public class KeyguardIndicationController {
if (mTransientIndication != null) {
mTransientIndication = null;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- updateIndication();
+ updateIndication(false);
}
}
- protected final void updateIndication() {
+ protected final void updateIndication(boolean animate) {
if (TextUtils.isEmpty(mTransientIndication)) {
mWakeLock.setAcquired(false);
}
@@ -295,7 +299,35 @@ public class KeyguardIndicationController {
mTextView.switchIndication(mTransientIndication);
} else if (mPowerPluggedIn) {
String indication = computePowerIndication();
- mTextView.switchIndication(indication);
+ if (animate) {
+ int yTranslation = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_distance);
+ int animateUpDuration = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_duration_up);
+ int animateDownDuration = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_duration_down);
+ mTextView.animate()
+ .translationYBy(yTranslation)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(animateUpDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mTextView.switchIndication(indication);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTextView.animate()
+ .setDuration(animateDownDuration)
+ .setInterpolator(Interpolators.BOUNCE)
+ .translationYBy(-1 * yTranslation)
+ .setListener(null);
+ }
+ });
+ } else {
+ mTextView.switchIndication(indication);
+ }
+
} else {
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
@@ -390,7 +422,7 @@ public class KeyguardIndicationController {
public void onReceive(Context context, Intent intent) {
mHandler.post(() -> {
if (mVisible) {
- updateIndication();
+ updateIndication(false);
}
});
}
@@ -412,7 +444,7 @@ public class KeyguardIndicationController {
return;
}
mDozing = dozing;
- updateIndication();
+ updateIndication(false);
updateDisclosure();
}
@@ -445,7 +477,7 @@ public class KeyguardIndicationController {
mChargingWattage = status.maxChargingWattage;
mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
mBatteryLevel = status.level;
- updateIndication();
+ updateIndication(!wasPluggedIn && mPowerPluggedIn);
if (mDozing) {
if (!wasPluggedIn && mPowerPluggedIn) {
showTransientIndication(computePowerIndication());
@@ -551,7 +583,7 @@ public class KeyguardIndicationController {
@Override
public void onUserUnlocked() {
if (mVisible) {
- updateIndication();
+ updateIndication(false);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 8325df7faddb..cad956cd602a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -802,10 +802,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
updateRelativeOffset();
}
- public void setDarkOffsetX(int offsetX) {
- mShelfIcons.setDarkOffsetX(offsetX);
- }
-
private class ShelfState extends ExpandableViewState {
private float openedAmount;
private boolean hasItemsInStableShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3dfb9130af2e..3ebeb4d45c26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -379,7 +379,7 @@ public class CarStatusBar extends StatusBar implements
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by removing all heads-
// up notifications.
- mHeadsUpManager.releaseAllImmediately();
+ mHeadsUpManager.removeAllHeadsUpEntries();
super.animateExpandNotificationsPanel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
index 3491f8121545..c2141714b391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification;
import android.content.Context;
import android.support.v4.view.AsyncLayoutInflater;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -30,15 +31,23 @@ import com.android.systemui.statusbar.NotificationData;
* An inflater task that asynchronously inflates a ExpandableNotificationRow
*/
public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {
+
+ private static final String TAG = "RowInflaterTask";
+ private static final boolean TRACE_ORIGIN = true;
+
private RowInflationFinishedListener mListener;
private NotificationData.Entry mEntry;
private boolean mCancelled;
+ private Throwable mInflateOrigin;
/**
* Inflates a new notificationView. This should not be called twice on this object
*/
public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
RowInflationFinishedListener listener) {
+ if (TRACE_ORIGIN) {
+ mInflateOrigin = new Throwable("inflate requested here");
+ }
mListener = listener;
AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
mEntry = entry;
@@ -54,8 +63,16 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf
@Override
public void onInflateFinished(View view, int resid, ViewGroup parent) {
if (!mCancelled) {
- mEntry.onInflationTaskFinished();
- mListener.onInflationFinished((ExpandableNotificationRow) view);
+ try {
+ mEntry.onInflationTaskFinished();
+ mListener.onInflationFinished((ExpandableNotificationRow) view);
+ } catch (Throwable t) {
+ if (mInflateOrigin != null) {
+ Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin);
+ t.addSuppressed(mInflateOrigin);
+ }
+ throw t;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 36f9f6b79417..b220686b3056 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -14,16 +14,13 @@
package com.android.systemui.statusbar.phone;
+import android.app.AlarmManager.AlarmClockInfo;
import android.content.Context;
import android.os.Handler;
-import android.os.Looper;
import android.provider.Settings.Secure;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.SecureSetting;
@@ -31,27 +28,37 @@ import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
/**
* Manages which tiles should be automatically added to QS.
*/
public class AutoTileManager {
-
public static final String HOTSPOT = "hotspot";
public static final String SAVER = "saver";
public static final String INVERSION = "inversion";
public static final String WORK = "work";
public static final String NIGHT = "night";
+ public static final String ALARM = "alarm";
+
private final Context mContext;
private final QSTileHost mHost;
private final Handler mHandler;
private final AutoAddTracker mAutoTracker;
public AutoTileManager(Context context, QSTileHost host) {
- mAutoTracker = new AutoAddTracker(context);
+ this(context, new AutoAddTracker(context), host,
+ new Handler(Dependency.get(Dependency.BG_LOOPER)));
+ }
+
+ @VisibleForTesting
+ AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
+ Handler handler) {
+ mAutoTracker = autoAddTracker;
mContext = context;
mHost = host;
- mHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
+ mHandler = handler;
if (!mAutoTracker.isAdded(HOTSPOT)) {
Dependency.get(HotspotController.class).addCallback(mHotspotCallback);
}
@@ -76,20 +83,25 @@ public class AutoTileManager {
if (!mAutoTracker.isAdded(WORK)) {
Dependency.get(ManagedProfileController.class).addCallback(mProfileCallback);
}
-
if (!mAutoTracker.isAdded(NIGHT)
- && ColorDisplayController.isAvailable(mContext)) {
+ && ColorDisplayController.isAvailable(mContext)) {
Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
}
+ if (!mAutoTracker.isAdded(ALARM)) {
+ Dependency.get(NextAlarmController.class).addCallback(mNextAlarmChangeCallback);
+ }
}
public void destroy() {
- mColorsSetting.setListening(false);
+ if (mColorsSetting != null) {
+ mColorsSetting.setListening(false);
+ }
mAutoTracker.destroy();
Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
Dependency.get(ColorDisplayController.class).setListener(null);
+ Dependency.get(NextAlarmController.class).removeCallback(mNextAlarmChangeCallback);
}
private final ManagedProfileController.Callback mProfileCallback =
@@ -138,6 +150,19 @@ public class AutoTileManager {
}
};
+ private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
+ @Override
+ public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
+ if (mAutoTracker.isAdded(ALARM)) return;
+ if (nextAlarm != null) {
+ mHost.addTile(ALARM);
+ mAutoTracker.setTileAdded(ALARM);
+ mHandler.post(() -> Dependency.get(NextAlarmController.class)
+ .removeCallback(mNextAlarmChangeCallback));
+ }
+ }
+ };
+
@VisibleForTesting
final ColorDisplayController.Callback mColorDisplayCallback =
new ColorDisplayController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
deleted file mode 100644
index c45c5386eda4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.support.v4.util.ArraySet;
-import android.util.Log;
-import android.util.Pools;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Stack;
-
-/**
- * A implementation of HeadsUpManager for phone and car.
- */
-public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
- ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
- OnHeadsUpChangedListener {
- private static final String TAG = "HeadsUpManagerPhone";
- private static final boolean DEBUG = false;
-
- private final View mStatusBarWindowView;
- private final int mStatusBarHeight;
- private final NotificationGroupManager mGroupManager;
- private final StatusBar mBar;
- private final VisualStabilityManager mVisualStabilityManager;
-
- private boolean mReleaseOnExpandFinish;
- private boolean mTrackingHeadsUp;
- private HashSet<String> mSwipedOutKeys = new HashSet<>();
- private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
- private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
- = new ArraySet<>();
- private boolean mIsExpanded;
- private int[] mTmpTwoArray = new int[2];
- private boolean mHeadsUpGoingAway;
- private boolean mWaitingOnCollapseWhenGoingAway;
- private boolean mIsObserving;
- private int mStatusBarState;
-
- private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
- private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
-
- @Override
- public HeadsUpEntryPhone acquire() {
- if (!mPoolObjects.isEmpty()) {
- return mPoolObjects.pop();
- }
- return new HeadsUpEntryPhone();
- }
-
- @Override
- public boolean release(@NonNull HeadsUpEntryPhone instance) {
- instance.reset();
- mPoolObjects.push(instance);
- return true;
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Constructor:
-
- public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView,
- @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar,
- @NonNull VisualStabilityManager visualStabilityManager) {
- super(context);
-
- mStatusBarWindowView = statusBarWindowView;
- mGroupManager = groupManager;
- mBar = bar;
- mVisualStabilityManager = visualStabilityManager;
-
- Resources resources = mContext.getResources();
- mStatusBarHeight = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
-
- addListener(new OnHeadsUpChangedListener() {
- @Override
- public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
- if (DEBUG) Log.w(TAG, "onHeadsUpPinnedModeChanged");
- updateTouchableRegionListener();
- }
- });
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Public methods:
-
- /**
- * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
- * that a user might have consciously clicked on it.
- *
- * @param key the key of the touched notification
- * @return whether the touch is invalid and should be discarded
- */
- public boolean shouldSwallowClick(@NonNull String key) {
- HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
- if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
- return true;
- }
- return false;
- }
-
- public void onExpandingFinished() {
- if (mReleaseOnExpandFinish) {
- releaseAllImmediately();
- mReleaseOnExpandFinish = false;
- } else {
- for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- }
- mEntriesToRemoveAfterExpand.clear();
- }
-
- /**
- * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
- * from the list even after a Heads Up Notification is gone.
- */
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
- }
-
- /**
- * Notify that the status bar panel gets expanded or collapsed.
- *
- * @param isExpanded True to notify expanded, false to notify collapsed.
- */
- public void setIsPanelExpanded(boolean isExpanded) {
- if (isExpanded != mIsExpanded) {
- mIsExpanded = isExpanded;
- if (isExpanded) {
- // make sure our state is sane
- mWaitingOnCollapseWhenGoingAway = false;
- mHeadsUpGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- }
-
- /**
- * Set the current state of the statusbar.
- */
- public void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
- }
-
- /**
- * Set that we are exiting the headsUp pinned mode, but some notifications might still be
- * animating out. This is used to keep the touchable regions in a sane state.
- */
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
- if (headsUpGoingAway != mHeadsUpGoingAway) {
- mHeadsUpGoingAway = headsUpGoingAway;
- if (!headsUpGoingAway) {
- waitForStatusBarLayout();
- }
- updateTouchableRegionListener();
- }
- }
-
- /**
- * Notifies that a remote input textbox in notification gets active or inactive.
- * @param entry The entry of the target notification.
- * @param remoteInputActive True to notify active, False to notify inactive.
- */
- public void setRemoteInputActive(
- @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
- HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
- if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
- headsUpEntry.remoteInputActive = remoteInputActive;
- if (remoteInputActive) {
- headsUpEntry.removeAutoRemovalCallbacks();
- } else {
- headsUpEntry.updateEntry(false /* updatePostTime */);
- }
- }
- }
-
- @VisibleForTesting
- public void removeMinimumDisplayTimeForTesting() {
- mMinimumDisplayTime = 1;
- mHeadsUpNotificationDecay = 1;
- mTouchAcceptanceDelay = 1;
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpManager public methods overrides:
-
- @Override
- public boolean isTrackingHeadsUp() {
- return mTrackingHeadsUp;
- }
-
- @Override
- public void snooze() {
- super.snooze();
- mReleaseOnExpandFinish = true;
- }
-
- /**
- * React to the removal of the notification in the heads up.
- *
- * @return true if the notification was removed and false if it still needs to be kept around
- * for a bit since it wasn't shown long enough
- */
- @Override
- public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
- if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
- return super.removeNotification(key, ignoreEarliestRemovalTime);
- } else {
- HeadsUpEntryPhone entry = getHeadsUpEntryPhone(key);
- entry.removeAsSoonAsPossible();
- return false;
- }
- }
-
- public void addSwipedOutNotification(@NonNull String key) {
- mSwipedOutKeys.add(key);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Dumpable overrides:
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("HeadsUpManagerPhone state:");
- dumpInternal(fd, pw, args);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // ViewTreeObserver.OnComputeInternalInsetsListener overrides:
-
- /**
- * Overridden from TreeObserver.
- */
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsExpanded || mBar.isBouncerShowing()) {
- // The touchable region is always the full area when expanded
- return;
- }
- if (hasPinnedHeadsUp()) {
- ExpandableNotificationRow topEntry = getTopEntry().row;
- if (topEntry.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
- if (groupSummary != null) {
- topEntry = groupSummary;
- }
- }
- topEntry.getLocationOnScreen(mTmpTwoArray);
- int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int maxY = topEntry.getIntrinsicHeight();
-
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(minX, 0, maxX, maxY);
- } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // VisualStabilityManager.Callback overrides:
-
- @Override
- public void onReorderingAllowed() {
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
- for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- mEntriesToRemoveWhenReorderingAllowed.clear();
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpManager utility (protected) methods overrides:
-
- @Override
- protected HeadsUpEntry createHeadsUpEntry() {
- return mEntryPool.acquire();
- }
-
- @Override
- protected void releaseHeadsUpEntry(HeadsUpEntry entry) {
- mEntryPool.release((HeadsUpEntryPhone) entry);
- }
-
- @Override
- protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
- return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
- || super.shouldHeadsUpBecomePinned(entry);
- }
-
- @Override
- protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dumpInternal(fd, pw, args);
- pw.print(" mStatusBarState="); pw.println(mStatusBarState);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Protected utility methods:
-
- @Nullable
- protected HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
- return (HeadsUpEntryPhone) getHeadsUpEntry(key);
- }
-
- @Nullable
- protected HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
- return (HeadsUpEntryPhone) getTopHeadsUpEntry();
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Private utility methods:
-
- private boolean wasShownLongEnough(@NonNull String key) {
- if (mSwipedOutKeys.contains(key)) {
- // We always instantly dismiss views being manually swiped out.
- mSwipedOutKeys.remove(key);
- return true;
- }
-
- HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
- HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
- if (headsUpEntry != topEntry) {
- return true;
- }
- return headsUpEntry.wasShownLongEnough();
- }
-
- /**
- * We need to wait on the whole panel to collapse, before we can remove the touchable region
- * listener.
- */
- private void waitForStatusBarLayout() {
- mWaitingOnCollapseWhenGoingAway = true;
- mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
- mStatusBarWindowView.removeOnLayoutChangeListener(this);
- mWaitingOnCollapseWhenGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- });
- }
-
- private void updateTouchableRegionListener() {
- boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
- || mWaitingOnCollapseWhenGoingAway;
- if (shouldObserve == mIsObserving) {
- return;
- }
- if (shouldObserve) {
- mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- mStatusBarWindowView.requestLayout();
- } else {
- mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
- mIsObserving = shouldObserve;
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpEntryPhone:
-
- protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
- public void setEntry(@NonNull final NotificationData.Entry entry) {
- Runnable removeHeadsUpRunnable = () -> {
- if (!mVisualStabilityManager.isReorderingAllowed()) {
- mEntriesToRemoveWhenReorderingAllowed.add(entry);
- mVisualStabilityManager.addReorderingAllowedCallback(
- HeadsUpManagerPhone.this);
- } else if (!mTrackingHeadsUp) {
- removeHeadsUpEntry(entry);
- } else {
- mEntriesToRemoveAfterExpand.add(entry);
- }
- };
-
- super.setEntry(entry, removeHeadsUpRunnable);
- }
-
- public boolean wasShownLongEnough() {
- return earliestRemovaltime < mClock.currentTimeMillis();
- }
-
- @Override
- public void updateEntry(boolean updatePostTime) {
- super.updateEntry(updatePostTime);
-
- if (mEntriesToRemoveAfterExpand.contains(entry)) {
- mEntriesToRemoveAfterExpand.remove(entry);
- }
- if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
- mEntriesToRemoveWhenReorderingAllowed.remove(entry);
- }
- }
-
- @Override
- public void expanded(boolean expanded) {
- if (this.expanded == expanded) {
- return;
- }
-
- this.expanded = expanded;
- if (expanded) {
- removeAutoRemovalCallbacks();
- } else {
- updateEntry(false /* updatePostTime */);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 2bfdefe39017..c85571c1895d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,7 @@ import android.view.ViewConfiguration;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
/**
@@ -31,7 +31,7 @@ import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
*/
public class HeadsUpTouchHelper implements Gefingerpoken {
- private HeadsUpManagerPhone mHeadsUpManager;
+ private HeadsUpManager mHeadsUpManager;
private NotificationStackScrollLayout mStackScroller;
private int mTrackingPointer;
private float mTouchSlop;
@@ -43,7 +43,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
private NotificationPanelView mPanel;
private ExpandableNotificationRow mPickedChild;
- public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
+ public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
NotificationStackScrollLayout stackScroller,
NotificationPanelView notificationPanelView) {
mHeadsUpManager = headsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 65c45a3120c1..1239a9ea0240 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -22,11 +22,13 @@ import static android.app.StatusBarManager.windowStateToString;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
+import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.annotation.IdRes;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -69,6 +71,7 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+import android.widget.Button;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -153,6 +156,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
private Animator mRotateShowAnimator;
private Animator mRotateHideAnimator;
+ private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ mNavigationBarView.onOverviewProxyConnectionChanged(isConnected);
+ updateScreenPinningGestures();
+ }
+
+ @Override
+ public void onRecentsAnimationStarted() {
+ mNavigationBarView.setRecentsAnimationStarted(true);
+ }
+ };
// ----- Fragment Lifecycle Callbacks -----
@@ -239,12 +254,14 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
filter.addAction(Intent.ACTION_SCREEN_ON);
getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
notifyNavigationBarScreenOn();
+ mOverviewProxyService.addCallback(mOverviewProxyListener);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mNavigationBarView.getLightTransitionsController().destroy(getContext());
+ mOverviewProxyService.removeCallback(mOverviewProxyListener);
getContext().unregisterReceiver(mBroadcastReceiver);
}
@@ -514,6 +531,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
if (masked != mDisabledFlags1) {
mDisabledFlags1 = masked;
if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
+ updateScreenPinningGestures();
}
}
@@ -528,7 +546,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
private boolean shouldDisableNavbarGestures() {
return !mStatusBar.isDeviceProvisioned()
|| (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0
- || mOverviewProxyService.getProxy() != null;
+ || mNavigationBarView.getRecentsButton().getVisibility() != View.VISIBLE;
}
private void repositionNavigationBar() {
@@ -540,6 +558,24 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
((View) mNavigationBarView.getParent()).getLayoutParams());
}
+ private void updateScreenPinningGestures() {
+ if (mNavigationBarView == null) {
+ return;
+ }
+
+ // Change the cancel pin gesture to home and back if recents button is invisible
+ boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
+ ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
+ ButtonDispatcher backButton = mNavigationBarView.getBackButton();
+ if (recentsVisible) {
+ homeButton.setOnLongClickListener(this::onHomeLongClick);
+ backButton.setOnLongClickListener(this::onLongPressBackRecents);
+ } else {
+ homeButton.setOnLongClickListener(this::onLongPressBackHome);
+ backButton.setOnLongClickListener(this::onLongPressBackHome);
+ }
+ }
+
private void notifyNavigationBarScreenOn() {
mNavigationBarView.notifyScreenOn();
}
@@ -555,11 +591,9 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
ButtonDispatcher backButton = mNavigationBarView.getBackButton();
backButton.setLongClickable(true);
- backButton.setOnLongClickListener(this::onLongPressBackRecents);
ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
- homeButton.setOnLongClickListener(this::onHomeLongClick);
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
@@ -569,6 +603,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
+ updateScreenPinningGestures();
}
private boolean onHomeTouch(View v, MotionEvent event) {
@@ -649,20 +684,29 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
mCommandQueue.toggleRecentApps();
}
+ private boolean onLongPressBackHome(View v) {
+ return onLongPressNavigationButtons(v, R.id.back, R.id.home);
+ }
+
+ private boolean onLongPressBackRecents(View v) {
+ return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
+ }
+
/**
- * This handles long-press of both back and recents. They are
- * handled together to capture them both being long-pressed
+ * This handles long-press of both back and recents/home. Back is the common button with
+ * combination of recents if it is visible or home if recents is invisible.
+ * They are handled together to capture them both being long-pressed
* at the same time to exit screen pinning (lock task).
*
- * When accessibility mode is on, only a long-press from recents
+ * When accessibility mode is on, only a long-press from recents/home
* is required to exit.
*
* In all other circumstances we try to pass through long-press events
* for Back, so that apps can still use it. Which can be from two things.
* 1) Not currently in screen pinning (lock task).
- * 2) Back is long-pressed without recents.
+ * 2) Back is long-pressed without recents/home.
*/
- private boolean onLongPressBackRecents(View v) {
+ private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
try {
boolean sendBackLongPress = false;
IActivityManager activityManager = ActivityManagerNative.getDefault();
@@ -670,6 +714,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
boolean inLockTaskMode = activityManager.isInLockTaskMode();
if (inLockTaskMode && !touchExplorationEnabled) {
long time = System.currentTimeMillis();
+
// If we recently long-pressed the other button then they were
// long-pressed 'together'
if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
@@ -677,26 +722,32 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
// When exiting refresh disabled flags.
mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
return true;
- } else if ((v.getId() == R.id.back)
- && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) {
- // If we aren't pressing recents right now then they presses
- // won't be together, so send the standard long-press action.
- sendBackLongPress = true;
+ } else if (v.getId() == btnId1) {
+ ButtonDispatcher button = btnId2 == R.id.recent_apps
+ ? mNavigationBarView.getRecentsButton()
+ : mNavigationBarView.getHomeButton();
+ if (!button.getCurrentView().isPressed()) {
+ // If we aren't pressing recents/home right now then they presses
+ // won't be together, so send the standard long-press action.
+ sendBackLongPress = true;
+ }
}
mLastLockToAppLongPress = time;
} else {
// If this is back still need to handle sending the long-press event.
- if (v.getId() == R.id.back) {
+ if (v.getId() == btnId1) {
sendBackLongPress = true;
} else if (touchExplorationEnabled && inLockTaskMode) {
- // When in accessibility mode a long press that is recents (not back)
+ // When in accessibility mode a long press that is recents/home (not back)
// should stop lock task.
activityManager.stopSystemLockTaskMode();
// When exiting refresh disabled flags.
mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
return true;
- } else if (v.getId() == R.id.recent_apps) {
- return onLongPressRecents();
+ } else if (v.getId() == btnId2) {
+ return btnId2 == R.id.recent_apps
+ ? onLongPressRecents()
+ : onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView());
}
}
if (sendBackLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index de6ecac5b00e..c37dd55cc6ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -207,23 +207,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
}
- private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
- @Override
- public void onConnectionChanged(boolean isConnected) {
- updateSlippery();
- setDisabledFlags(mDisabledFlags, true);
- setUpSwipeUpOnboarding(isConnected);
- }
-
- @Override
- public void onRecentsAnimationStarted() {
- mRecentsAnimationStarted = true;
- if (mSwipeUpOnboarding != null) {
- mSwipeUpOnboarding.onRecentsAnimationStarted();
- }
- }
- };
-
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -280,6 +263,19 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
notifyVerticalChangedListener(mVertical);
}
+ public void setRecentsAnimationStarted(boolean started) {
+ mRecentsAnimationStarted = started;
+ if (mSwipeUpOnboarding != null) {
+ mSwipeUpOnboarding.onRecentsAnimationStarted();
+ }
+ }
+
+ public void onConnectionChanged(boolean isConnected) {
+ updateSlippery();
+ setDisabledFlags(mDisabledFlags, true);
+ setUpSwipeUpOnboarding(isConnected);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureHelper.onTouchEvent(event)) {
@@ -304,7 +300,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
}
}
- return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
+ return mGestureHelper.onInterceptTouchEvent(event) || mRecentsAnimationStarted;
}
public void abortCurrentGesture() {
@@ -353,6 +349,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
return mButtonDispatchers;
}
+ public boolean isRecentsButtonVisible() {
+ return getRecentsButton().getVisibility() == View.VISIBLE;
+ }
+
private void updateCarModeIcons(Context ctx) {
mBackCarModeIcon = getDrawable(ctx,
R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
@@ -613,6 +613,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
final ViewGroup navbarView = ((ViewGroup) getParent());
final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView
.getLayoutParams();
+ if (lp == null) {
+ return;
+ }
if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) {
lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
changed = true;
@@ -676,6 +679,12 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
}
+ public void onOverviewProxyConnectionChanged(boolean isConnected) {
+ setSlippery(!isConnected);
+ setDisabledFlags(mDisabledFlags, true);
+ setUpSwipeUpOnboarding(isConnected);
+ }
+
@Override
protected void onDraw(Canvas canvas) {
mGestureHelper.onDraw(canvas);
@@ -873,7 +882,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
onPluginDisconnected(null); // Create default gesture helper
Dependency.get(PluginManager.class).addPluginListener(this,
NavGesture.class, false /* Only one */);
- mOverviewProxyService.addCallback(mOverviewProxyListener);
setUpSwipeUpOnboarding(mOverviewProxyService.getProxy() != null);
}
@@ -884,7 +892,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
- mOverviewProxyService.removeCallback(mOverviewProxyListener);
setUpSwipeUpOnboarding(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 91cae0af6b13..5cf4c4c70974 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -120,7 +120,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private boolean mDisallowNextAnimation;
private boolean mAnimationsEnabled = true;
private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
- private int mDarkOffsetX;
// Keep track of the last visible icon so collapsed container can report on its location
private IconState mLastVisibleIconState;
@@ -378,14 +377,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth();
}
}
-
- if (mDark && mDarkOffsetX != 0) {
- for (int i = 0; i < childCount; i++) {
- View view = getChildAt(i);
- IconState iconState = mIconStates.get(view);
- iconState.xTranslation += mDarkOffsetX;
- }
- }
}
private float getLayoutEnd() {
@@ -534,10 +525,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mAnimationsEnabled = enabled;
}
- public void setDarkOffsetX(int offsetX) {
- mDarkOffsetX = offsetX;
- }
-
public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) {
mReplacingIcons = replacingIcons;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 2111d2ef5d87..cd2e77ae2591 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -47,6 +47,7 @@ import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardStatusView;
@@ -67,15 +68,14 @@ import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
-import java.util.Collection;
import java.util.List;
+import java.util.Collection;
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener,
@@ -482,7 +482,7 @@ public class NotificationPanelView extends PanelView implements
mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
- mNotificationStackScroller.setDarkShelfOffsetX(mClockPositionResult.clockX);
+ mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
requestScrollerTopPaddingUpdate(animate);
}
@@ -1571,7 +1571,7 @@ public class NotificationPanelView extends PanelView implements
private void updatePanelExpanded() {
boolean isExpanded = !isFullyCollapsed();
if (mPanelExpanded != isExpanded) {
- mHeadsUpManager.setIsPanelExpanded(isExpanded);
+ mHeadsUpManager.setIsExpanded(isExpanded);
mStatusBar.setPanelExpanded(isExpanded);
mPanelExpanded = isExpanded;
}
@@ -2338,7 +2338,7 @@ public class NotificationPanelView extends PanelView implements
}
@Override
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
super.setHeadsUpManager(headsUpManager);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
this);
@@ -2630,8 +2630,8 @@ public class NotificationPanelView extends PanelView implements
}
}
- public void setPulsing(boolean pulsing) {
- mKeyguardStatusView.setPulsing(pulsing);
+ public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+ mKeyguardStatusView.setPulsing(pulsing != null);
positionClockAndNotifications();
mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
+ mKeyguardStatusView.getClockBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6daabede7f32..2b7e4747a837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -50,7 +50,7 @@ import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -75,7 +75,7 @@ public abstract class PanelView extends FrameLayout {
}
protected StatusBar mStatusBar;
- protected HeadsUpManagerPhone mHeadsUpManager;
+ protected HeadsUpManager mHeadsUpManager;
private float mPeekHeight;
private float mHintDistance;
@@ -1252,7 +1252,7 @@ public abstract class PanelView extends FrameLayout {
*/
protected abstract int getClearAllHeight();
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
}
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
index 1dcb0addc18b..0d07ad9cdf2e 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.am;
+package com.android.systemui.statusbar.phone;
import android.content.Context;
import android.os.SystemClock;
@@ -22,36 +22,37 @@ import android.util.Slog;
import android.view.WindowManager;
import android.widget.Toast;
-import com.android.internal.R;
+import com.android.systemui.R;
+import com.android.systemui.SysUIToast;
/**
* Helper to manage showing/hiding a image to notify them that they are entering or exiting screen
* pinning mode. All exposed methods should be called from a handler thread.
*/
-public class LockTaskNotify {
- private static final String TAG = "LockTaskNotify";
+public class ScreenPinningNotify {
+ private static final String TAG = "ScreenPinningNotify";
private static final long SHOW_TOAST_MINIMUM_INTERVAL = 1000;
private final Context mContext;
private Toast mLastToast;
private long mLastShowToastTime;
- public LockTaskNotify(Context context) {
+ public ScreenPinningNotify(Context context) {
mContext = context;
}
/** Show "Screen pinned" toast. */
void showPinningStartToast() {
- makeAllUserToastAndShow(R.string.lock_to_app_start);
+ makeAllUserToastAndShow(R.string.screen_pinning_start);
}
/** Show "Screen unpinned" toast. */
void showPinningExitToast() {
- makeAllUserToastAndShow(R.string.lock_to_app_exit);
+ makeAllUserToastAndShow(R.string.screen_pinning_exit);
}
/** Show a toast that describes the gesture the user should use to escape pinned mode. */
- void showEscapeToast() {
+ void showEscapeToast(boolean isRecentsButtonVisible) {
long showToastTime = SystemClock.elapsedRealtime();
if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -60,14 +61,14 @@ public class LockTaskNotify {
if (mLastToast != null) {
mLastToast.cancel();
}
- mLastToast = makeAllUserToastAndShow(R.string.lock_to_app_toast);
+ mLastToast = makeAllUserToastAndShow(isRecentsButtonVisible
+ ? R.string.screen_pinning_toast
+ : R.string.screen_pinning_toast_recents_invisible);
mLastShowToastTime = showToastTime;
}
private Toast makeAllUserToastAndShow(int resId) {
- Toast toast = Toast.makeText(mContext, resId, Toast.LENGTH_LONG);
- toast.getWindowParams().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ Toast toast = SysUIToast.makeText(mContext, resId, Toast.LENGTH_LONG);
toast.show();
return toast;
}
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 116e3f93bf8f..1bf719ae68af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -208,7 +208,6 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -220,7 +219,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -406,6 +404,13 @@ public class StatusBar extends SystemUI implements DemoMode,
protected NotificationEntryManager mEntryManager;
protected NotificationViewHierarchyManager mViewHierarchyManager;
+ /**
+ * Helper that is responsible for showing the right toast when a disallowed activity operation
+ * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
+ * fully locked mode we only show that unlocking is blocked.
+ */
+ private ScreenPinningNotify mScreenPinningNotify;
+
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -804,14 +809,15 @@ public class StatusBar extends SystemUI implements DemoMode,
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
- mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
- mVisualStabilityManager);
+ mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
+ mHeadsUpManager.setBar(this);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
@@ -831,7 +837,7 @@ public class StatusBar extends SystemUI implements DemoMode,
} catch (RemoteException ex) {
// no window manager? good luck with that
}
-
+ mScreenPinningNotify = new ScreenPinningNotify(mContext);
mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
@@ -1342,8 +1348,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
- if (mStackScroller.hasPulsingNotifications() &&
- !mHeadsUpManager.hasHeadsUpNotifications()) {
+ if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -2092,8 +2097,9 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public void maybeEscalateHeadsUp() {
- mHeadsUpManager.getAllEntries().forEach(entry -> {
- final StatusBarNotification sbn = entry.notification;
+ Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
+ for (HeadsUpManager.HeadsUpEntry entry : entries) {
+ final StatusBarNotification sbn = entry.entry.notification;
final Notification notification = sbn.getNotification();
if (notification.fullScreenIntent != null) {
if (DEBUG) {
@@ -2103,11 +2109,11 @@ public class StatusBar extends SystemUI implements DemoMode,
EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
sbn.getKey());
notification.fullScreenIntent.send();
- entry.notifyFullScreenIntentLaunched();
+ entry.entry.notifyFullScreenIntentLaunched();
} catch (PendingIntent.CanceledException e) {
}
}
- });
+ }
mHeadsUpManager.releaseAllImmediately();
}
@@ -2142,6 +2148,21 @@ public class StatusBar extends SystemUI implements DemoMode,
}
+ @Override
+ public void showPinningEnterExitToast(boolean entering) {
+ if (entering) {
+ mScreenPinningNotify.showPinningStartToast();
+ } else {
+ mScreenPinningNotify.showPinningExitToast();
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ mScreenPinningNotify.showEscapeToast(getNavigationBarView() == null
+ || getNavigationBarView().isRecentsButtonVisible());
+ }
+
boolean panelsEnabled() {
return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0
&& (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
@@ -4637,22 +4658,24 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- if (mHeadsUpManager.hasHeadsUpNotifications()) {
+ Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
+ mHeadsUpManager.getAllEntries();
+ if (!pulsingEntries.isEmpty()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
- setPulsing(true);
+ setPulsing(pulsingEntries);
}
}
@Override
public void onPulseFinished() {
callback.onPulseFinished();
- setPulsing(false);
+ setPulsing(null);
}
- private void setPulsing(boolean pulsing) {
+ private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
mNotificationPanel.setPulsing(pulsing);
- mVisualStabilityManager.setPulsing(pulsing);
+ mVisualStabilityManager.setPulsing(pulsing != null);
mIgnoreTouchWhilePulsing = false;
}
}, reason);
@@ -4800,7 +4823,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// for heads up notifications
- protected HeadsUpManagerPhone mHeadsUpManager;
+ protected HeadsUpManager mHeadsUpManager;
private AboveShelfObserver mAboveShelfObserver;
@@ -4903,7 +4926,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// Release the HUN notification to the shade.
if (isPresenterFullyCollapsed()) {
- HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
+ HeadsUpManager.setIsClickedNotification(row, true);
}
//
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index a2b896dc015b..53dfb244c776 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,68 +16,118 @@
package com.android.systemui.statusbar.policy;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.os.SystemClock;
import android.os.Handler;
import android.os.Looper;
-import android.util.ArrayMap;
+import android.os.SystemClock;
import android.provider.Settings;
+import android.support.v4.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Iterator;
-import java.util.stream.Stream;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Stack;
/**
* A manager which handles heads up notifications which is a special mode where
* they simply peek from the top of the screen.
*/
-public class HeadsUpManager {
+public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener,
+ VisualStabilityManager.Callback {
private static final String TAG = "HeadsUpManager";
private static final boolean DEBUG = false;
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
+ private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
- protected final Clock mClock = new Clock();
- protected final Context mContext;
- protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
- protected final Handler mHandler = new Handler(Looper.getMainLooper());
-
- protected int mHeadsUpNotificationDecay;
- protected int mMinimumDisplayTime;
- protected int mTouchAcceptanceDelay;
- protected int mSnoozeLengthMs;
- protected boolean mHasPinnedNotification;
- protected int mUser;
+ private final int mHeadsUpNotificationDecay;
+ private final int mMinimumDisplayTime;
- private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
+ private final int mTouchAcceptanceDelay;
private final ArrayMap<String, Long> mSnoozedPackages;
- private final ContentObserver mSettingsObserver;
+ private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+ private final int mDefaultSnoozeLengthMs;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
+
+ private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
- public HeadsUpManager(@NonNull final Context context) {
+ @Override
+ public HeadsUpEntry acquire() {
+ if (!mPoolObjects.isEmpty()) {
+ return mPoolObjects.pop();
+ }
+ return new HeadsUpEntry();
+ }
+
+ @Override
+ public boolean release(HeadsUpEntry instance) {
+ instance.reset();
+ mPoolObjects.push(instance);
+ return true;
+ }
+ };
+
+ private final View mStatusBarWindowView;
+ private final int mStatusBarHeight;
+ private final Context mContext;
+ private final NotificationGroupManager mGroupManager;
+ private StatusBar mBar;
+ private int mSnoozeLengthMs;
+ private ContentObserver mSettingsObserver;
+ private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
+ private HashSet<String> mSwipedOutKeys = new HashSet<>();
+ private int mUser;
+ private Clock mClock;
+ private boolean mReleaseOnExpandFinish;
+ private boolean mTrackingHeadsUp;
+ private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+ = new ArraySet<>();
+ private boolean mIsExpanded;
+ private boolean mHasPinnedNotification;
+ private int[] mTmpTwoArray = new int[2];
+ private boolean mHeadsUpGoingAway;
+ private boolean mWaitingOnCollapseWhenGoingAway;
+ private boolean mIsObserving;
+ private boolean mRemoteInputActive;
+ private float mExpandedHeight;
+ private VisualStabilityManager mVisualStabilityManager;
+ private int mStatusBarState;
+
+ public HeadsUpManager(final Context context, View statusBarWindowView,
+ NotificationGroupManager groupManager) {
mContext = context;
- Resources resources = context.getResources();
- mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
- mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+ Resources resources = mContext.getResources();
mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
mSnoozedPackages = new ArrayMap<>();
- int defaultSnoozeLengthMs =
- resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+ mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+ mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+ mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
+ mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+ mClock = new Clock();
mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
- SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
+ SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -92,26 +142,47 @@ public class HeadsUpManager {
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
mSettingsObserver);
+ mStatusBarWindowView = statusBarWindowView;
+ mGroupManager = groupManager;
+ mStatusBarHeight = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
}
- /**
- * Adds an OnHeadUpChangedListener to observe events.
- */
- public void addListener(@NonNull OnHeadsUpChangedListener listener) {
+ private void updateTouchableRegionListener() {
+ boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
+ || mWaitingOnCollapseWhenGoingAway;
+ if (shouldObserve == mIsObserving) {
+ return;
+ }
+ if (shouldObserve) {
+ mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ mStatusBarWindowView.requestLayout();
+ } else {
+ mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+ mIsObserving = shouldObserve;
+ }
+
+ public void setBar(StatusBar bar) {
+ mBar = bar;
+ }
+
+ public void addListener(OnHeadsUpChangedListener listener) {
mListeners.add(listener);
}
- /**
- * Removes the OnHeadUpChangedListener from the observer list.
- */
- public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
+ public void removeListener(OnHeadsUpChangedListener listener) {
mListeners.remove(listener);
}
+ public StatusBar getBar() {
+ return mBar;
+ }
+
/**
* Called when posting a new notification to the heads up.
*/
- public void showNotification(@NonNull NotificationData.Entry headsUp) {
+ public void showNotification(NotificationData.Entry headsUp) {
if (DEBUG) Log.v(TAG, "showNotification");
addHeadsUpEntry(headsUp);
updateNotification(headsUp, true);
@@ -121,7 +192,7 @@ public class HeadsUpManager {
/**
* Called when updating or posting a notification to the heads up.
*/
- public void updateNotification(@NonNull NotificationData.Entry headsUp, boolean alert) {
+ public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
if (DEBUG) Log.v(TAG, "updateNotification");
headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -133,13 +204,14 @@ public class HeadsUpManager {
// with the groupmanager
return;
}
- headsUpEntry.updateEntry(true /* updatePostTime */);
+ headsUpEntry.updateEntry();
setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
}
}
- private void addHeadsUpEntry(@NonNull NotificationData.Entry entry) {
- HeadsUpEntry headsUpEntry = createHeadsUpEntry();
+ private void addHeadsUpEntry(NotificationData.Entry entry) {
+ HeadsUpEntry headsUpEntry = mEntryPool.acquire();
+
// This will also add the entry to the sortedList
headsUpEntry.setEntry(entry);
mHeadsUpEntries.put(entry.key, headsUpEntry);
@@ -151,17 +223,16 @@ public class HeadsUpManager {
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
- protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
- return hasFullScreenIntent(entry);
+ private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+ return mStatusBarState != StatusBarState.KEYGUARD
+ && !mIsExpanded || hasFullScreenIntent(entry);
}
- protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
+ private boolean hasFullScreenIntent(NotificationData.Entry entry) {
return entry.notification.getNotification().fullScreenIntent != null;
}
- protected void setEntryPinned(
- @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
- if (DEBUG) Log.v(TAG, "setEntryPinned: " + isPinned);
+ private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
ExpandableNotificationRow row = headsUpEntry.entry.row;
if (row.isPinned() != isPinned) {
row.setPinned(isPinned);
@@ -176,35 +247,33 @@ public class HeadsUpManager {
}
}
- protected void removeHeadsUpEntry(NotificationData.Entry entry) {
+ private void removeHeadsUpEntry(NotificationData.Entry entry) {
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
- onHeadsUpEntryRemoved(remove);
- releaseHeadsUpEntry(remove);
- }
-
- protected void onHeadsUpEntryRemoved(HeadsUpEntry remove) {
- NotificationData.Entry entry = remove.entry;
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.row.setHeadsUp(false);
setEntryPinned(remove, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
+ mEntryPool.release(remove);
+ }
+
+ public void removeAllHeadsUpEntries() {
+ for (String key : mHeadsUpEntries.keySet()) {
+ removeHeadsUpEntry(mHeadsUpEntries.get(key).entry);
+ }
}
- protected void updatePinnedMode() {
+ private void updatePinnedMode() {
boolean hasPinnedNotification = hasPinnedNotificationInternal();
if (hasPinnedNotification == mHasPinnedNotification) {
return;
}
- if (DEBUG) {
- Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
- hasPinnedNotification);
- }
mHasPinnedNotification = hasPinnedNotification;
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
}
+ updateTouchableRegionListener();
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
}
@@ -216,36 +285,47 @@ public class HeadsUpManager {
* @return true if the notification was removed and false if it still needs to be kept around
* for a bit since it wasn't shown long enough
*/
- public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
- if (DEBUG) Log.v(TAG, "removeNotification");
- releaseImmediately(key);
- return true;
+ public boolean removeNotification(String key, boolean ignoreEarliestRemovalTime) {
+ if (DEBUG) Log.v(TAG, "remove");
+ if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
+ releaseImmediately(key);
+ return true;
+ } else {
+ getHeadsUpEntry(key).removeAsSoonAsPossible();
+ return false;
+ }
+ }
+
+ private boolean wasShownLongEnough(String key) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ HeadsUpEntry topEntry = getTopEntry();
+ if (mSwipedOutKeys.contains(key)) {
+ // We always instantly dismiss views being manually swiped out.
+ mSwipedOutKeys.remove(key);
+ return true;
+ }
+ if (headsUpEntry != topEntry) {
+ return true;
+ }
+ return headsUpEntry.wasShownLongEnough();
}
- /**
- * Returns if the given notification is in the Heads Up Notification list or not.
- */
public boolean isHeadsUp(String key) {
return mHeadsUpEntries.containsKey(key);
}
/**
- * Pushes any current Heads Up notification down into the shade.
+ * Push any current Heads Up notification down into the shade.
*/
public void releaseAllImmediately() {
if (DEBUG) Log.v(TAG, "releaseAllImmediately");
- Iterator<HeadsUpEntry> iterator = mHeadsUpEntries.values().iterator();
- while (iterator.hasNext()) {
- HeadsUpEntry entry = iterator.next();
- iterator.remove();
- onHeadsUpEntryRemoved(entry);
+ ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
+ for (String key : keys) {
+ releaseImmediately(key);
}
}
- /**
- * Pushes the given Heads Up notification down into the shade.
- */
- public void releaseImmediately(@NonNull String key) {
+ public void releaseImmediately(String key) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
if (headsUpEntry == null) {
return;
@@ -254,14 +334,11 @@ public class HeadsUpManager {
removeHeadsUpEntry(shadeEntry);
}
- /**
- * Returns if the given notification is snoozed or not.
- */
- public boolean isSnoozed(@NonNull String packageName) {
+ public boolean isSnoozed(String packageName) {
final String key = snoozeKey(packageName, mUser);
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
- if (snoozedUntil > mClock.currentTimeMillis()) {
+ if (snoozedUntil > SystemClock.elapsedRealtime()) {
if (DEBUG) Log.v(TAG, key + " snoozed");
return true;
}
@@ -270,61 +347,33 @@ public class HeadsUpManager {
return false;
}
- /**
- * Snoozes all current Heads Up Notifications.
- */
public void snooze() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
String packageName = entry.entry.notification.getPackageName();
mSnoozedPackages.put(snoozeKey(packageName, mUser),
- mClock.currentTimeMillis() + mSnoozeLengthMs);
+ SystemClock.elapsedRealtime() + mSnoozeLengthMs);
}
+ mReleaseOnExpandFinish = true;
}
- private static String snoozeKey(@NonNull String packageName, int user) {
+ private static String snoozeKey(String packageName, int user) {
return user + "," + packageName;
}
- protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
+ private HeadsUpEntry getHeadsUpEntry(String key) {
return mHeadsUpEntries.get(key);
}
- /**
- * Returns the entry of given Heads Up Notification.
- *
- * @param key Key of heads up notification
- */
- public NotificationData.Entry getEntry(@NonNull String key) {
- HeadsUpEntry entry = mHeadsUpEntries.get(key);
- return entry != null ? entry.entry : null;
- }
-
- /**
- * Returns the stream of all current Heads Up Notifications.
- */
- @NonNull
- public Stream<NotificationData.Entry> getAllEntries() {
- return mHeadsUpEntries.values().stream().map(headsUpEntry -> headsUpEntry.entry);
- }
-
- /**
- * Returns the top Heads Up Notification, which appeares to show at first.
- */
- @Nullable
- public NotificationData.Entry getTopEntry() {
- HeadsUpEntry topEntry = getTopHeadsUpEntry();
- return (topEntry != null) ? topEntry.entry : null;
+ public NotificationData.Entry getEntry(String key) {
+ return mHeadsUpEntries.get(key).entry;
}
- /**
- * Returns if any heads up notification is available or not.
- */
- public boolean hasHeadsUpNotifications() {
- return !mHeadsUpEntries.isEmpty();
+ public Collection<HeadsUpEntry> getAllEntries() {
+ return mHeadsUpEntries.values();
}
- protected HeadsUpEntry getTopHeadsUpEntry() {
+ public HeadsUpEntry getTopEntry() {
if (mHeadsUpEntries.isEmpty()) {
return null;
}
@@ -338,21 +387,56 @@ public class HeadsUpManager {
}
/**
- * Sets the current user.
+ * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
+ * that a user might have consciously clicked on it.
+ *
+ * @param key the key of the touched notification
+ * @return whether the touch is invalid and should be discarded
*/
+ public boolean shouldSwallowClick(String key) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
+ return true;
+ }
+ return false;
+ }
+
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ if (mIsExpanded || mBar.isBouncerShowing()) {
+ // The touchable region is always the full area when expanded
+ return;
+ }
+ if (mHasPinnedNotification) {
+ ExpandableNotificationRow topEntry = getTopEntry().entry.row;
+ if (topEntry.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ if (groupSummary != null) {
+ topEntry = groupSummary;
+ }
+ }
+ topEntry.getLocationOnScreen(mTmpTwoArray);
+ int minX = mTmpTwoArray[0];
+ int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+ int maxY = topEntry.getIntrinsicHeight();
+
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(minX, 0, maxX, maxY);
+ } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+ }
+ }
+
public void setUser(int user) {
mUser = user;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("HeadsUpManager state:");
- dumpInternal(fd, pw, args);
- }
-
- protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
- pw.print(" now="); pw.println(mClock.currentTimeMillis());
+ pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
pw.print(" mUser="); pw.println(mUser);
for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.entry);
@@ -365,9 +449,6 @@ public class HeadsUpManager {
}
}
- /**
- * Returns if there are any pinned Heads Up Notifications or not.
- */
public boolean hasPinnedHeadsUp() {
return mHasPinnedNotification;
}
@@ -383,8 +464,14 @@ public class HeadsUpManager {
}
/**
- * Unpins all pinned Heads Up Notifications.
+ * Notifies that a notification was swiped out and will be removed.
+ *
+ * @param key the notification key
*/
+ public void addSwipedOutNotification(String key) {
+ mSwipedOutKeys.add(key);
+ }
+
public void unpinAll() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
@@ -394,13 +481,60 @@ public class HeadsUpManager {
}
}
+ public void onExpandingFinished() {
+ if (mReleaseOnExpandFinish) {
+ releaseAllImmediately();
+ mReleaseOnExpandFinish = false;
+ } else {
+ for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
+ }
+ }
+ mEntriesToRemoveAfterExpand.clear();
+ }
+
+ public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+ mTrackingHeadsUp = trackingHeadsUp;
+ }
+
+ public boolean isTrackingHeadsUp() {
+ return mTrackingHeadsUp;
+ }
+
+ public void setIsExpanded(boolean isExpanded) {
+ if (isExpanded != mIsExpanded) {
+ mIsExpanded = isExpanded;
+ if (isExpanded) {
+ // make sure our state is sane
+ mWaitingOnCollapseWhenGoingAway = false;
+ mHeadsUpGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ }
+
/**
- * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
- * well.
+ * @return the height of the top heads up notification when pinned. This is different from the
+ * intrinsic height, which also includes whether the notification is system expanded and
+ * is mainly used when dragging down from a heads up notification.
*/
- public boolean isTrackingHeadsUp() {
- // Might be implemented in subclass.
- return false;
+ public int getTopHeadsUpPinnedHeight() {
+ HeadsUpEntry topEntry = getTopEntry();
+ if (topEntry == null || topEntry.entry == null) {
+ return 0;
+ }
+ ExpandableNotificationRow row = topEntry.entry.row;
+ if (row.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+ if (groupSummary != null) {
+ row = groupSummary;
+ }
+ }
+ return row.getPinnedHeadsUpHeight();
}
/**
@@ -419,67 +553,147 @@ public class HeadsUpManager {
}
/**
- * Sets an entry to be expanded and therefore stick in the heads up area if it's pinned
- * until it's collapsed again.
+ * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+ * animating out. This is used to keep the touchable regions in a sane state.
*/
+ public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ if (headsUpGoingAway != mHeadsUpGoingAway) {
+ mHeadsUpGoingAway = headsUpGoingAway;
+ if (!headsUpGoingAway) {
+ waitForStatusBarLayout();
+ }
+ updateTouchableRegionListener();
+ }
+ }
+
+ /**
+ * We need to wait on the whole panel to collapse, before we can remove the touchable region
+ * listener.
+ */
+ private void waitForStatusBarLayout() {
+ mWaitingOnCollapseWhenGoingAway = true;
+ mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
+ mStatusBarWindowView.removeOnLayoutChangeListener(this);
+ mWaitingOnCollapseWhenGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ });
+ }
+
+ public static void setIsClickedNotification(View child, boolean clicked) {
+ child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
+ }
+
+ public static boolean isClickedHeadsUpNotification(View child) {
+ Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION);
+ return clicked != null && clicked;
+ }
+
+ public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
+ HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+ headsUpEntry.remoteInputActive = remoteInputActive;
+ if (remoteInputActive) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
/**
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned
* until it's collapsed again.
*/
- public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
- HeadsUpManager.HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
- if (headsUpEntry != null && entry.row.isPinned()) {
- headsUpEntry.expanded(expanded);
+ public void setExpanded(NotificationData.Entry entry, boolean expanded) {
+ HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && headsUpEntry.expanded != expanded && entry.row.isPinned()) {
+ headsUpEntry.expanded = expanded;
+ if (expanded) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
+
+ @Override
+ public void onReorderingAllowed() {
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
+ for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
}
+ mEntriesToRemoveWhenReorderingAllowed.clear();
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
}
- @NonNull
- protected HeadsUpEntry createHeadsUpEntry() {
- return new HeadsUpEntry();
+ public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) {
+ mVisualStabilityManager = visualStabilityManager;
}
- protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) {
- // Do nothing for HeadsUpEntry.
+ public void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
}
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
*/
- protected class HeadsUpEntry implements Comparable<HeadsUpEntry> {
- @Nullable public NotificationData.Entry entry;
+ public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+ public NotificationData.Entry entry;
public long postTime;
- public boolean remoteInputActive;
public long earliestRemovaltime;
- public boolean expanded;
-
private Runnable mRemoveHeadsUpRunnable;
+ public boolean remoteInputActive;
+ public boolean expanded;
- public void setEntry(@Nullable final NotificationData.Entry entry) {
- setEntry(entry, null);
- }
-
- public void setEntry(@Nullable final NotificationData.Entry entry,
- @Nullable Runnable removeHeadsUpRunnable) {
+ public void setEntry(final NotificationData.Entry entry) {
this.entry = entry;
- this.mRemoveHeadsUpRunnable = removeHeadsUpRunnable;
// The actual post time will be just after the heads-up really slided in
postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
- updateEntry(true /* updatePostTime */);
+ mRemoveHeadsUpRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!mVisualStabilityManager.isReorderingAllowed()) {
+ mEntriesToRemoveWhenReorderingAllowed.add(entry);
+ mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManager.this);
+ } else if (!mTrackingHeadsUp) {
+ removeHeadsUpEntry(entry);
+ } else {
+ mEntriesToRemoveAfterExpand.add(entry);
+ }
+ }
+ };
+ updateEntry();
}
- public void updateEntry(boolean updatePostTime) {
- if (DEBUG) Log.v(TAG, "updateEntry");
+ public void updateEntry() {
+ updateEntry(true);
+ }
+ public void updateEntry(boolean updatePostTime) {
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
if (updatePostTime) {
postTime = Math.max(postTime, currentTime);
}
removeAutoRemovalCallbacks();
-
+ if (mEntriesToRemoveAfterExpand.contains(entry)) {
+ mEntriesToRemoveAfterExpand.remove(entry);
+ }
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(entry);
+ }
if (!isSticky()) {
long finishTime = postTime + mHeadsUpNotificationDecay;
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
@@ -493,7 +707,7 @@ public class HeadsUpManager {
}
@Override
- public int compareTo(@NonNull HeadsUpEntry o) {
+ public int compareTo(HeadsUpEntry o) {
boolean isPinned = entry.row.isPinned();
boolean otherPinned = o.entry.row.isPinned();
if (isPinned && !otherPinned) {
@@ -520,29 +734,26 @@ public class HeadsUpManager {
: -1;
}
- public void expanded(boolean expanded) {
- this.expanded = expanded;
+ public void removeAutoRemovalCallbacks() {
+ mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
}
- public void reset() {
- entry = null;
- expanded = false;
- remoteInputActive = false;
- removeAutoRemovalCallbacks();
- mRemoveHeadsUpRunnable = null;
+ public boolean wasShownLongEnough() {
+ return earliestRemovaltime < mClock.currentTimeMillis();
}
- public void removeAutoRemovalCallbacks() {
- if (mRemoveHeadsUpRunnable != null)
- mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+ public void removeAsSoonAsPossible() {
+ removeAutoRemovalCallbacks();
+ mHandler.postDelayed(mRemoveHeadsUpRunnable,
+ earliestRemovaltime - mClock.currentTimeMillis());
}
- public void removeAsSoonAsPossible() {
- if (mRemoveHeadsUpRunnable != null) {
- removeAutoRemovalCallbacks();
- mHandler.postDelayed(mRemoveHeadsUpRunnable,
- earliestRemovaltime - mClock.currentTimeMillis());
- }
+ public void reset() {
+ removeAutoRemovalCallbacks();
+ entry = null;
+ mRemoveHeadsUpRunnable = null;
+ expanded = false;
+ remoteInputActive = false;
}
}
@@ -551,4 +762,5 @@ public class HeadsUpManager {
return SystemClock.elapsedRealtime();
}
}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
deleted file mode 100644
index 1e3c123cfbc6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.view.View;
-
-import com.android.systemui.R;
-
-/**
- * A class of utility static methods for heads up notifications.
- */
-public final class HeadsUpUtil {
- private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
-
- /**
- * Set the given view as clicked or not-clicked.
- * @param view The view to be set the flag to.
- * @param clicked True to set as clicked. False to not-clicked.
- */
- public static void setIsClickedHeadsUpNotification(View view, boolean clicked) {
- view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
- }
-
- /**
- * Check if the given view has the flag of "clicked notification"
- * @param view The view to be checked.
- * @return True if the view has clicked. False othrewise.
- */
- public static boolean isClickedHeadsUpNotification(View view) {
- Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
- return clicked != null && clicked;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index d7a810eca02e..424858a86e58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -64,7 +64,7 @@ public class AmbientState {
private boolean mPanelTracking;
private boolean mExpansionChanging;
private boolean mPanelFullWidth;
- private boolean mPulsing;
+ private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
@@ -315,18 +315,23 @@ public class AmbientState {
}
public boolean hasPulsingNotifications() {
- return mPulsing;
+ return mPulsing != null;
}
- public void setPulsing(boolean hasPulsing) {
+ public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) {
mPulsing = hasPulsing;
}
public boolean isPulsing(NotificationData.Entry entry) {
- if (!mPulsing || mHeadsUpManager == null) {
+ if (mPulsing == null) {
return false;
}
- return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+ for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
+ if (e.entry == entry) {
+ return true;
+ }
+ }
+ return false;
}
public boolean isPanelTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index b28e1a9cafef..c114a6f5a6d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -92,11 +92,10 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import android.support.v4.graphics.ColorUtils;
@@ -289,7 +288,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
- private HeadsUpManagerPhone mHeadsUpManager;
+ private HeadsUpManager mHeadsUpManager;
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
private boolean mForceNoOverlappingRendering;
@@ -359,7 +358,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
};
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- private boolean mPulsing;
+ private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
private boolean mDrawBackgroundAsSrc;
private boolean mFadingOut;
private boolean mParentNotFullyVisible;
@@ -403,6 +402,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private final int mSeparatorThickness;
private final Rect mTmpRect = new Rect();
private int mClockBottom;
+ private int mAntiBurnInOffsetX;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -690,7 +690,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private void updateAlgorithmHeightAndPadding() {
- if (mPulsing) {
+ if (mPulsing != null) {
mTopPadding = mClockBottom;
} else {
mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
@@ -920,27 +920,6 @@ public class NotificationStackScrollLayout extends ViewGroup
}
/**
- * @return the height of the top heads up notification when pinned. This is different from the
- * intrinsic height, which also includes whether the notification is system expanded and
- * is mainly used when dragging down from a heads up notification.
- */
- private int getTopHeadsUpPinnedHeight() {
- NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
- if (topEntry == null) {
- return 0;
- }
- ExpandableNotificationRow row = topEntry.row;
- if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null) {
- row = groupSummary;
- }
- }
- return row.getPinnedHeadsUpHeight();
- }
-
- /**
* @return the position from where the appear transition ends when expanding.
* Measured in absolute height.
*/
@@ -951,7 +930,7 @@ public class NotificationStackScrollLayout extends ViewGroup
int minNotificationsForShelf = 1;
if (mTrackingHeadsUp
|| (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
- appearPosition = getTopHeadsUpPinnedHeight();
+ appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
minNotificationsForShelf = 2;
} else {
appearPosition = 0;
@@ -1219,9 +1198,9 @@ public class NotificationStackScrollLayout extends ViewGroup
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().row != row
+ && mHeadsUpManager.getTopEntry().entry.row != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
+ mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
!= row) {
continue;
}
@@ -2141,7 +2120,7 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public boolean hasPulsingNotifications() {
- return mPulsing;
+ return mPulsing != null;
}
private void updateScrollability() {
@@ -2774,7 +2753,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private boolean isClickedHeadsUp(View child) {
- return HeadsUpUtil.isClickedHeadsUpNotification(child);
+ return HeadsUpManager.isClickedHeadsUpNotification(child);
}
/**
@@ -3889,9 +3868,14 @@ public class NotificationStackScrollLayout extends ViewGroup
applyCurrentBackgroundBounds();
updateWillNotDraw();
updateContentHeight();
+ updateAntiBurnInTranslation();
notifyHeightChangeListener(mShelf);
}
+ private void updateAntiBurnInTranslation() {
+ setTranslationX(mAmbientState.isDark() ? mAntiBurnInOffsetX : 0);
+ }
+
/**
* Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
* not {@link #onDraw(Canvas)} is called). This method should be called whenever the
@@ -4274,7 +4258,7 @@ public class NotificationStackScrollLayout extends ViewGroup
mAnimationFinishedRunnables.add(runnable);
}
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
}
@@ -4342,8 +4326,8 @@ public class NotificationStackScrollLayout extends ViewGroup
return mIsExpanded;
}
- public void setPulsing(boolean pulsing, int clockBottom) {
- if (!mPulsing && !pulsing) {
+ public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
+ if (mPulsing == null && pulsing == null) {
return;
}
mPulsing = pulsing;
@@ -4473,15 +4457,16 @@ public class NotificationStackScrollLayout extends ViewGroup
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
}
- public void setDarkShelfOffsetX(int shelfOffsetX) {
- mShelf.setDarkOffsetX(shelfOffsetX);
+ public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
+ mAntiBurnInOffsetX = antiBurnInOffsetX;
+ updateAntiBurnInTranslation();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d]",
this.getClass().getSimpleName(),
- mPulsing ? "T":"f",
+ mPulsing != null ?"T":"f",
mAmbientState.isQsCustomizerShowing() ? "T":"f",
getVisibility() == View.VISIBLE ? "visible"
: getVisibility() == View.GONE ? "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 04a7bd79c6ca..682b8493e913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -30,7 +30,7 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
/**
* A state of a view. This can be used to apply a set of view properties to a view with
@@ -582,7 +582,7 @@ public class ViewState {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
+ HeadsUpManager.setIsClickedNotification(child, false);
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
child.setTag(TAG_START_TRANSLATION_Y, null);
child.setTag(TAG_END_TRANSLATION_Y, null);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 001a582297af..a131a618512f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,6 +18,7 @@ package com.android.systemui.volume;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static android.media.AudioManager.STREAM_ACCESSIBILITY;
import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
@@ -36,7 +37,6 @@ import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -48,6 +48,7 @@ import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.support.v7.media.MediaRouter;
+import android.text.InputFilter;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -58,7 +59,6 @@ import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnClickListener;
-import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
@@ -105,6 +105,7 @@ public class VolumeDialogImpl implements VolumeDialog {
private CustomDialog mDialog;
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
+ private ViewGroup mFooter;
private ImageButton mRingerIcon;
private TextView mRingerStatus;
private final List<VolumeRow> mRows = new ArrayList<>();
@@ -202,8 +203,9 @@ public class VolumeDialogImpl implements VolumeDialog {
hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
- mRingerIcon = mDialog.findViewById(R.id.ringer_icon);
- mRingerStatus = mDialog.findViewById(R.id.ringer_status);
+ mFooter = mDialog.findViewById(R.id.footer);
+ mRingerIcon = mFooter.findViewById(R.id.ringer_icon);
+ mRingerStatus = mFooter.findViewById(R.id.ringer_status);
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
@@ -219,7 +221,7 @@ public class VolumeDialogImpl implements VolumeDialog {
R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false);
addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system,
R.drawable.ic_volume_system_mute, false, false);
- addRow(AudioManager.STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
+ addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
R.drawable.ic_volume_accessibility, true, false);
}
} else {
@@ -334,42 +336,17 @@ public class VolumeDialogImpl implements VolumeDialog {
row.view.setTag(row);
row.header = row.view.findViewById(R.id.volume_row_header);
row.header.setId(20 * row.stream);
+ if (stream == STREAM_ACCESSIBILITY) {
+ row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)});
+ }
row.slider = row.view.findViewById(R.id.volume_row_slider);
row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
row.anim = null;
row.outputChooser = row.view.findViewById(R.id.output_chooser);
row.outputChooser.setOnClickListener(mClickOutputChooser);
- row.outputChooser.findViewById(R.id.output_chooser_button)
- .setOnClickListener(mClickOutputChooser);
row.connectedDevice = row.view.findViewById(R.id.volume_row_connected_device);
- // forward events above the slider into the slider
- row.view.findViewById(R.id.volume_row_slider_frame)
- .setOnTouchListener(new OnTouchListener() {
- private final Rect mSliderHitRect = new Rect();
- private boolean mDragging;
-
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- row.slider.getHitRect(mSliderHitRect);
- if (!mDragging && event.getActionMasked() == MotionEvent.ACTION_DOWN
- && event.getY() < mSliderHitRect.top) {
- mDragging = true;
- }
- if (mDragging) {
- event.offsetLocation(-mSliderHitRect.left, -mSliderHitRect.top);
- row.slider.dispatchTouchEvent(event);
- if (event.getActionMasked() == MotionEvent.ACTION_UP
- || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
- mDragging = false;
- }
- return true;
- }
- return false;
- }
- });
row.icon = row.view.findViewById(R.id.volume_row_icon);
row.icon.setImageResource(iconRes);
if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
@@ -412,6 +389,8 @@ public class VolumeDialogImpl implements VolumeDialog {
if (ss == null) {
return;
}
+ // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have
+ // a vibrator.
final boolean hasVibrator = mController.hasVibrator();
if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
if (hasVibrator) {
@@ -419,7 +398,12 @@ public class VolumeDialogImpl implements VolumeDialog {
} else {
final boolean wasZero = ss.level == 0;
mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
+ mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
}
+ } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
+ final boolean wasZero = ss.level == 0;
+ mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
+ mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
} else {
mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
if (ss.level == 0) {
@@ -664,7 +648,7 @@ public class VolumeDialogImpl implements VolumeDialog {
if (ss.level == row.requestedLevel) {
row.requestedLevel = -1;
}
- final boolean isA11yStream = row.stream == AudioManager.STREAM_ACCESSIBILITY;
+ final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY;
final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
@@ -908,7 +892,6 @@ public class VolumeDialogImpl implements VolumeDialog {
private final OnClickListener mClickOutputChooser = new OnClickListener() {
@Override
public void onClick(View v) {
- // TODO: log
dismissH(DISMISS_REASON_OUTPUT_CHOOSER);
showOutputChooserH();
}
@@ -971,7 +954,7 @@ public class VolumeDialogImpl implements VolumeDialog {
public void onAccessibilityModeChanged(Boolean showA11yStream) {
mShowA11yStream = showA11yStream == null ? false : showA11yStream;
VolumeRow activeRow = getActiveRow();
- if (!mShowA11yStream && AudioManager.STREAM_ACCESSIBILITY == activeRow.stream) {
+ if (!mShowA11yStream && STREAM_ACCESSIBILITY == activeRow.stream) {
dismissH(Events.DISMISS_STREAM_GONE);
} else {
updateRowsH(activeRow);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index 368194e57b9d..3d4438148c39 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -14,26 +14,22 @@
package com.android.systemui.volume;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.Gravity;
+import android.view.DisplayCutout;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
import com.android.systemui.R;
import com.android.systemui.util.leak.RotationUtils;
@@ -46,6 +42,9 @@ public class VolumeUiLayout extends FrameLayout {
private AnimatorSet mAnimation;
private boolean mHasOutsideTouch;
private int mRotation = ROTATION_NONE;
+ @Nullable
+ private DisplayCutout mDisplayCutout;
+
public VolumeUiLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -60,6 +59,7 @@ public class VolumeUiLayout extends FrameLayout {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+ mDisplayCutout = null;
}
@Override
@@ -86,200 +86,64 @@ public class VolumeUiLayout extends FrameLayout {
updateRotation();
}
- private void updateRotation() {
- int rotation = RotationUtils.getRotation(getContext());
- if (rotation != mRotation) {
- rotate(mRotation, rotation);
- mRotation = rotation;
- }
- }
-
- private void rotate(View view, int from, int to, boolean swapDimens) {
- if (from != ROTATION_NONE && to != ROTATION_NONE) {
- // Rather than handling this confusing case, just do 2 rotations.
- rotate(view, from, ROTATION_NONE, swapDimens);
- rotate(view, ROTATION_NONE, to, swapDimens);
- return;
- }
- if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) {
- rotateRight(view);
- } else {
- rotateLeft(view);
- }
- if (to != ROTATION_NONE) {
- if (swapDimens && view instanceof LinearLayout) {
- LinearLayout linearLayout = (LinearLayout) view;
- linearLayout.setOrientation(LinearLayout.HORIZONTAL);
- swapDimens(view);
- }
- } else {
- if (swapDimens && view instanceof LinearLayout) {
- LinearLayout linearLayout = (LinearLayout) view;
- linearLayout.setOrientation(LinearLayout.VERTICAL);
- swapDimens(view);
+ private void setDisplayCutout() {
+ if (mDisplayCutout == null && getRootWindowInsets() != null) {
+ DisplayCutout cutout = getRootWindowInsets().getDisplayCutout();
+ if (cutout != null) {
+ mDisplayCutout = cutout;
}
}
}
- private void rotate(int from, int to) {
- View footer = mChild.findViewById(R.id.footer);
- rotate(footer, from, to, false);
- rotate(this, from, to, true);
- rotate(mChild, from, to, true);
- ViewGroup rows = mChild.findViewById(R.id.volume_dialog_rows);
- rotate(rows, from, to, true);
- swapOrientation((LinearLayout) rows);
- int rowCount = rows.getChildCount();
- for (int i = 0; i < rowCount; i++) {
- View row = rows.getChildAt(i);
- if (to == ROTATION_SEASCAPE) {
- rotateSeekBars(row, to, 180);
- } else if (to == ROTATION_LANDSCAPE) {
- rotateSeekBars(row, to, 0);
- } else {
- rotateSeekBars(row, to, 270);
- }
- rotate(row, from, to, true);
- }
- }
-
- private void swapOrientation(LinearLayout layout) {
- if(layout.getOrientation() == LinearLayout.HORIZONTAL) {
- layout.setOrientation(LinearLayout.VERTICAL);
- } else {
- layout.setOrientation(LinearLayout.HORIZONTAL);
- }
- }
-
- private void swapDimens(View v) {
- if (v == null) {
- return;
+ private void updateRotation() {
+ setDisplayCutout();
+ int rotation = RotationUtils.getRotation(getContext());
+ if (rotation != mRotation) {
+ updateSafeInsets(rotation);
+ mRotation = rotation;
}
- ViewGroup.LayoutParams params = v.getLayoutParams();
- int h = params.width;
- params.width = params.height;
- params.height = h;
- v.setLayoutParams(params);
}
- private void rotateSeekBars(View row, int to, int rotation) {
- SeekBar seekbar = row.findViewById(R.id.volume_row_slider);
- if (seekbar != null) {
- seekbar.setRotation((float) rotation);
- }
-
- View parent = row.findViewById(R.id.volume_row_slider_frame);
- swapDimens(parent);
- ViewGroup.LayoutParams params = seekbar.getLayoutParams();
- ViewGroup.LayoutParams parentParams = parent.getLayoutParams();
- if (to != ROTATION_NONE) {
- params.height = parentParams.height;
- params.width = parentParams.width;
- } else {
- params.height = parentParams.width;
- params.width = parentParams.height;
- }
- seekbar.setLayoutParams(params);
- }
+ private void updateSafeInsets(int rotation) {
+ // Depending on our rotation, we may have to work around letterboxing from the right
+ // side from the navigation bar or a cutout.
- private int rotateGravityRight(int gravity) {
- int retGravity = 0;
- int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
- final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ MarginLayoutParams lp = (MarginLayoutParams) mChild.getLayoutParams();
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- retGravity |= Gravity.CENTER_VERTICAL;
+ int margin = (int) getResources().getDimension(R.dimen.volume_dialog_base_margin);
+ switch (rotation) {
+ /*
+ * Landscape: <-|. Have to deal with the nav bar
+ * Seascape: |->. Have to deal with the cutout
+ */
+ case RotationUtils.ROTATION_LANDSCAPE:
+ margin += getNavBarHeight();
break;
- case Gravity.RIGHT:
- retGravity |= Gravity.BOTTOM;
+ case RotationUtils.ROTATION_SEASCAPE:
+ margin += getDisplayCutoutHeight();
break;
- case Gravity.LEFT:
default:
- retGravity |= Gravity.TOP;
break;
}
- switch (verticalGravity) {
- case Gravity.CENTER_VERTICAL:
- retGravity |= Gravity.CENTER_HORIZONTAL;
- break;
- case Gravity.BOTTOM:
- retGravity |= Gravity.LEFT;
- break;
- case Gravity.TOP:
- default:
- retGravity |= Gravity.RIGHT;
- break;
- }
- return retGravity;
+ lp.rightMargin = margin;
+ mChild.setLayoutParams(lp);
}
- private int rotateGravityLeft(int gravity) {
- if (gravity == -1) {
- gravity = Gravity.TOP | Gravity.START;
- }
- int retGravity = 0;
- int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
- final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- retGravity |= Gravity.CENTER_VERTICAL;
- break;
- case Gravity.RIGHT:
- retGravity |= Gravity.TOP;
- break;
- case Gravity.LEFT:
- default:
- retGravity |= Gravity.BOTTOM;
- break;
- }
-
- switch (verticalGravity) {
- case Gravity.CENTER_VERTICAL:
- retGravity |= Gravity.CENTER_HORIZONTAL;
- break;
- case Gravity.BOTTOM:
- retGravity |= Gravity.RIGHT;
- break;
- case Gravity.TOP:
- default:
- retGravity |= Gravity.LEFT;
- break;
- }
- return retGravity;
+ private int getNavBarHeight() {
+ return (int) getResources().getDimension(R.dimen.navigation_bar_size);
}
- private void rotateLeft(View v) {
- if (v.getParent() instanceof FrameLayout) {
- LayoutParams p = (LayoutParams) v.getLayoutParams();
- p.gravity = rotateGravityLeft(p.gravity);
+ //TODO: Find a better way
+ private int getDisplayCutoutHeight() {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ return 0;
}
- v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(),
- v.getPaddingLeft());
- MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
- params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin,
- params.leftMargin);
- v.setLayoutParams(params);
+ Rect r = mDisplayCutout.getBoundingRect();
+ return r.bottom - r.top;
}
- private void rotateRight(View v) {
- if (v.getParent() instanceof FrameLayout) {
- LayoutParams p = (LayoutParams) v.getLayoutParams();
- p.gravity = rotateGravityRight(p.gravity);
- }
-
- v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(),
- v.getPaddingRight());
- MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
- params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin,
- params.rightMargin);
- v.setLayoutParams(params);
- }
private void animateChild(int oldHeight, int newHeight) {
if (true) return;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 40f8059fecbf..dfc1852502e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -30,6 +30,7 @@ import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
import com.android.systemui.SysuiTestCase;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +41,11 @@ public class AutoAddTrackerTest extends SysuiTestCase {
private AutoAddTracker mAutoTracker;
+ @Before
+ public void setUp() {
+ Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, "");
+ }
+
@Test
public void testMigration() {
Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
@@ -50,7 +56,10 @@ public class AutoAddTrackerTest extends SysuiTestCase {
assertTrue(mAutoTracker.isAdded(WORK));
assertFalse(mAutoTracker.isAdded(INVERSION));
+ // These keys have been removed; retrieving their values should always return the default.
+ assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true ));
assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false));
+ assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true));
assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false));
mAutoTracker.destroy();
@@ -96,5 +105,4 @@ public class AutoAddTrackerTest extends SysuiTestCase {
mAutoTracker.destroy();
}
-
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index f3c1171f650c..6e7477fbac38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,7 +32,6 @@ import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationInflaterTest;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -52,7 +51,7 @@ public class NotificationTestHelper {
public NotificationTestHelper(Context context) {
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
+ mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager);
}
public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index a95e3dbb1e3b..2d2db1bba735 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -18,45 +18,48 @@ package com.android.systemui.statusbar.phone;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.AlarmManager.AlarmClockInfo;
+import android.os.Handler;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-
import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
-
-import org.junit.After;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
public class AutoTileManagerTest extends SysuiTestCase {
- private QSTileHost mQsTileHost;
+ @Mock private QSTileHost mQsTileHost;
+ @Mock private AutoAddTracker mAutoAddTracker;
+ @Captor private ArgumentCaptor<NextAlarmChangeCallback> mAlarmCallback;
+
private AutoTileManager mAutoTileManager;
@Before
public void setUp() throws Exception {
- mDependency.injectTestDependency(Dependency.BG_LOOPER,
- TestableLooper.get(this).getLooper());
- Prefs.putBoolean(mContext, Key.QS_NIGHTDISPLAY_ADDED, false);
- mQsTileHost = Mockito.mock(QSTileHost.class);
- mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
- }
-
- @After
- public void tearDown() throws Exception {
- mAutoTileManager = null;
+ MockitoAnnotations.initMocks(this);
+ mDependency.injectMockDependency(NextAlarmController.class);
+ mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker,
+ mQsTileHost, new Handler(TestableLooper.get(this).getLooper()));
+ verify(Dependency.get(NextAlarmController.class))
+ .addCallback(mAlarmCallback.capture());
}
@Test
@@ -106,4 +109,30 @@ public class AutoTileManagerTest extends SysuiTestCase {
ColorDisplayController.AUTO_MODE_DISABLED);
verify(mQsTileHost, never()).addTile("night");
}
+
+ @Test
+ public void alarmTileAdded_whenAlarmSet() {
+ mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
+
+ verify(mQsTileHost).addTile("alarm");
+ verify(mAutoAddTracker).setTileAdded("alarm");
+ }
+
+ @Test
+ public void alarmTileNotAdded_whenAlarmNotSet() {
+ mAlarmCallback.getValue().onNextAlarmChanged(null);
+
+ verify(mQsTileHost, never()).addTile("alarm");
+ verify(mAutoAddTracker, never()).setTileAdded("alarm");
+ }
+
+ @Test
+ public void alarmTileNotAdded_whenAlreadyAdded() {
+ when(mAutoAddTracker.isAdded("alarm")).thenReturn(true);
+
+ mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
+
+ verify(mQsTileHost, never()).addTile("alarm");
+ verify(mAutoAddTracker, never()).setTileAdded("alarm");
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
deleted file mode 100644
index 28f941779043..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.view.View;
-import android.service.notification.StatusBarNotification;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-
-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 static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class HeadsUpManagerPhoneTest extends SysuiTestCase {
- @Rule public MockitoRule rule = MockitoJUnit.rule();
-
- private static final String TEST_PACKAGE_NAME = "test";
- private static final int TEST_UID = 0;
-
- private HeadsUpManagerPhone mHeadsUpManager;
-
- private NotificationData.Entry mEntry;
- private StatusBarNotification mSbn;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
- @Mock private NotificationGroupManager mGroupManager;
- @Mock private View mStatusBarWindowView;
- @Mock private StatusBar mBar;
- @Mock private ExpandableNotificationRow mRow;
- @Mock private VisualStabilityManager mVSManager;
-
- @Before
- public void setUp() {
- when(mVSManager.isReorderingAllowed()).thenReturn(true);
-
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
-
- Notification.Builder n = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
- mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
- 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-
- mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
- mEntry.expandedIcon = mock(StatusBarIconView.class);
- }
-
- @Test
- public void testBasicOperations() {
- // Check the initial state.
- assertNull(mHeadsUpManager.getEntry(mEntry.key));
- assertNull(mHeadsUpManager.getTopEntry());
- assertEquals(0, mHeadsUpManager.getAllEntries().count());
- assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
-
- // Add a notification.
- mHeadsUpManager.showNotification(mEntry);
-
- assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
- assertEquals(mEntry, mHeadsUpManager.getTopEntry());
- assertEquals(1, mHeadsUpManager.getAllEntries().count());
- assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
-
- // Update the notification.
- mHeadsUpManager.updateNotification(mEntry, false);
-
- assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
- assertEquals(mEntry, mHeadsUpManager.getTopEntry());
- assertEquals(1, mHeadsUpManager.getAllEntries().count());
- assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
-
- // Remove but defer, since the notification is visible on display.
- mHeadsUpManager.removeNotification(mEntry.key, false);
-
- assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
- assertEquals(mEntry, mHeadsUpManager.getTopEntry());
- assertEquals(1, mHeadsUpManager.getAllEntries().count());
- assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 31442af5a04c..bdf9b1f6da9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -86,8 +86,8 @@ import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -110,7 +110,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private UnlockMethodCache mUnlockMethodCache;
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Mock private SystemServicesProxy mSystemServicesProxy;
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@@ -588,7 +588,7 @@ public class StatusBarTest extends SysuiTestCase {
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
- NotificationStackScrollLayout stack, HeadsUpManagerPhone hum,
+ NotificationStackScrollLayout stack, HeadsUpManager hum,
PowerManager pm, NotificationPanelView panelView,
IStatusBarService barService, NotificationListener notificationListener,
NotificationLogger notificationLogger,
@@ -650,7 +650,7 @@ public class StatusBarTest extends SysuiTestCase {
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
Callback callback,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
NotificationData notificationData) {
super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index 1c010b66056a..d9673d3552d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -61,4 +61,15 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
Assert.assertFalse(mStackScroller.isDimmed());
}
+ @Test
+ public void testAntiBurnInOffset() {
+ final int burnInOffset = 30;
+ mStackScroller.setAntiBurnInOffsetX(burnInOffset);
+ mStackScroller.setDark(false /* dark */, false /* animated */, null /* touch */);
+ Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
+ mStackScroller.setDark(true /* dark */, false /* animated */, null /* touch */);
+ Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
+ 0.01 /* delta */);
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
index c18ed732f244..f7bb0655b46e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -44,11 +44,13 @@ import com.android.systemui.statusbar.policy.BluetoothController;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@Ignore
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -87,7 +89,7 @@ public class OutputChooserDialogTest extends SysuiTestCase {
public void tearDown() throws Exception {
TestableLooper.get(this).processAllMessages();
}
-
+/*
@Test
public void testClickMediaRouterItemConnectsMedia() {
mDialog.show();
@@ -137,7 +139,7 @@ public class OutputChooserDialogTest extends SysuiTestCase {
.getText().toString().contains("Phone"));
mDialog.dismiss();
}
-
+*/
@Test
public void testNoMediaScanIfInCall() {
mDialog.setIsInCall(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 2d28c9f214fb..43d60e41a6b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -16,12 +16,21 @@
package com.android.systemui.volume;
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_SILENT;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.media.AudioManager.STREAM_RING;
+
import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.app.KeyguardManager;
import android.media.AudioManager;
import android.support.test.filters.SmallTest;
@@ -32,11 +41,14 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -44,6 +56,7 @@ import org.mockito.MockitoAnnotations;
import java.util.function.Predicate;
+@Ignore
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -70,13 +83,19 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mDialog = new VolumeDialogImpl(getContext());
mDialog.init(0, null);
- VolumeDialogController.State state = new VolumeDialogController.State();
+ State state = createShellState();
+ mDialog.onStateChangedH(state);
+ }
+
+ private State createShellState() {
+ State state = new VolumeDialogController.State();
for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) {
VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState();
ss.name = STREAMS.get(i);
+ ss.level = 1;
state.states.append(i, ss);
}
- mDialog.onStateChangedH(state);
+ return state;
}
private void navigateViews(View view, Predicate<View> condition) {
@@ -94,7 +113,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
+ " failed test", condition.test(view));
}
}
-
+/*
@Test
public void testContentDescriptions() {
mDialog.show(SHOW_REASON_UNKNOWN);
@@ -111,4 +130,95 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mDialog.dismiss(DISMISS_REASON_UNKNOWN);
}
+ @Test
+ public void testNoDuplicationOfParentState() {
+ mDialog.show(SHOW_REASON_UNKNOWN);
+ ViewGroup dialog = mDialog.getDialogView();
+
+ navigateViews(dialog, view -> !view.isDuplicateParentStateEnabled());
+
+ mDialog.dismiss(DISMISS_REASON_UNKNOWN);
+ }
+
+ @Test
+ public void testNoClickableViewGroups() {
+ mDialog.show(SHOW_REASON_UNKNOWN);
+ ViewGroup dialog = mDialog.getDialogView();
+
+ navigateViews(dialog, view -> {
+ if (view instanceof ViewGroup) {
+ return !view.isClickable();
+ } else {
+ return true;
+ }
+ });
+
+ mDialog.dismiss(DISMISS_REASON_UNKNOWN);
+ }
+
+ @Test
+ public void testTristateToggle_withVibrator() {
+ when(mController.hasVibrator()).thenReturn(true);
+
+ State state = createShellState();
+ state.ringerModeInternal = RINGER_MODE_NORMAL;
+ mDialog.onStateChangedH(state);
+
+ mDialog.show(SHOW_REASON_UNKNOWN);
+ ViewGroup dialog = mDialog.getDialogView();
+
+ // click once, verify updates to vibrate
+ dialog.findViewById(R.id.ringer_icon).performClick();
+ verify(mController, times(1)).setRingerMode(RINGER_MODE_VIBRATE, false);
+
+ // fake the update back to the dialog with the new ringer mode
+ state = createShellState();
+ state.ringerModeInternal = RINGER_MODE_VIBRATE;
+ mDialog.onStateChangedH(state);
+
+ // click once, verify updates to silent
+ dialog.findViewById(R.id.ringer_icon).performClick();
+ verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
+ verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+
+ // fake the update back to the dialog with the new ringer mode
+ state = createShellState();
+ state.states.get(STREAM_RING).level = 0;
+ state.ringerModeInternal = RINGER_MODE_SILENT;
+ mDialog.onStateChangedH(state);
+
+ // click once, verify updates to normal
+ dialog.findViewById(R.id.ringer_icon).performClick();
+ verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
+ verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+ }
+
+ @Test
+ public void testTristateToggle_withoutVibrator() {
+ when(mController.hasVibrator()).thenReturn(false);
+
+ State state = createShellState();
+ state.ringerModeInternal = RINGER_MODE_NORMAL;
+ mDialog.onStateChangedH(state);
+
+ mDialog.show(SHOW_REASON_UNKNOWN);
+ ViewGroup dialog = mDialog.getDialogView();
+
+ // click once, verify updates to silent
+ dialog.findViewById(R.id.ringer_icon).performClick();
+ verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
+ verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+
+ // fake the update back to the dialog with the new ringer mode
+ state = createShellState();
+ state.states.get(STREAM_RING).level = 0;
+ state.ringerModeInternal = RINGER_MODE_SILENT;
+ mDialog.onStateChangedH(state);
+
+ // click once, verify updates to normal
+ dialog.findViewById(R.id.ringer_icon).performClick();
+ verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
+ verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+ }
+ */
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
deleted file mode 100644
index 4ab2063196d4..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.net.Uri;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.ZenModeConfig;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.FlakyTest;
-import android.view.LayoutInflater;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Ignore
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ZenModePanelTest extends SysuiTestCase {
-
- ZenModePanel mPanel;
- ZenModeController mController;
- Uri mForeverId;
-
- @Before
- public void setup() throws Exception {
- final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- mPanel = (ZenModePanel) layoutInflater.inflate(com.android.systemui.R.layout.zen_mode_panel,
- null);
- mController = mock(ZenModeController.class);
- mForeverId = Condition.newId(mContext).appendPath("forever").build();
-
- mPanel.init(mController);
- }
-
- private void assertProperConditionTagTypes(boolean hasAlarm) {
- final int N = mPanel.getVisibleConditions();
- assertEquals(hasAlarm ? 3 : 2, N);
-
- assertEquals(mForeverId, mPanel.getConditionTagAt(0).condition.id);
- assertTrue(ZenModeConfig.isValidCountdownConditionId(
- mPanel.getConditionTagAt(1).condition.id));
- assertFalse(ZenModeConfig.isValidCountdownToAlarmConditionId(
- mPanel.getConditionTagAt(1).condition.id));
- if (hasAlarm) {
- assertTrue(ZenModeConfig.isValidCountdownToAlarmConditionId(
- mPanel.getConditionTagAt(2).condition.id));
- }
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_alarmExists() {
- Condition forever = new Condition(mForeverId, "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(forever);
- assertProperConditionTagTypes(true);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_noAlarm() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(forever);
- assertProperConditionTagTypes(false);
- assertEquals(foreverId, mPanel.getConditionTagAt(0).condition.id);
- }
-
- @Test
- public void testHandleUpdateConditions_countdownSelected_alarmExists() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(countdown);
- assertProperConditionTagTypes(true);
- assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_countdownSelected_noAlarm() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(countdown);
- assertProperConditionTagTypes(false);
- assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_nextAlarmSelected() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 1000, true),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 9000);
-
- mPanel.handleUpdateConditions(alarm);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_alarmConditionDoesNotChangeIfAttached() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 9000, true),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(alarm);
- mPanel.setAttached(true);
- mPanel.handleUpdateConditions(forever);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_timeConditionDoesNotChangeIfAttached() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(countdown);
- mPanel.setAttached(true);
- mPanel.handleUpdateConditions(forever);
-
- assertProperConditionTagTypes(false);
- assertEquals(countdown, mPanel.getConditionTagAt(1).condition);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- @UiThreadTest
- public void testHandleUpdateManualRule_stillSelectedAfterModeChange() {
- ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 1000, true),
- "", Condition.STATE_TRUE);
-
- rule.condition = alarm;
- rule.conditionId = alarm.id;
- rule.enabled = true;
- rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-
- mPanel.handleUpdateManualRule(rule);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
- assertEquals(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
-
- rule.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
-
- mPanel.handleUpdateManualRule(rule);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
- assertEquals(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS,
- mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
- }
-}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 7539d8847c2d..01714cf571b5 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5173,6 +5173,18 @@ message MetricsEvent {
// OS: P
AUTOFILL_INVALID_PERMISSION = 1289;
+ // OPEN: QS Alarm tile shown
+ // ACTION: QS Alarm tile tapped
+ // SUBTYPE: 0 is off, 1 is on
+ // CATEGORY: QUICK_SETTINGS
+ // OS: P
+ QS_ALARM = 1290;
+
+ // OPEN: Settings->Connected Devices->USB->(click on details link)
+ // CATEGORY: SETTINGS
+ // OS: P
+ USB_DEVICE_DETAILS = 1291;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index db70184e9e9f..08fdb9775d0a 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -200,6 +200,10 @@ message SystemMessage {
// Package: android
NOTE_CARRIER_NETWORK_AVAILABLE = 46;
+ // Inform that USB is configured for Tethering
+ // Package: android
+ NOTE_USB_TETHER = 47;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index f5349df0bd94..c77dcc01b7a1 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -63,7 +63,7 @@ message WifiLog {
// Number scans that returned at least one result.
optional int32 num_non_empty_scan_results = 13;
- // Number of scans that were one time.
+ // Number of single scans requests.
optional int32 num_oneshot_scans = 14;
// Number of repeated background scans that were scheduled to the chip.
@@ -373,6 +373,12 @@ message WifiLog {
// Wps connection metrics
optional WpsMetrics wps_metrics = 91;
+
+ // Wifi power statistics
+ optional WifiPowerStats wifi_power_stats = 92;
+
+ // Number of connectivity single scan requests.
+ optional int32 num_connectivity_oneshot_scans = 93;
}
// Information that gets logged for every WiFi connection.
@@ -1134,3 +1140,22 @@ message WpsMetrics {
// Total number of wps cancellation
optional int32 num_wps_cancellation = 8;
}
+
+// Power stats for Wifi
+message WifiPowerStats {
+
+ // Duration of log (ms)
+ optional int64 logging_duration_ms = 1;
+
+ // Energy consumed by wifi (mAh)
+ optional double energy_consumed_mah = 2;
+
+ // Amount of time wifi is in idle (ms)
+ optional int64 idle_time_ms = 3;
+
+ // Amount of time wifi is in rx (ms)
+ optional int64 rx_time_ms = 4;
+
+ // Amount of time wifi is in tx (ms)
+ optional int64 tx_time_ms = 5;
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 5ee3cbfed3f5..5f112c7fc773 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -27,6 +27,7 @@ import android.content.IntentSender;
import android.graphics.Point;
import android.graphics.Rect;
import android.service.autofill.Dataset;
+import android.service.autofill.Dataset.DatasetFieldFilter;
import android.service.autofill.FillResponse;
import android.text.TextUtils;
import android.util.Slog;
@@ -58,6 +59,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
final class FillUi {
private static final String TAG = "FillUi";
@@ -185,7 +187,7 @@ final class FillUi {
final ArrayList<ViewItem> items = new ArrayList<>(totalItems);
if (header != null) {
if (sVerbose) Slog.v(TAG, "adding header");
- items.add(new ViewItem(null, null, null, header));
+ items.add(new ViewItem(null, null, false, null, header));
}
for (int i = 0; i < datasetCount; i++) {
final Dataset dataset = response.getDatasets().get(i);
@@ -205,21 +207,32 @@ final class FillUi {
Slog.e(TAG, "Error inflating remote views", e);
continue;
}
- final Pattern filter = dataset.getFilter(index);
+ final DatasetFieldFilter filter = dataset.getFilter(index);
+ Pattern filterPattern = null;
String valueText = null;
+ boolean filterable = true;
if (filter == null) {
final AutofillValue value = dataset.getFieldValues().get(index);
if (value != null && value.isText()) {
valueText = value.getTextValue().toString().toLowerCase();
}
+ } else {
+ filterPattern = filter.pattern;
+ if (filterPattern == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "Explicitly disabling filter at id " + focusedViewId
+ + " for dataset #" + index);
+ }
+ filterable = false;
+ }
}
- items.add(new ViewItem(dataset, filter, valueText, view));
+ items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
}
}
if (footer != null) {
if (sVerbose) Slog.v(TAG, "adding footer");
- items.add(new ViewItem(null, null, null, footer));
+ items.add(new ViewItem(null, null, false, null, footer));
}
mAdapter = new ItemsAdapter(items);
@@ -354,7 +367,7 @@ final class FillUi {
MeasureSpec.AT_MOST);
final int itemCount = mAdapter.getCount();
for (int i = 0; i < itemCount; i++) {
- View view = mAdapter.getItem(i).view;
+ final View view = mAdapter.getItem(i).view;
view.measure(widthMeasureSpec, heightMeasureSpec);
final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
@@ -400,13 +413,62 @@ final class FillUi {
public final @Nullable Dataset dataset;
public final @NonNull View view;
public final @Nullable Pattern filter;
+ public final boolean filterable;
- ViewItem(@Nullable Dataset dataset, @Nullable Pattern filter, @Nullable String value,
- @NonNull View view) {
+ /**
+ * Default constructor.
+ *
+ * @param dataset dataset associated with the item or {@code null} if it's a header or
+ * footer (TODO(b/69796626): make @NonNull if header/footer is refactored out of the list)
+ * @param filter optional filter set by the service to determine how the item should be
+ * filtered
+ * @param filterable optional flag set by the service to indicate this item should not be
+ * filtered (typically used when the dataset has value but it's sensitive, like a password)
+ * @param value dataset value
+ * @param view dataset presentation.
+ */
+ ViewItem(@Nullable Dataset dataset, @Nullable Pattern filter, boolean filterable,
+ @Nullable String value, @NonNull View view) {
this.dataset = dataset;
this.value = value;
this.view = view;
this.filter = filter;
+ this.filterable = filterable;
+ }
+
+ /**
+ * Returns whether this item matches the value input by the user so it can be included
+ * in the filtered datasets.
+ */
+ public boolean matches(CharSequence filterText) {
+ if (TextUtils.isEmpty(filterText)) {
+ // Always show item when the user input is empty
+ return true;
+ }
+ if (!filterable) {
+ // Service explicitly disabled filtering using a null Pattern.
+ return false;
+ }
+ final String constraintLowerCase = filterText.toString().toLowerCase();
+ if (filter != null) {
+ // Uses pattern provided by service
+ return filter.matcher(constraintLowerCase).matches();
+ } else {
+ // Compares it with dataset value with dataset
+ return (value == null)
+ ? (dataset.getAuthentication() == null)
+ : value.toLowerCase().startsWith(constraintLowerCase);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ViewItem: [dataset=" + (dataset == null ? "null" : dataset.getId())
+ + ", value=" + (value == null ? "null" : value.length() + "_chars")
+ + ", filterable=" + filterable
+ + ", filter=" + (filter == null ? "null" : filter.pattern().length() + "_chars")
+ + ", view=" + view.getAutofillId()
+ + "]";
}
}
@@ -509,7 +571,7 @@ final class FillUi {
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
- pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter != null);
+ pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
pw.print(prefix); pw.print("mFilterText: ");
Helper.printlnRedactedText(pw, mFilterText);
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
@@ -556,33 +618,14 @@ final class FillUi {
public Filter getFilter() {
return new Filter() {
@Override
- protected FilterResults performFiltering(CharSequence constraint) {
+ protected FilterResults performFiltering(CharSequence filterText) {
// No locking needed as mAllItems is final an immutable
+ final List<ViewItem> filtered = mAllItems.stream()
+ .filter((item) -> item.matches(filterText))
+ .collect(Collectors.toList());
final FilterResults results = new FilterResults();
- if (TextUtils.isEmpty(constraint)) {
- results.values = mAllItems;
- results.count = mAllItems.size();
- return results;
- }
- final List<ViewItem> filteredItems = new ArrayList<>();
- final String constraintLowerCase = constraint.toString().toLowerCase();
- final int itemCount = mAllItems.size();
- for (int i = 0; i < itemCount; i++) {
- final ViewItem item = mAllItems.get(i);
- final boolean matches;
- if (item.filter != null) {
- matches = item.filter.matcher(constraintLowerCase).matches();
- } else {
- matches = (item.value == null)
- ? (item.dataset.getAuthentication() == null)
- : item.value.toLowerCase().startsWith(constraintLowerCase);
- }
- if (matches) {
- filteredItems.add(item);
- }
- }
- results.values = filteredItems;
- results.count = filteredItems.size();
+ results.values = filtered;
+ results.count = filtered.size();
return results;
}
@@ -624,6 +667,11 @@ final class FillUi {
public View getView(int position, View convertView, ViewGroup parent) {
return getItem(position).view;
}
+
+ @Override
+ public String toString() {
+ return "ItemsAdapter: [all=" + mAllItems + ", filtered=" + mFilteredItems + "]";
+ }
}
private final class AnnounceFilterResult implements Runnable {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 219facd0b002..0502117cd73a 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -34,7 +34,7 @@ option java_package com.android.server
2731 power_soft_sleep_requested (savedwaketimems|2)
# Power save state has changed. See BatterySaverController.java for the details.
2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
-27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6)
+27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
#
# Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fc91d0d7abf1..2f425859fec4 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -59,6 +59,7 @@ import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -468,7 +469,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
int mCurFocusedWindowSoftInputMode;
/**
- * The client by which {@link #mCurFocusedWindow} was reported. Used only for debugging.
+ * The client by which {@link #mCurFocusedWindow} was reported.
*/
ClientState mCurFocusedWindowClient;
@@ -2989,8 +2990,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final int uid = Binder.getCallingUid();
if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
return true;
- } else if (mCurClient != null && client != null
- && mCurClient.client.asBinder() == client.asBinder()) {
+ } else if (mCurFocusedWindowClient != null && client != null
+ && mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
return true;
} else if (mCurIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
mAppOpsManager,
@@ -3026,6 +3027,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ public boolean isInputMethodPickerShownForTest() {
+ synchronized(mMethodMap) {
+ if (mSwitchingDialog == null) {
+ return false;
+ }
+ return mSwitchingDialog.isShowing();
+ }
+ }
+
@Override
public void setInputMethod(IBinder token, String id) {
if (!calledFromValidUser()) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index fe4ac6d771bc..a07a982abc53 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -87,6 +87,7 @@ public class IpSecService extends IIpSecService.Stub {
private static final String NETD_SERVICE_NAME = "netd";
private static final int[] DIRECTIONS =
new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
+ private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"};
private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
private static final int MAX_PORT_BIND_ATTEMPTS = 10;
@@ -413,12 +414,16 @@ public class IpSecService extends IIpSecService.Stub {
.append(mTransformQuotaTracker)
.append(", mSocketQuotaTracker=")
.append(mSocketQuotaTracker)
+ .append(", mTunnelQuotaTracker=")
+ .append(mTunnelQuotaTracker)
.append(", mSpiRecords=")
.append(mSpiRecords)
.append(", mTransformRecords=")
.append(mTransformRecords)
.append(", mEncapSocketRecords=")
.append(mEncapSocketRecords)
+ .append(", mTunnelInterfaceRecords=")
+ .append(mTunnelInterfaceRecords)
.append("}")
.toString();
}
@@ -815,12 +820,14 @@ public class IpSecService extends IIpSecService.Stub {
try {
mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
- for (int direction : DIRECTIONS) {
- int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityPolicy(
- 0, direction, mLocalAddress, mRemoteAddress, mark, 0xffffffff);
+ for(String wildcardAddr : WILDCARD_ADDRESSES) {
+ for (int direction : DIRECTIONS) {
+ int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecDeleteSecurityPolicy(
+ 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
+ }
}
} catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception
@@ -1261,19 +1268,21 @@ public class IpSecService extends IIpSecService.Stub {
.getNetdInstance()
.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
- for (int direction : DIRECTIONS) {
- int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
+ for(String wildcardAddr : WILDCARD_ADDRESSES) {
+ for (int direction : DIRECTIONS) {
+ int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
- mSrvConfig
- .getNetdInstance()
- .ipSecAddSecurityPolicy(
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecAddSecurityPolicy(
0, // Use 0 for reqId
direction,
- "",
- "",
+ wildcardAddr,
+ wildcardAddr,
0,
mark,
0xffffffff);
+ }
}
userRecord.mTunnelInterfaceRecords.put(
@@ -1646,16 +1655,18 @@ public class IpSecService extends IIpSecService.Stub {
c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
// If outbound, also add SPI to the policy.
- mSrvConfig
- .getNetdInstance()
- .ipSecUpdateSecurityPolicy(
- 0, // Use 0 for reqId
- direction,
- "",
- "",
- transformInfo.getSpiRecord().getSpi(),
- mark,
- 0xffffffff);
+ for(String wildcardAddr : WILDCARD_ADDRESSES) {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecUpdateSecurityPolicy(
+ 0, // Use 0 for reqId
+ direction,
+ wildcardAddr,
+ wildcardAddr,
+ transformInfo.getSpiRecord().getSpi(),
+ mark,
+ 0xffffffff);
+ }
}
// Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 1dd92f3130a7..65e90bad41a7 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1397,23 +1397,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
/**
- * Returns "true" if access to the specified location provider is allowed by the specified
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- * @param uid the requestor's UID
- * @param userId the user id to query
- */
- private boolean isAllowedByUserSettingsLockedForUser(
- String provider, int uid, int userId) {
- if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
- return false;
- }
- return isLocationProviderEnabledForUser(provider, userId);
- }
-
- /**
* Returns the permission string associated with the specified resolution level.
*
* @param resolutionLevel the resolution level
@@ -2585,143 +2568,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
/**
- * Method for enabling or disabling location.
- *
- * @param enabled true to enable location. false to disable location
- * @param userId the user id to set
- */
- @Override
- public void setLocationEnabledForUser(boolean enabled, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Enable or disable all location providers. Fused provider and passive provider are
- // excluded.
- synchronized (mLock) {
- for(String provider : getAllProvidersForLocationSettings()) {
- setProviderEnabledForUser(provider, enabled, userId);
- }
- }
- }
-
- /**
- * Returns the current enabled/disabled status of location
- *
- * @param userId the user id to query
- * @return true if location is enabled. false if location is disabled.
- */
- @Override
- public boolean isLocationEnabledForUser(int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // If at least one location provider is enabled, return true. Fused provider and passive
- // provider are excluded.
- synchronized (mLock) {
- for (String provider : getAllProvidersForLocationSettings()) {
- if (isProviderEnabledForUser(provider, userId)) {
- return true;
- }
- }
- return false;
- }
- }
-
- @Override
- public boolean isProviderEnabled(String provider) {
- return isProviderEnabledForUser(provider, UserHandle.getCallingUserId());
- }
-
- /**
- * Method for determining if a location provider is enabled.
- *
- * @param provider the location provider to query
- * @param userId the user id to query
- * @return true if the provider is enabled
- */
- @Override
- public boolean isProviderEnabledForUser(String provider, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null
- && isAllowedByUserSettingsLockedForUser(provider, uid, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Method for enabling or disabling a single location provider.
- *
- * @param provider the name of the provider
- * @param enabled true to enable the provider. false to disable the provider
- * @param userId the user id to set
- * @return true if the value was set successfully. false on failure.
- */
- @Override
- public boolean setProviderEnabledForUser(
- String provider, boolean enabled, int userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
-
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- if (enabled) {
- provider = "+" + provider;
- } else {
- provider = "-" + provider;
- }
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- provider,
- userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Return all location providers except fused provider and passive provider. These two
- * providers are not generating location by themselves, but only echo locations from other
- * providers.
- *
- * @return All location providers except fused provider and passive provider, including
- * providers that are not permitted to be accessed by the calling activity or are
- * currently disabled.
- */
- private List<String> getAllProvidersForLocationSettings() {
- List<String> providersForSettings = new ArrayList<>(mProviders.size());
- for (String provider : getAllProviders()) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)) {
- continue;
- }
- providersForSettings.add(provider);
- }
- return providersForSettings;
- }
-
- /**
* Read location provider status from Settings.Secure
*
* @param provider the location provider to query
@@ -2742,23 +2588,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
/**
- * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
- * current user id
- *
- * @param userId the user id to get or set value
- */
- private void checkInteractAcrossUsersPermission(int userId) {
- int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
- if (ActivityManager.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
- != PERMISSION_GRANTED) {
- throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
- }
- }
- }
-
- /**
* Returns "true" if the UID belongs to a bound location provider.
*
* @param uid the uid
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 6747be340d46..6743484b91c4 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -35,6 +35,7 @@ import android.os.UserHandle;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.DisconnectCause;
+import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneStateListener;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
@@ -93,7 +94,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
- int callerUserId;
+ int callerUid;
+ int callerPid;
int events;
@@ -117,7 +119,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " callback=" + callback
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
- + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
+ + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
+ " events=" + Integer.toHexString(events)
+ " canReadPhoneState=" + canReadPhoneState + "}";
}
@@ -356,6 +358,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
public void addOnSubscriptionsChangedListener(String callingPackage,
IOnSubscriptionsChangedListener callback) {
int callerUserId = UserHandle.getCallingUserId();
+ mContext.getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (VDBG) {
log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
+ " callerUserId=" + callerUserId + " callback=" + callback
@@ -399,7 +403,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.onSubscriptionsChangedListenerCallback = callback;
r.callingPackage = callingPackage;
- r.callerUserId = callerUserId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
r.events = 0;
r.canReadPhoneState = true; // permission has been enforced above
if (DBG) {
@@ -470,6 +475,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private void listen(String callingPackage, IPhoneStateListener callback, int events,
boolean notifyNow, int subId) {
int callerUserId = UserHandle.getCallingUserId();
+ mContext.getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (VDBG) {
log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
+ " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
@@ -514,7 +521,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.callback = callback;
r.callingPackage = callingPackage;
- r.callerUserId = callerUserId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
| ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
@@ -572,8 +580,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (DBG_LOC) log("listen: mCellLocation = "
+ mCellLocation[phoneId]);
- r.callback.onCellLocationChanged(
- new Bundle(mCellLocation[phoneId]));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellLocationChanged(
+ new Bundle(mCellLocation[phoneId]));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -619,7 +629,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -979,7 +991,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCellInfo.set(phoneId, cellInfo);
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
- idMatch(r.subId, subId, phoneId)) {
+ idMatch(r.subId, subId, phoneId) &&
+ checkLocationAccess(r)) {
try {
if (DBG_LOC) {
log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1262,7 +1275,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCellLocation[phoneId] = cellLocation;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
- idMatch(r.subId, subId, phoneId)) {
+ idMatch(r.subId, subId, phoneId) &&
+ checkLocationAccess(r)) {
try {
if (DBG_LOC) {
log("notifyCellLocation: cellLocation=" + cellLocation
@@ -1706,10 +1720,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
boolean valid = false;
try {
foregroundUser = ActivityManager.getCurrentUser();
- valid = r.callerUserId == foregroundUser && r.matchPhoneStateListenerEvent(events);
+ valid = UserHandle.getUserId(r.callerUid) == foregroundUser
+ && r.matchPhoneStateListenerEvent(events);
if (DBG | DBG_LOC) {
log("validateEventsAndUserLocked: valid=" + valid
- + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
+ + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
+ " r.events=" + r.events + " events=" + events);
}
} finally {
@@ -1741,6 +1756,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
+ private boolean checkLocationAccess(Record r) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return LocationAccessPolicy.canAccessCellLocation(mContext,
+ r.callingPackage, r.callerUid, r.callerPid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void checkPossibleMissNotify(Record r, int phoneId) {
int events = r.events;
@@ -1788,7 +1813,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
}
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1836,7 +1863,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
+ mCellLocation[phoneId]);
- r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5b8b6912cd19..8d1632a3cef6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -802,6 +802,7 @@ public class ActivityManagerService extends IActivityManager.Stub
doDump(fd, pw, new String[]{"recents"}, asProto);
doDump(fd, pw, new String[]{"lastanr"}, asProto);
doDump(fd, pw, new String[]{"starter"}, asProto);
+ doDump(fd, pw, new String[]{"containers"}, asProto);
if (mAssociations.size() > 0) {
doDump(fd, pw, new String[]{"associations"}, asProto);
}
@@ -4027,9 +4028,13 @@ public class ActivityManagerService extends IActivityManager.Stub
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("true".equals(genDebugInfoProperty)) {
+ if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
+ String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+ if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
@@ -4331,7 +4336,9 @@ public class ActivityManagerService extends IActivityManager.Stub
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
component.userId, component.realActivity.getPackageName(),
- component.realActivity.getShortClassName(), resumed ? 1 : 0);
+ component.realActivity.getShortClassName(), resumed ?
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
@@ -12844,7 +12851,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// TODO: Where should the corresponding '1' (start) write go?
- StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, 0);
+ StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED,
+ StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF);
boolean timedout = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index f0c90e0f1fab..24a77c7693d0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -115,6 +115,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
private int mActivityType;
private int mTaskId;
private boolean mIsTaskOverlay;
+ private boolean mIsLockTask;
final boolean mDumping;
@@ -278,6 +279,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
mActivityType = ACTIVITY_TYPE_UNDEFINED;
mTaskId = INVALID_TASK_ID;
mIsTaskOverlay = false;
+ mIsLockTask = false;
return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
@Override
@@ -334,6 +336,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
mTaskId = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("--task-overlay")) {
mIsTaskOverlay = true;
+ } else if (opt.equals("--lock-task")) {
+ mIsLockTask = true;
} else {
return false;
}
@@ -429,13 +433,22 @@ final class ActivityManagerShellCommand extends ShellCommand {
options.setLaunchActivityType(mActivityType);
}
if (mTaskId != INVALID_TASK_ID) {
- options = ActivityOptions.makeBasic();
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
options.setLaunchTaskId(mTaskId);
if (mIsTaskOverlay) {
options.setTaskOverlay(true, true /* canResume */);
}
}
+ android.util.Log.d("bfranz", "I was here: " + mIsLockTask);
+ if (mIsLockTask) {
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ options.setLockTaskMode(true);
+ }
if (mWaitOption) {
result = mInterface.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6beafcb94ead..bf3882587693 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1251,10 +1251,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
synchronized (mService) {
try {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+ int modifiedFlags = flags
+ | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
+ if (intent.isBrowsableWebIntent()
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
+ modifiedFlags |= PackageManager.MATCH_INSTANT;
+ }
return mService.getPackageManagerInternalLocked().resolveIntent(
- intent, resolvedType, PackageManager.MATCH_INSTANT
- | PackageManager.MATCH_DEFAULT_ONLY | flags
- | ActivityManagerService.STOCK_PM_FLAGS, userId, true);
+ intent, resolvedType, modifiedFlags, userId, true);
} finally {
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 8fd754af1a0f..eab88aa45e44 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -31,6 +31,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -789,7 +790,7 @@ class ActivityStarter {
// Instead, launch the ephemeral installer. Once the installer is finished, it
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
- if (rInfo != null && rInfo.auxiliaryInfo != null) {
+ if (rInfo != null && rInfo.isInstantAppAvailable) {
intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
callingPackage, verificationBundle, resolvedType, userId);
resolvedType = null;
@@ -849,22 +850,27 @@ class ActivityStarter {
/**
* Creates a launch intent for the given auxiliary resolution data.
*/
- private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse,
+ private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse,
Intent originalIntent, String callingPackage, Bundle verificationBundle,
String resolvedType, int userId) {
- if (auxiliaryResponse.needsPhaseTwo) {
+ if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
// request phase two resolution
mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
auxiliaryResponse, originalIntent, resolvedType, callingPackage,
verificationBundle, userId);
}
return InstantAppResolver.buildEphemeralInstallerIntent(
- Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, originalIntent,
- auxiliaryResponse.failureIntent, callingPackage, verificationBundle,
- resolvedType, userId, auxiliaryResponse.packageName, auxiliaryResponse.splitName,
- auxiliaryResponse.installFailureActivity, auxiliaryResponse.versionCode,
- auxiliaryResponse.token, auxiliaryResponse.resolveInfo.getExtras(),
- auxiliaryResponse.needsPhaseTwo);
+ originalIntent,
+ InstantAppResolver.sanitizeIntent(originalIntent),
+ auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent,
+ callingPackage,
+ verificationBundle,
+ resolvedType,
+ userId,
+ auxiliaryResponse == null ? null : auxiliaryResponse.installFailureActivity,
+ auxiliaryResponse == null ? null : auxiliaryResponse.token,
+ auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo,
+ auxiliaryResponse == null ? null : auxiliaryResponse.filters);
}
void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
@@ -924,12 +930,12 @@ class ActivityStarter {
// Don't modify the client's object!
intent = new Intent(intent);
if (componentSpecified
- && intent.getData() != null
- && Intent.ACTION_VIEW.equals(intent.getAction())
+ && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
+ && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
&& mService.getPackageManagerInternalLocked()
.isInstantAppInstallerComponent(intent.getComponent())) {
// intercept intents targeted directly to the ephemeral installer the
- // ephemeral installer should never be started with a raw URL; instead
+ // ephemeral installer should never be started with a raw Intent; instead
// adjust the intent so it looks like a "normal" instant app launch
intent.setComponent(null /*component*/);
componentSpecified = false;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0d96468761be..ea52782027ba 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -325,39 +325,38 @@ public final class BatteryStatsService extends IBatteryStats.Stub
void noteProcessStart(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessStartLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED);
}
}
void noteProcessCrash(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessCrashLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED);
}
}
void noteProcessAnr(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessAnrLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED);
}
}
void noteProcessFinish(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessFinishLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED);
}
}
/** @param state Process state from ActivityManager.java. */
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid,
ActivityManager.processStateAmToProto(state));
@@ -603,7 +602,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state);
mStats.noteScreenStateLocked(state);
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 21f9135bd03d..e5762d294922 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -38,6 +38,7 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -142,14 +143,6 @@ public class LockTaskController {
TelecomManager mTelecomManager;
/**
- * Helper that is responsible for showing the right toast when a disallowed activity operation
- * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
- * fully locked mode we only show that unlocking is blocked.
- */
- @VisibleForTesting
- LockTaskNotify mLockTaskNotify;
-
- /**
* The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode.
*
* The first task in the list, which started the current LockTask session, is called the root
@@ -475,7 +468,7 @@ public class LockTaskController {
getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
}
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- getLockTaskNotify().showPinningExitToast();
+ getStatusBarService().showPinningEnterExitToast(false /* entering */);
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
@@ -490,7 +483,11 @@ public class LockTaskController {
*/
void showLockTaskToast() {
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- mHandler.post(() -> getLockTaskNotify().showEscapeToast());
+ try {
+ getStatusBarService().showPinningEscapeToast();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to send pinning escape toast", e);
+ }
}
}
@@ -582,7 +579,7 @@ public class LockTaskController {
// When lock task starts, we disable the status bars.
try {
if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
- getLockTaskNotify().showPinningStartToast();
+ getStatusBarService().showPinningEnterExitToast(true /* entering */);
}
mLockTaskModeState = lockTaskModeState;
setStatusBarState(lockTaskModeState, userId);
@@ -835,15 +832,6 @@ public class LockTaskController {
return mTelecomManager;
}
- // Should only be called on the handler thread
- @NonNull
- private LockTaskNotify getLockTaskNotify() {
- if (mLockTaskNotify == null) {
- mLockTaskNotify = new LockTaskNotify(mContext);
- }
- return mLockTaskNotify;
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "LockTaskController");
prefix = prefix + " ";
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 3bb3d1fdd032..5af19eca5034 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.hardware.broadcastradio.V2_0.AmFmBandRange;
import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
import android.hardware.broadcastradio.V2_0.Announcement;
+import android.hardware.broadcastradio.V2_0.DabTableEntry;
import android.hardware.broadcastradio.V2_0.IdentifierType;
import android.hardware.broadcastradio.V2_0.ProgramFilter;
import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
@@ -196,9 +197,15 @@ class Convert {
return bands.toArray(new RadioManager.BandDescriptor[bands.size()]);
}
+ private static @Nullable Map<String, Integer> dabConfigFromHal(
+ @Nullable List<DabTableEntry> config) {
+ if (config == null) return null;
+ return config.stream().collect(Collectors.toMap(e -> e.label, e -> e.frequency));
+ }
+
static @NonNull RadioManager.ModuleProperties
propertiesFromHal(int id, @NonNull String serviceName, @NonNull Properties prop,
- @Nullable AmFmRegionConfig amfmConfig) {
+ @Nullable AmFmRegionConfig amfmConfig, @Nullable List<DabTableEntry> dabConfig) {
Objects.requireNonNull(serviceName);
Objects.requireNonNull(prop);
@@ -228,6 +235,7 @@ class Convert {
true, // isBgScanSupported is deprecated
supportedProgramTypes,
supportedIdentifierTypes,
+ dabConfigFromHal(dabConfig),
vendorInfoFromHal(prop.vendorInfo)
);
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 50f032d659b7..daec97a0a166 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -24,6 +24,7 @@ import android.hardware.radio.ITuner;
import android.hardware.radio.RadioManager;
import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
import android.hardware.broadcastradio.V2_0.Announcement;
+import android.hardware.broadcastradio.V2_0.DabTableEntry;
import android.hardware.broadcastradio.V2_0.IAnnouncementListener;
import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hardware.broadcastradio.V2_0.ICloseHandle;
@@ -58,12 +59,17 @@ class RadioModule {
if (service == null) return null;
Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>();
- service.getAmFmRegionConfig(false, (int result, AmFmRegionConfig config) -> {
+ service.getAmFmRegionConfig(false, (result, config) -> {
if (result == Result.OK) amfmConfig.value = config;
});
- RadioManager.ModuleProperties prop =
- Convert.propertiesFromHal(idx, fqName, service.getProperties(), amfmConfig.value);
+ Mutable<List<DabTableEntry>> dabConfig = new Mutable<>();
+ service.getDabRegionConfig((result, config) -> {
+ if (result == Result.OK) dabConfig.value = config;
+ });
+
+ RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
+ service.getProperties(), amfmConfig.value, dabConfig.value);
return new RadioModule(service, prop);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index be6c4a12d114..9a9cdbde3987 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1095,7 +1095,8 @@ public class Tethering extends BaseNetworkObserver {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
synchronized (mPublicSync) {
- usbManager.setCurrentFunction(enable ? UsbManager.USB_FUNCTION_RNDIS : null, false);
+ usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
+ : UsbManager.FUNCTION_NONE);
}
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
new file mode 100644
index 000000000000..6e571bd75946
--- /dev/null
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class that stores stats of ambient brightness regions as histogram.
+ */
+public class AmbientBrightnessStatsTracker {
+
+ private static final String TAG = "AmbientBrightnessStatsTracker";
+ private static final boolean DEBUG = false;
+
+ @VisibleForTesting
+ static final float[] BUCKET_BOUNDARIES_FOR_NEW_STATS =
+ {0, 0.1f, 0.3f, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000};
+ @VisibleForTesting
+ static final int MAX_DAYS_TO_TRACK = 7;
+
+ private final AmbientBrightnessStats mAmbientBrightnessStats;
+ private final Timer mTimer;
+ private final Injector mInjector;
+ private final UserManager mUserManager;
+ private float mCurrentAmbientBrightness;
+ private @UserIdInt int mCurrentUserId;
+
+ public AmbientBrightnessStatsTracker(UserManager userManager, @Nullable Injector injector) {
+ mUserManager = userManager;
+ if (injector != null) {
+ mInjector = injector;
+ } else {
+ mInjector = new Injector();
+ }
+ mAmbientBrightnessStats = new AmbientBrightnessStats();
+ mTimer = new Timer(() -> mInjector.elapsedRealtimeMillis());
+ mCurrentAmbientBrightness = -1;
+ }
+
+ public synchronized void start() {
+ mTimer.reset();
+ mTimer.start();
+ }
+
+ public synchronized void stop() {
+ if (mTimer.isRunning()) {
+ mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
+ mCurrentAmbientBrightness, mTimer.totalDurationSec());
+ }
+ mTimer.reset();
+ mCurrentAmbientBrightness = -1;
+ }
+
+ public synchronized void add(@UserIdInt int userId, float newAmbientBrightness) {
+ if (mTimer.isRunning()) {
+ if (userId == mCurrentUserId) {
+ mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
+ mCurrentAmbientBrightness, mTimer.totalDurationSec());
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "User switched since last sensor event.");
+ }
+ mCurrentUserId = userId;
+ }
+ mTimer.reset();
+ mTimer.start();
+ mCurrentAmbientBrightness = newAmbientBrightness;
+ } else {
+ if (DEBUG) {
+ Slog.e(TAG, "Timer not running while trying to add brightness stats.");
+ }
+ }
+ }
+
+ public synchronized void writeStats(OutputStream stream) throws IOException {
+ mAmbientBrightnessStats.writeToXML(stream);
+ }
+
+ public synchronized void readStats(InputStream stream) throws IOException {
+ mAmbientBrightnessStats.readFromXML(stream);
+ }
+
+ public synchronized ArrayList<AmbientBrightnessDayStats> getUserStats(int userId) {
+ return mAmbientBrightnessStats.getUserStats(userId);
+ }
+
+ public synchronized void dump(PrintWriter pw) {
+ pw.println("AmbientBrightnessStats:");
+ pw.print(mAmbientBrightnessStats);
+ }
+
+ /**
+ * AmbientBrightnessStats tracks ambient brightness stats across users over multiple days.
+ * This class is not ThreadSafe.
+ */
+ class AmbientBrightnessStats {
+
+ private static final String TAG_AMBIENT_BRIGHTNESS_STATS = "ambient-brightness-stats";
+ private static final String TAG_AMBIENT_BRIGHTNESS_DAY_STATS =
+ "ambient-brightness-day-stats";
+ private static final String ATTR_USER = "user";
+ private static final String ATTR_LOCAL_DATE = "local-date";
+ private static final String ATTR_BUCKET_BOUNDARIES = "bucket-boundaries";
+ private static final String ATTR_BUCKET_STATS = "bucket-stats";
+
+ private Map<Integer, Deque<AmbientBrightnessDayStats>> mStats;
+
+ public AmbientBrightnessStats() {
+ mStats = new HashMap<>();
+ }
+
+ public void log(@UserIdInt int userId, LocalDate localDate, float ambientBrightness,
+ float durationSec) {
+ Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(mStats, userId);
+ AmbientBrightnessDayStats dayStats = getOrCreateDayStats(userStats, localDate);
+ dayStats.log(ambientBrightness, durationSec);
+ }
+
+ public ArrayList<AmbientBrightnessDayStats> getUserStats(@UserIdInt int userId) {
+ if (mStats.containsKey(userId)) {
+ return new ArrayList<>(mStats.get(userId));
+ } else {
+ return null;
+ }
+ }
+
+ public void writeToXML(OutputStream stream) throws IOException {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
+ out.startTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
+ for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
+ for (AmbientBrightnessDayStats userDayStats : entry.getValue()) {
+ int userSerialNumber = mInjector.getUserSerialNumber(mUserManager,
+ entry.getKey());
+ if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) {
+ out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
+ out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber));
+ out.attribute(null, ATTR_LOCAL_DATE,
+ userDayStats.getLocalDate().toString());
+ StringBuilder bucketBoundariesValues = new StringBuilder();
+ StringBuilder timeSpentValues = new StringBuilder();
+ for (int i = 0; i < userDayStats.getBucketBoundaries().length; i++) {
+ if (i > 0) {
+ bucketBoundariesValues.append(",");
+ timeSpentValues.append(",");
+ }
+ bucketBoundariesValues.append(userDayStats.getBucketBoundaries()[i]);
+ timeSpentValues.append(userDayStats.getStats()[i]);
+ }
+ out.attribute(null, ATTR_BUCKET_BOUNDARIES,
+ bucketBoundariesValues.toString());
+ out.attribute(null, ATTR_BUCKET_STATS, timeSpentValues.toString());
+ out.endTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
+ }
+ }
+ }
+ out.endTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
+ out.endDocument();
+ stream.flush();
+ }
+
+ public void readFromXML(InputStream stream) throws IOException {
+ try {
+ Map<Integer, Deque<AmbientBrightnessDayStats>> parsedStats = new HashMap<>();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+ String tag = parser.getName();
+ if (!TAG_AMBIENT_BRIGHTNESS_STATS.equals(tag)) {
+ throw new XmlPullParserException(
+ "Ambient brightness stats not found in tracker file " + tag);
+ }
+
+ final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
+ parser.next();
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ tag = parser.getName();
+ if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) {
+ String userSerialNumber = parser.getAttributeValue(null, ATTR_USER);
+ LocalDate localDate = LocalDate.parse(
+ parser.getAttributeValue(null, ATTR_LOCAL_DATE));
+ String[] bucketBoundaries = parser.getAttributeValue(null,
+ ATTR_BUCKET_BOUNDARIES).split(",");
+ String[] bucketStats = parser.getAttributeValue(null,
+ ATTR_BUCKET_STATS).split(",");
+ if (bucketBoundaries.length != bucketStats.length
+ || bucketBoundaries.length < 1) {
+ throw new IOException("Invalid brightness stats string.");
+ }
+ float[] parsedBucketBoundaries = new float[bucketBoundaries.length];
+ float[] parsedBucketStats = new float[bucketStats.length];
+ for (int i = 0; i < bucketBoundaries.length; i++) {
+ parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]);
+ parsedBucketStats[i] = Float.parseFloat(bucketStats[i]);
+ }
+ int userId = mInjector.getUserId(mUserManager,
+ Integer.parseInt(userSerialNumber));
+ if (userId != -1 && localDate.isAfter(cutOffDate)) {
+ Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(
+ parsedStats, userId);
+ userStats.offer(
+ new AmbientBrightnessDayStats(localDate,
+ parsedBucketBoundaries, parsedBucketStats));
+ }
+ }
+ }
+ mStats = parsedStats;
+ } catch (NullPointerException | NumberFormatException | XmlPullParserException |
+ DateTimeParseException | IOException e) {
+ throw new IOException("Failed to parse brightness stats file.", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
+ for (AmbientBrightnessDayStats dayStats : entry.getValue()) {
+ builder.append(" ");
+ builder.append(entry.getKey()).append(" ");
+ builder.append(dayStats).append("\n");
+ }
+ }
+ return builder.toString();
+ }
+
+ private Deque<AmbientBrightnessDayStats> getOrCreateUserStats(
+ Map<Integer, Deque<AmbientBrightnessDayStats>> stats, @UserIdInt int userId) {
+ if (!stats.containsKey(userId)) {
+ stats.put(userId, new ArrayDeque<>());
+ }
+ return stats.get(userId);
+ }
+
+ private AmbientBrightnessDayStats getOrCreateDayStats(
+ Deque<AmbientBrightnessDayStats> userStats, LocalDate localDate) {
+ AmbientBrightnessDayStats lastBrightnessStats = userStats.peekLast();
+ if (lastBrightnessStats != null && lastBrightnessStats.getLocalDate().equals(
+ localDate)) {
+ return lastBrightnessStats;
+ } else {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(localDate,
+ BUCKET_BOUNDARIES_FOR_NEW_STATS);
+ if (userStats.size() == MAX_DAYS_TO_TRACK) {
+ userStats.poll();
+ }
+ userStats.offer(dayStats);
+ return dayStats;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ interface Clock {
+ long elapsedTimeMillis();
+ }
+
+ @VisibleForTesting
+ static class Timer {
+
+ private final Clock clock;
+ private long startTimeMillis;
+ private boolean started;
+
+ public Timer(Clock clock) {
+ this.clock = clock;
+ }
+
+ public void reset() {
+ started = false;
+ }
+
+ public void start() {
+ if (!started) {
+ startTimeMillis = clock.elapsedTimeMillis();
+ started = true;
+ }
+ }
+
+ public boolean isRunning() {
+ return started;
+ }
+
+ public float totalDurationSec() {
+ if (started) {
+ return (float) ((clock.elapsedTimeMillis() - startTimeMillis) / 1000.0);
+ }
+ return 0;
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ public long elapsedRealtimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ public int getUserSerialNumber(UserManager userManager, int userId) {
+ return userManager.getUserSerialNumber(userId);
+ }
+
+ public int getUserId(UserManager userManager, int userSerialNumber) {
+ return userManager.getUserHandle(userSerialNumber);
+ }
+
+ public LocalDate getLocalDate() {
+ return LocalDate.now();
+ }
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 6a88b1e1735d..e2aee88d331a 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -269,6 +269,14 @@ class AutomaticBrightnessController {
}
}
+ public boolean hasUserDataPoints() {
+ return mBrightnessMapper.hasUserDataPoints();
+ }
+
+ public boolean isDefaultConfig() {
+ return mBrightnessMapper.isDefaultConfig();
+ }
+
private boolean setDisplayPolicy(int policy) {
if (mDisplayPolicy == policy) {
return false;
diff --git a/services/core/java/com/android/server/display/BrightnessIdleJob.java b/services/core/java/com/android/server/display/BrightnessIdleJob.java
index 876acf45fda8..b0a41cb589eb 100644
--- a/services/core/java/com/android/server/display/BrightnessIdleJob.java
+++ b/services/core/java/com/android/server/display/BrightnessIdleJob.java
@@ -70,7 +70,7 @@ public class BrightnessIdleJob extends JobService {
Slog.d(BrightnessTracker.TAG, "Scheduled write of brightness events");
}
DisplayManagerInternal dmi = LocalServices.getService(DisplayManagerInternal.class);
- dmi.persistBrightnessSliderEvents();
+ dmi.persistBrightnessTrackerState();
return false;
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 001d4d903f3f..c0d259992a46 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -200,6 +200,12 @@ public abstract class BrightnessMappingStrategy {
*/
public abstract void clearUserDataPoints();
+ /** @return true if there are any short term adjustments applied to the curve */
+ public abstract boolean hasUserDataPoints();
+
+ /** @return true if the current brightness config is the default one */
+ public abstract boolean isDefaultConfig();
+
public abstract void dump(PrintWriter pw);
private static float normalizeAbsoluteBrightness(int brightness) {
@@ -390,6 +396,16 @@ public abstract class BrightnessMappingStrategy {
}
@Override
+ public boolean hasUserDataPoints() {
+ return mUserLux != -1;
+ }
+
+ @Override
+ public boolean isDefaultConfig() {
+ return true;
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("SimpleMappingStrategy");
pw.println(" mSpline=" + mSpline);
@@ -507,6 +523,16 @@ public abstract class BrightnessMappingStrategy {
}
@Override
+ public boolean hasUserDataPoints() {
+ return mUserLux != -1;
+ }
+
+ @Override
+ public boolean isDefaultConfig() {
+ return mDefaultConfig.equals(mConfig);
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("PhysicalMappingStrategy");
pw.println(" mConfig=" + mConfig);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index bcf8bfe6aaad..6b4666a679ed 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -28,8 +29,8 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
-import android.net.Uri;
import android.os.BatteryManager;
import android.os.Environment;
import android.os.Handler;
@@ -81,6 +82,7 @@ public class BrightnessTracker {
static final boolean DEBUG = false;
private static final String EVENTS_FILE = "brightness_events.xml";
+ private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
private static final int MAX_EVENTS = 100;
// Discard events when reading or writing that are older than this.
private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
@@ -99,6 +101,9 @@ public class BrightnessTracker {
private static final String ATTR_NIGHT_MODE = "nightMode";
private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
private static final String ATTR_LAST_NITS = "lastNits";
+ private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
+ private static final String ATTR_POWER_SAVE = "powerSaveFactor";
+ private static final String ATTR_USER_POINT = "userPoint";
private static final int MSG_BACKGROUND_START = 0;
private static final int MSG_BRIGHTNESS_CHANGED = 1;
@@ -113,6 +118,10 @@ public class BrightnessTracker {
private final Runnable mEventsWriter = () -> writeEvents();
private volatile boolean mWriteEventsScheduled;
+ private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
+ private final Runnable mAmbientBrightnessStatsWriter = () -> writeAmbientBrightnessStats();
+ private volatile boolean mWriteBrightnessStatsScheduled;
+
private UserManager mUserManager;
private final Context mContext;
private final ContentResolver mContentResolver;
@@ -120,6 +129,7 @@ public class BrightnessTracker {
// mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread.
private BroadcastReceiver mBroadcastReceiver;
private SensorListener mSensorListener;
+ private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
// Lock held while collecting data related to brightness changes.
private final Object mDataCollectionLock = new Object();
@@ -157,12 +167,19 @@ public class BrightnessTracker {
}
mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
mUserManager = mContext.getSystemService(UserManager.class);
-
+ try {
+ final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
+ mCurrentUserId = focusedStack.userId;
+ } catch (RemoteException e) {
+ // Really shouldn't be possible.
+ return;
+ }
mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
}
private void backgroundStart(float initialBrightness) {
readEvents();
+ readAmbientBrightnessStats();
mSensorListener = new SensorListener();
@@ -196,12 +213,20 @@ public class BrightnessTracker {
mInjector.unregisterSensorListener(mContext, mSensorListener);
mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
mInjector.cancelIdleJob(mContext);
+ mAmbientBrightnessStatsTracker.stop();
synchronized (mDataCollectionLock) {
mStarted = false;
}
}
+ public void onSwitchUser(@UserIdInt int newUserId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId);
+ }
+ mCurrentUserId = newUserId;
+ }
+
/**
* @param userId userId to fetch data for.
* @param includePackage if false we will null out BrightnessChangeEvent.packageName
@@ -228,24 +253,29 @@ public class BrightnessTracker {
return new ParceledListSlice<>(out);
}
- public void persistEvents() {
- scheduleWriteEvents();
+ public void persistBrightnessTrackerState() {
+ scheduleWriteBrightnessTrackerState();
}
/**
* Notify the BrightnessTracker that the user has changed the brightness of the display.
*/
- public void notifyBrightnessChanged(float brightness, boolean userInitiated) {
+ public void notifyBrightnessChanged(float brightness, boolean userInitiated,
+ float powerBrightnessFactor, boolean isUserSetBrightness,
+ boolean isDefaultBrightnessConfig) {
if (DEBUG) {
Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
brightness, userInitiated));
}
Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
- userInitiated ? 1 : 0, 0 /*unused*/, (Float) brightness);
+ userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
+ powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig));
m.sendToTarget();
}
- private void handleBrightnessChanged(float brightness, boolean userInitiated) {
+ private void handleBrightnessChanged(float brightness, boolean userInitiated,
+ float powerBrightnessFactor, boolean isUserSetBrightness,
+ boolean isDefaultBrightnessConfig) {
BrightnessChangeEvent.Builder builder;
synchronized (mDataCollectionLock) {
@@ -267,6 +297,9 @@ public class BrightnessTracker {
builder = new BrightnessChangeEvent.Builder();
builder.setBrightness(brightness);
builder.setTimeStamp(mInjector.currentTimeMillis());
+ builder.setPowerBrightnessFactor(powerBrightnessFactor);
+ builder.setUserBrightnessPoint(isUserSetBrightness);
+ builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
final int readingCount = mLastSensorReadings.size();
if (readingCount == 0) {
@@ -321,11 +354,15 @@ public class BrightnessTracker {
}
}
- private void scheduleWriteEvents() {
+ private void scheduleWriteBrightnessTrackerState() {
if (!mWriteEventsScheduled) {
mBgHandler.post(mEventsWriter);
mWriteEventsScheduled = true;
}
+ if (!mWriteBrightnessStatsScheduled) {
+ mBgHandler.post(mAmbientBrightnessStatsWriter);
+ mWriteBrightnessStatsScheduled = true;
+ }
}
private void writeEvents() {
@@ -336,7 +373,7 @@ public class BrightnessTracker {
return;
}
- final AtomicFile writeTo = mInjector.getFile();
+ final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE);
if (writeTo == null) {
return;
}
@@ -360,12 +397,29 @@ public class BrightnessTracker {
}
}
+ private void writeAmbientBrightnessStats() {
+ mWriteBrightnessStatsScheduled = false;
+ final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
+ if (writeTo == null) {
+ return;
+ }
+ FileOutputStream output = null;
+ try {
+ output = writeTo.startWrite();
+ mAmbientBrightnessStatsTracker.writeStats(output);
+ writeTo.finishWrite(output);
+ } catch (IOException e) {
+ writeTo.failWrite(output);
+ Slog.e(TAG, "Failed to write ambient brightness stats.", e);
+ }
+ }
+
private void readEvents() {
synchronized (mEventsLock) {
// Read might prune events so mark as dirty.
mEventsDirty = true;
mEvents.clear();
- final AtomicFile readFrom = mInjector.getFile();
+ final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE);
if (readFrom != null && readFrom.exists()) {
FileInputStream input = null;
try {
@@ -381,6 +435,23 @@ public class BrightnessTracker {
}
}
+ private void readAmbientBrightnessStats() {
+ mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null);
+ final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
+ if (readFrom != null && readFrom.exists()) {
+ FileInputStream input = null;
+ try {
+ input = readFrom.openRead();
+ mAmbientBrightnessStatsTracker.readStats(input);
+ } catch (IOException e) {
+ readFrom.delete();
+ Slog.e(TAG, "Failed to read ambient brightness stats.", e);
+ } finally {
+ IoUtils.closeQuietly(input);
+ }
+ }
+ }
+
@VisibleForTesting
@GuardedBy("mEventsLock")
void writeEventsLocked(OutputStream stream) throws IOException {
@@ -412,6 +483,12 @@ public class BrightnessTracker {
toWrite[i].colorTemperature));
out.attribute(null, ATTR_LAST_NITS,
Float.toString(toWrite[i].lastBrightness));
+ out.attribute(null, ATTR_DEFAULT_CONFIG,
+ Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
+ out.attribute(null, ATTR_POWER_SAVE,
+ Float.toString(toWrite[i].powerBrightnessFactor));
+ out.attribute(null, ATTR_USER_POINT,
+ Boolean.toString(toWrite[i].isUserSetBrightness));
StringBuilder luxValues = new StringBuilder();
StringBuilder luxTimestamps = new StringBuilder();
for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
@@ -496,6 +573,21 @@ public class BrightnessTracker {
builder.setLuxValues(luxValues);
builder.setLuxTimestamps(luxTimestamps);
+ String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
+ if (defaultConfig != null) {
+ builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
+ }
+ String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
+ if (powerSave != null) {
+ builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
+ } else {
+ builder.setPowerBrightnessFactor(1.0f);
+ }
+ String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
+ if (userPoint != null) {
+ builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
+ }
+
BrightnessChangeEvent event = builder.build();
if (DEBUG) {
Slog.i(TAG, "Read event " + event.brightness
@@ -535,7 +627,11 @@ public class BrightnessTracker {
BrightnessChangeEvent[] events = mEvents.toArray();
for (int i = 0; i < events.length; ++i) {
pw.print(" " + events[i].timeStamp + ", " + events[i].userId);
- pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness + ", {");
+ pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness);
+ pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness);
+ pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor);
+ pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig);
+ pw.print(" {");
for (int j = 0; j < events[i].luxValues.length; ++j){
if (j != 0) {
pw.print(", ");
@@ -545,6 +641,13 @@ public class BrightnessTracker {
pw.println("}");
}
}
+ if (mAmbientBrightnessStatsTracker != null) {
+ mAmbientBrightnessStatsTracker.dump(pw);
+ }
+ }
+
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
+ return new ParceledListSlice<>(mAmbientBrightnessStatsTracker.getUserStats(userId));
}
// Not allowed to keep the SensorEvent so used to copy the data we care about.
@@ -584,6 +687,10 @@ public class BrightnessTracker {
}
}
+ private void recordAmbientBrightnessStats(SensorEvent event) {
+ mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
+ }
+
private void batteryLevelChanged(int level, int scale) {
synchronized (mDataCollectionLock) {
mLastBatteryLevel = (float) level / (float) scale;
@@ -594,6 +701,7 @@ public class BrightnessTracker {
@Override
public void onSensorChanged(SensorEvent event) {
recordSensorEvent(event);
+ recordAmbientBrightnessStats(event);
}
@Override
@@ -611,7 +719,7 @@ public class BrightnessTracker {
String action = intent.getAction();
if (Intent.ACTION_SHUTDOWN.equals(action)) {
stop();
- scheduleWriteEvents();
+ scheduleWriteBrightnessTrackerState();
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
@@ -619,8 +727,10 @@ public class BrightnessTracker {
batteryLevelChanged(level, scale);
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mAmbientBrightnessStatsTracker.stop();
mInjector.unregisterSensorListener(mContext, mSensorListener);
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ mAmbientBrightnessStatsTracker.start();
mInjector.registerSensorListener(mContext, mSensorListener,
mInjector.getBackgroundHandler());
}
@@ -637,14 +747,31 @@ public class BrightnessTracker {
backgroundStart((float)msg.obj /*initial brightness*/);
break;
case MSG_BRIGHTNESS_CHANGED:
- float newBrightness = (float) msg.obj;
+ BrightnessChangeValues values = (BrightnessChangeValues) msg.obj;
boolean userInitiatedChange = (msg.arg1 == 1);
- handleBrightnessChanged(newBrightness, userInitiatedChange);
+ handleBrightnessChanged(values.brightness, userInitiatedChange,
+ values.powerBrightnessFactor, values.isUserSetBrightness,
+ values.isDefaultBrightnessConfig);
break;
}
}
}
+ private static class BrightnessChangeValues {
+ final float brightness;
+ final float powerBrightnessFactor;
+ final boolean isUserSetBrightness;
+ final boolean isDefaultBrightnessConfig;
+
+ BrightnessChangeValues(float brightness, float powerBrightnessFactor,
+ boolean isUserSetBrightness, boolean isDefaultBrightnessConfig) {
+ this.brightness = brightness;
+ this.powerBrightnessFactor = powerBrightnessFactor;
+ this.isUserSetBrightness = isUserSetBrightness;
+ this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ }
+ }
+
@VisibleForTesting
static class Injector {
public void registerSensorListener(Context context,
@@ -679,8 +806,8 @@ public class BrightnessTracker {
return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
}
- public AtomicFile getFile() {
- return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE));
+ public AtomicFile getFile(String filename) {
+ return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename));
}
public long currentTimeMillis() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0c2ff0519615..a5c1fe299e4e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -38,6 +38,7 @@ import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Point;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerGlobal;
@@ -359,6 +360,7 @@ public final class DisplayManagerService extends SystemService {
mPersistentDataStore.getBrightnessConfiguration(userSerial);
mDisplayPowerController.setBrightnessConfiguration(config);
}
+ mDisplayPowerController.onSwitchUser(newUserId);
}
}
@@ -1835,6 +1837,23 @@ public final class DisplayManagerService extends SystemService {
}
@Override // Binder call
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS,
+ "Permission required to to access ambient light stats.");
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ return mDisplayPowerController.getAmbientBrightnessStats(userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void setBrightnessConfigurationForUser(
BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
mContext.enforceCallingOrSelfPermission(
@@ -2039,9 +2058,9 @@ public final class DisplayManagerService extends SystemService {
}
@Override
- public void persistBrightnessSliderEvents() {
+ public void persistBrightnessTrackerState() {
synchronized (mSyncRoot) {
- mDisplayPowerController.persistBrightnessSliderEvents();
+ mDisplayPowerController.persistBrightnessTrackerState();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 80aec42b719c..b27f1ec3c653 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -34,6 +34,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
@@ -478,6 +479,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
mTemporaryScreenBrightness = -1;
+ mPendingScreenBrightnessSetting = -1;
mTemporaryAutoBrightnessAdjustment = Float.NaN;
}
@@ -498,11 +500,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return mBrightnessTracker.getEvents(userId, includePackage);
}
+ public void onSwitchUser(@UserIdInt int newUserId) {
+ mBrightnessTracker.onSwitchUser(newUserId);
+ }
+
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
+ @UserIdInt int userId) {
+ return mBrightnessTracker.getAmbientBrightnessStats(userId);
+ }
+
/**
- * Persist the brightness slider events to disk.
+ * Persist the brightness slider events and ambient brightness stats to disk.
*/
- public void persistBrightnessSliderEvents() {
- mBrightnessTracker.persistEvents();
+ public void persistBrightnessTrackerState() {
+ mBrightnessTracker.persistBrightnessTrackerState();
}
/**
@@ -795,13 +806,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
brightness = PowerManager.BRIGHTNESS_ON;
}
- // If the brightness is already set then it's been overriden by something other than the
+ // If the brightness is already set then it's been overridden by something other than the
// user, or is a temporary adjustment.
final boolean userInitiatedChange = brightness < 0
&& (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
+ boolean hadUserBrightnessPoint = false;
// Configure auto-brightness.
if (mAutomaticBrightnessController != null) {
+ hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
mAutomaticBrightnessController.configure(autoBrightnessEnabled,
mBrightnessConfiguration,
mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
@@ -930,7 +943,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
if (!brightnessIsTemporary) {
- notifyBrightnessChanged(brightness, userInitiatedChange);
+ notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint);
}
}
@@ -1464,18 +1477,26 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mPendingScreenBrightnessSetting = -1;
return false;
}
+ mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
mPendingScreenBrightnessSetting = -1;
return true;
}
- private void notifyBrightnessChanged(int brightness, boolean userInitiated) {
+ private void notifyBrightnessChanged(int brightness, boolean userInitiated,
+ boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
- if (brightnessInNits >= 0.0f) {
+ if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
+ && mAutomaticBrightnessController != null) {
// We only want to track changes on devices that can actually map the display backlight
// values into a physical brightness unit since the value provided by the API is in
// nits and not using the arbitrary backlight units.
- mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated);
+ final float powerFactor = mPowerRequest.lowPowerMode
+ ? mPowerRequest.screenLowPowerBrightnessFactor
+ : 1.0f;
+ mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
+ powerFactor, hadUserDataPoint,
+ mAutomaticBrightnessController.isDefaultConfig());
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e0baeee01c69..401c05e80307 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -862,7 +862,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
startTrackingJobLocked(jobStatus, toCancel);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uId, null, jobStatus.getBatteryName(), 2);
+ uId, null, jobStatus.getBatteryName(),
+ StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 28fa86b3a893..e71572483cca 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -383,8 +383,8 @@ public class LockSettingsService extends ILockSettings.Stub {
return KeyStore.getInstance();
}
- public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
- return RecoverableKeyStoreManager.getInstance(mContext);
+ public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
+ return RecoverableKeyStoreManager.getInstance(mContext, keyStore);
}
public IStorageManager getStorageManager() {
@@ -413,7 +413,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mInjector = injector;
mContext = injector.getContext();
mKeyStore = injector.getKeyStore();
- mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
+ mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore);
mHandler = injector.getHandler();
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();
@@ -1887,7 +1887,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mSpManager.removeUser(userId);
mStorage.removeUser(userId);
mStrongAuth.removeUser(userId);
- cleanSpCache();
+ tryRemoveUserFromSpCacheLater(userId);
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
@@ -2064,6 +2064,16 @@ public class LockSettingsService extends ILockSettings.Stub {
return mRecoverableKeyStoreManager.generateAndStoreKey(alias);
}
+ @Override
+ public String generateKey(@NonNull String alias, byte[] account) throws RemoteException {
+ return mRecoverableKeyStoreManager.generateKey(alias, account);
+ }
+
+ @Override
+ public String getKey(@NonNull String alias) throws RemoteException {
+ return mRecoverableKeyStoreManager.getKey(alias);
+ }
+
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
@@ -2141,6 +2151,13 @@ public class LockSettingsService extends ILockSettings.Stub {
private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
+ // Preemptively cache the SP and then try to remove it in a handler.
+ Slog.i(TAG, "Caching SP for user " + userId);
+ synchronized (mSpManager) {
+ mSpCache.put(userId, auth);
+ }
+ tryRemoveUserFromSpCacheLater(userId);
+
// Pass the primary user's auth secret to the HAL
if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) {
try {
@@ -2154,33 +2171,25 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e);
}
}
-
- // Update the SP cache, removing the entry when allowed
- synchronized (mSpManager) {
- if (shouldCacheSpForUser(userId)) {
- Slog.i(TAG, "Caching SP for user " + userId);
- mSpCache.put(userId, auth);
- } else {
- Slog.i(TAG, "Not caching SP for user " + userId);
- mSpCache.delete(userId);
- }
- }
}
- /** Clean up the SP cache by removing unneeded entries. */
- private void cleanSpCache() {
- synchronized (mSpManager) {
- // Preserve indicies after removal by iterating backwards
- for (int i = mSpCache.size() - 1; i >= 0; --i) {
- final int userId = mSpCache.keyAt(i);
- if (!shouldCacheSpForUser(userId)) {
- Slog.i(TAG, "Uncaching SP for user " + userId);
- mSpCache.removeAt(i);
+ private void tryRemoveUserFromSpCacheLater(@UserIdInt int userId) {
+ mHandler.post(() -> {
+ if (!shouldCacheSpForUser(userId)) {
+ // The transition from 'should not cache' to 'should cache' can only happen if
+ // certain admin apps are installed after provisioning e.g. via adb. This is not
+ // a common case and we do not seamlessly support; it may result in the SP not
+ // being cached when it is needed. The cache can be re-populated by verifying
+ // the credential again.
+ Slog.i(TAG, "Removing SP from cache for user " + userId);
+ synchronized (mSpManager) {
+ mSpCache.remove(userId);
}
}
- }
+ });
}
+ /** Do not hold any of the locks from this service when calling. */
private boolean shouldCacheSpForUser(@UserIdInt int userId) {
// Before the user setup has completed, an admin could be installed that requires the SP to
// be cached (see below).
@@ -2731,7 +2740,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (mDeviceProvisionedUri.equals(uri)) {
updateRegistration();
@@ -2741,7 +2750,7 @@ public class LockSettingsService extends ILockSettings.Stub {
clearFrpCredentialIfOwnerNotSecure();
}
} else if (mUserSetupCompleteUri.equals(uri)) {
- cleanSpCache();
+ tryRemoveUserFromSpCacheLater(userId);
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
index 59132da7d4c7..285e722886c2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
@@ -16,10 +16,13 @@
package com.android.server.locksettings.recoverablekeystore;
+import java.io.IOException;
+import java.security.cert.CertificateException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
/**
@@ -27,6 +30,7 @@ import java.security.UnrecoverableKeyException;
*/
public class KeyStoreProxyImpl implements KeyStoreProxy {
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private final KeyStore mKeyStore;
/**
@@ -57,4 +61,21 @@ public class KeyStoreProxyImpl implements KeyStoreProxy {
public void deleteEntry(String alias) throws KeyStoreException {
mKeyStore.deleteEntry(alias);
}
+
+ /**
+ * Returns AndroidKeyStore-provided {@link KeyStore}, having already invoked
+ * {@link KeyStore#load(KeyStore.LoadStoreParameter)}.
+ *
+ * @throws KeyStoreException if there was a problem getting or initializing the key store.
+ */
+ public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ try {
+ keyStore.load(/*param=*/ null);
+ } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
+ // Should never happen.
+ throw new KeyStoreException("Unable to load keystore.", e);
+ }
+ return keyStore;
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index ec72b221931f..fda6cdf33e0c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -37,21 +37,23 @@ import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.WrappedApplicationKey;
+import android.security.KeyStore;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
import java.security.InvalidKeyException;
-import java.security.KeyStoreException;
import java.security.KeyFactory;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
-import java.security.UnrecoverableKeyException;
import java.security.spec.InvalidKeySpecException;
+import java.security.UnrecoverableKeyException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
@@ -82,18 +84,23 @@ public class RecoverableKeyStoreManager {
private final RecoverableKeyGenerator mRecoverableKeyGenerator;
private final RecoverySnapshotStorage mSnapshotStorage;
private final PlatformKeyManager mPlatformKeyManager;
+ private final KeyStore mKeyStore;
+ private final ApplicationKeyStorage mApplicationKeyStorage;
/**
* Returns a new or existing instance.
*
* @hide
*/
- public static synchronized RecoverableKeyStoreManager getInstance(Context context) {
+ public static synchronized RecoverableKeyStoreManager
+ getInstance(Context context, KeyStore keystore) {
if (mInstance == null) {
RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
PlatformKeyManager platformKeyManager;
+ ApplicationKeyStorage applicationKeyStorage;
try {
platformKeyManager = PlatformKeyManager.getInstance(context, db);
+ applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore);
} catch (NoSuchAlgorithmException e) {
// Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e);
@@ -103,12 +110,14 @@ public class RecoverableKeyStoreManager {
mInstance = new RecoverableKeyStoreManager(
context.getApplicationContext(),
+ keystore,
db,
new RecoverySessionStorage(),
Executors.newSingleThreadExecutor(),
new RecoverySnapshotStorage(),
new RecoverySnapshotListenersStorage(),
- platformKeyManager);
+ platformKeyManager,
+ applicationKeyStorage);
}
return mInstance;
}
@@ -116,19 +125,23 @@ public class RecoverableKeyStoreManager {
@VisibleForTesting
RecoverableKeyStoreManager(
Context context,
+ KeyStore keystore,
RecoverableKeyStoreDb recoverableKeyStoreDb,
RecoverySessionStorage recoverySessionStorage,
ExecutorService executorService,
RecoverySnapshotStorage snapshotStorage,
RecoverySnapshotListenersStorage listenersStorage,
- PlatformKeyManager platformKeyManager) {
+ PlatformKeyManager platformKeyManager,
+ ApplicationKeyStorage applicationKeyStorage) {
mContext = context;
+ mKeyStore = keystore;
mDatabase = recoverableKeyStoreDb;
mRecoverySessionStorage = recoverySessionStorage;
mExecutorService = executorService;
mListenersStorage = listenersStorage;
mSnapshotStorage = snapshotStorage;
mPlatformKeyManager = platformKeyManager;
+ mApplicationKeyStorage = applicationKeyStorage;
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
@@ -406,6 +419,7 @@ public class RecoverableKeyStoreManager {
}
/**
+ * Deprecated
* Generates a key named {@code alias} in the recoverable store for the calling uid. Then
* returns the raw key material.
*
@@ -450,9 +464,55 @@ public class RecoverableKeyStoreManager {
boolean wasRemoved = mDatabase.removeKey(uid, alias);
if (wasRemoved) {
mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ mApplicationKeyStorage.deleteEntry(userId, uid, alias);
}
}
+ /**
+ * Generates a key named {@code alias} in caller's namespace.
+ * The key is stored in system service keystore namespace.
+ *
+ * @return grant alias, which caller can use to access the key.
+ */
+ public String generateKey(@NonNull String alias, byte[] account) throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+
+ PlatformEncryptionKey encryptionKey;
+ try {
+ encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
+ } catch (NoSuchAlgorithmException e) {
+ // Impossible: all algorithms must be supported by AOSP
+ throw new RuntimeException(e);
+ } catch (KeyStoreException | UnrecoverableKeyException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ } catch (InsecureUserException e) {
+ throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
+ }
+
+ try {
+ byte[] secretKey =
+ mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
+ mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
+ String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+ return grantAlias;
+ } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Gets a key named {@code alias} in caller's namespace.
+ *
+ * @return grant alias, which caller can use to access the key.
+ */
+ public String getKey(@NonNull String alias) throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+ return grantAlias;
+ }
+
private byte[] decryptRecoveryKey(
RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse)
throws RemoteException, ServiceSpecificException {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
new file mode 100644
index 000000000000..600a534facf9
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore.storage;
+
+import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
+
+import android.annotation.Nullable;
+import android.os.ServiceSpecificException;
+import android.security.Credentials;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.KeyStore;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy;
+import com.android.server.locksettings.recoverablekeystore.KeyStoreProxyImpl;
+
+import java.security.KeyStore.SecretKeyEntry;
+import java.security.KeyStoreException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Storage for Application keys in LockSettings service KeyStore namespace.
+ *
+ * <p> Uses KeyStore's grant mechanism to make keys usable by application process without
+ * revealing key material
+ */
+public class ApplicationKeyStorage {
+ private static final String APPLICATION_KEY_ALIAS_PREFIX =
+ "com.android.server.locksettings.recoverablekeystore/application/";
+
+ KeyStoreProxy mKeyStore;
+ KeyStore mKeystoreService;
+
+ public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
+ throws KeyStoreException {
+ return new ApplicationKeyStorage(
+ new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()),
+ keystoreService);
+ }
+
+ @VisibleForTesting
+ ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) {
+ mKeyStore = keyStore;
+ mKeystoreService = keystoreService;
+ }
+
+ /**
+ * Returns grant alias, valid in Applications namespace.
+ */
+ public @Nullable String getGrantAlias(int userId, int uid, String alias) {
+ // Aliases used by {@link KeyStore} are different than used by public API.
+ // {@code USER_PRIVATE_KEY} prefix is used secret keys.
+ String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
+ return mKeystoreService.grant(keystoreAlias, uid);
+ }
+
+ public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
+ throws KeyStoreException {
+ try {
+ mKeyStore.setEntry(
+ getInternalAlias(userId, uid, alias),
+ new SecretKeyEntry(
+ new SecretKeySpec(secretKey, KeyProperties.KEY_ALGORITHM_AES)),
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ } catch (KeyStoreException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ public void deleteEntry(int userId, int uid, String alias) {
+ try {
+ mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias));
+ } catch (KeyStoreException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Returns the alias in locksettins service's KeyStore namespace used for given application key.
+ *
+ * <p>These IDs look as follows:
+ * {@code com.security.recoverablekeystore/application/<userId>/<uid>/<alias>}
+ *
+ * @param userId The ID of the user
+ * @param uid The uid
+ * @param alias - alias in application's namespace
+ * @return The alias.
+ */
+ private String getInternalAlias(int userId, int uid, String alias) {
+ return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index d96671c5cd9d..79fd496ad11c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -28,7 +28,7 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKe
* Helper for creating the recoverable key database.
*/
class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
- private static final int DATABASE_VERSION = 1;
+ private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "recoverablekeystore.db";
private static final String SQL_CREATE_KEYS_ENTRY =
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 502760a2cfc4..fd435f952c10 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -771,7 +771,7 @@ abstract public class ManagedServices {
* Called whenever packages change, the user switches, or the secure setting
* is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
*/
- private void rebindServices(boolean forceRebind) {
+ protected void rebindServices(boolean forceRebind) {
if (DEBUG) Slog.d(TAG, "rebindServices");
final int[] userIds = mUserProfiles.getCurrentProfileIds();
final int nUserIds = userIds.length;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 727e7ee2ac18..b25124ac4562 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2891,6 +2891,7 @@ public class NotificationManagerService extends SystemService {
// Backup/restore interface
@Override
public byte[] getBackupPayload(int user) {
+ checkCallerIsSystem();
if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
//TODO: http://b/22388012
if (user != UserHandle.USER_SYSTEM) {
@@ -2911,6 +2912,7 @@ public class NotificationManagerService extends SystemService {
@Override
public void applyRestore(byte[] payload, int user) {
+ checkCallerIsSystem();
if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
+ (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
if (payload == null) {
@@ -5687,6 +5689,12 @@ public class NotificationManagerService extends SystemService {
mListeners.unregisterService(removed.service, removed.userid);
}
+ @Override
+ public void onUserUnlocked(int user) {
+ if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
+ rebindServices(true);
+ }
+
public void onNotificationEnqueued(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
TrimCache trimCache = new TrimCache(sbn);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c3f20af77b10..b79caca317a4 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -290,13 +290,14 @@ public class Installer extends SystemService {
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
@Nullable String seInfo, boolean downgrade, int targetSdkVersion,
- @Nullable String profileName) throws InstallerException {
+ @Nullable String profileName, @Nullable String dexMetadataPath)
+ throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
- targetSdkVersion, profileName);
+ targetSdkVersion, profileName, dexMetadataPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 30072d45ccab..af446ba02914 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -40,26 +40,33 @@ import android.content.pm.InstantAppIntentFilter;
import android.content.pm.InstantAppResolveInfo;
import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
import android.metrics.LogMaker;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.server.pm.EphemeralResolverConnection.ConnectionException;
-import com.android.server.pm.EphemeralResolverConnection.PhaseTwoCallback;
+import com.android.server.pm.InstantAppResolverConnection.ConnectionException;
+import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
/** @hide */
public abstract class InstantAppResolver {
- private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+ private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
private static final String TAG = "PackageManager";
private static final int RESOLUTION_SUCCESS = 0;
@@ -79,6 +86,7 @@ public abstract class InstantAppResolver {
public @interface ResolutionStatus {}
private static MetricsLogger sMetricsLogger;
+
private static MetricsLogger getLogger() {
if (sMetricsLogger == null) {
sMetricsLogger = new MetricsLogger();
@@ -86,26 +94,49 @@ public abstract class InstantAppResolver {
return sMetricsLogger;
}
- public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(Context context,
- EphemeralResolverConnection connection, InstantAppRequest requestObj) {
+ /**
+ * Returns an intent with potential PII removed from the original intent. Fields removed
+ * include extras and the host + path of the data, if defined.
+ */
+ public static Intent sanitizeIntent(Intent origIntent) {
+ final Intent sanitizedIntent;
+ sanitizedIntent = new Intent(origIntent.getAction());
+ Set<String> categories = origIntent.getCategories();
+ if (categories != null) {
+ for (String category : categories) {
+ sanitizedIntent.addCategory(category);
+ }
+ }
+ Uri sanitizedUri = origIntent.getData() == null
+ ? null
+ : Uri.fromParts(origIntent.getScheme(), "", "");
+ sanitizedIntent.setDataAndType(sanitizedUri, origIntent.getType());
+ sanitizedIntent.addFlags(origIntent.getFlags());
+ sanitizedIntent.setPackage(origIntent.getPackage());
+ return sanitizedIntent;
+ }
+
+ public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
+ InstantAppResolverConnection connection, InstantAppRequest requestObj) {
final long startTime = System.currentTimeMillis();
final String token = UUID.randomUUID().toString();
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Log.d(TAG, "[" + token + "] Phase1; resolving");
}
- final Intent intent = requestObj.origIntent;
- final InstantAppDigest digest =
- new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/);
+ final Intent origIntent = requestObj.origIntent;
+ final Intent sanitizedIntent = sanitizeIntent(origIntent);
+
+ final InstantAppDigest digest = getInstantAppDigest(origIntent);
final int[] shaPrefix = digest.getDigestPrefix();
AuxiliaryResolveInfo resolveInfo = null;
@ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
try {
final List<InstantAppResolveInfo> instantAppResolveInfoList =
- connection.getInstantAppResolveInfoList(shaPrefix, token);
+ connection.getInstantAppResolveInfoList(sanitizedIntent, shaPrefix, token);
if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
resolveInfo = InstantAppResolver.filterInstantAppIntent(
- instantAppResolveInfoList, intent, requestObj.resolvedType,
- requestObj.userId, intent.getPackage(), digest, token);
+ instantAppResolveInfoList, origIntent, requestObj.resolvedType,
+ requestObj.userId, origIntent.getPackage(), digest, token);
}
} catch (ConnectionException e) {
if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -121,7 +152,7 @@ public abstract class InstantAppResolver {
logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
resolutionStatus);
}
- if (DEBUG_EPHEMERAL && resolveInfo == null) {
+ if (DEBUG_INSTANT && resolveInfo == null) {
if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
Log.d(TAG, "[" + token + "] Phase1; bind timed out");
} else if (resolutionStatus == RESOLUTION_CALL_TIMEOUT) {
@@ -135,81 +166,67 @@ public abstract class InstantAppResolver {
return resolveInfo;
}
+ private static InstantAppDigest getInstantAppDigest(Intent origIntent) {
+ return origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())
+ ? new InstantAppDigest(origIntent.getData().getHost(), 5 /*maxDigests*/)
+ : InstantAppDigest.UNDEFINED;
+ }
+
public static void doInstantAppResolutionPhaseTwo(Context context,
- EphemeralResolverConnection connection, InstantAppRequest requestObj,
+ InstantAppResolverConnection connection, InstantAppRequest requestObj,
ActivityInfo instantAppInstaller, Handler callbackHandler) {
final long startTime = System.currentTimeMillis();
final String token = requestObj.responseObj.token;
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Log.d(TAG, "[" + token + "] Phase2; resolving");
}
- final Intent intent = requestObj.origIntent;
- final String hostName = intent.getData().getHost();
- final InstantAppDigest digest = new InstantAppDigest(hostName, 5 /*maxDigests*/);
+ final Intent origIntent = requestObj.origIntent;
+ final Intent sanitizedIntent = sanitizeIntent(origIntent);
+ final InstantAppDigest digest = getInstantAppDigest(origIntent);
final int[] shaPrefix = digest.getDigestPrefix();
final PhaseTwoCallback callback = new PhaseTwoCallback() {
@Override
void onPhaseTwoResolved(List<InstantAppResolveInfo> instantAppResolveInfoList,
long startTime) {
- final String packageName;
- final String splitName;
- final long versionCode;
final Intent failureIntent;
- final Bundle extras;
if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
final AuxiliaryResolveInfo instantAppIntentInfo =
InstantAppResolver.filterInstantAppIntent(
- instantAppResolveInfoList, intent, null /*resolvedType*/,
- 0 /*userId*/, intent.getPackage(), digest, token);
- if (instantAppIntentInfo != null
- && instantAppIntentInfo.resolveInfo != null) {
- packageName = instantAppIntentInfo.resolveInfo.getPackageName();
- splitName = instantAppIntentInfo.splitName;
- versionCode = instantAppIntentInfo.resolveInfo.getVersionCode();
+ instantAppResolveInfoList, origIntent, null /*resolvedType*/,
+ 0 /*userId*/, origIntent.getPackage(), digest, token);
+ if (instantAppIntentInfo != null) {
failureIntent = instantAppIntentInfo.failureIntent;
- extras = instantAppIntentInfo.resolveInfo.getExtras();
} else {
- packageName = null;
- splitName = null;
- versionCode = -1;
failureIntent = null;
- extras = null;
}
} else {
- packageName = null;
- splitName = null;
- versionCode = -1;
failureIntent = null;
- extras = null;
}
final Intent installerIntent = buildEphemeralInstallerIntent(
- Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE,
requestObj.origIntent,
+ sanitizedIntent,
failureIntent,
requestObj.callingPackage,
requestObj.verificationBundle,
requestObj.resolvedType,
requestObj.userId,
- packageName,
- splitName,
requestObj.responseObj.installFailureActivity,
- versionCode,
token,
- extras,
- false /*needsPhaseTwo*/);
+ false /*needsPhaseTwo*/,
+ requestObj.responseObj.filters);
installerIntent.setComponent(new ComponentName(
instantAppInstaller.packageName, instantAppInstaller.name));
logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token,
- packageName != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE);
+ requestObj.responseObj.filters != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE);
context.startActivity(installerIntent);
}
};
try {
- connection.getInstantAppIntentFilterList(
- shaPrefix, token, hostName, callback, callbackHandler, startTime);
+ connection.getInstantAppIntentFilterList(sanitizedIntent, shaPrefix, token, callback,
+ callbackHandler, startTime);
} catch (ConnectionException e) {
@ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -217,7 +234,7 @@ public abstract class InstantAppResolver {
}
logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token,
resolutionStatus);
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
Log.d(TAG, "[" + token + "] Phase2; bind timed out");
} else {
@@ -231,49 +248,53 @@ public abstract class InstantAppResolver {
* Builds and returns an intent to launch the instant installer.
*/
public static Intent buildEphemeralInstallerIntent(
- @NonNull String action,
@NonNull Intent origIntent,
- @NonNull Intent failureIntent,
+ @NonNull Intent sanitizedIntent,
+ @Nullable Intent failureIntent,
@NonNull String callingPackage,
@Nullable Bundle verificationBundle,
@NonNull String resolvedType,
int userId,
- @NonNull String instantAppPackageName,
- @Nullable String instantAppSplitName,
@Nullable ComponentName installFailureActivity,
- long versionCode,
@Nullable String token,
- @Nullable Bundle extras,
- boolean needsPhaseTwo) {
+ boolean needsPhaseTwo,
+ List<AuxiliaryResolveInfo.AuxiliaryFilter> filters) {
// Construct the intent that launches the instant installer
int flags = origIntent.getFlags();
- final Intent intent = new Intent(action);
+ final Intent intent = new Intent();
intent.setFlags(flags
| Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_HISTORY
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
if (token != null) {
+ // TODO(b/72700831): remove populating old extra
intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token);
+ intent.putExtra(Intent.EXTRA_INSTANT_APP_TOKEN, token);
}
if (origIntent.getData() != null) {
+ // TODO(b/72700831): remove populating old extra
intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
+ intent.putExtra(Intent.EXTRA_INSTANT_APP_HOSTNAME, origIntent.getData().getHost());
}
intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
- if (extras != null) {
- intent.putExtra(Intent.EXTRA_INSTANT_APP_EXTRAS, extras);
- }
+ intent.putExtra(Intent.EXTRA_INTENT, sanitizedIntent);
- // We have all of the data we need; just start the installer without a second phase
- if (!needsPhaseTwo) {
- // Intent that is launched if the package couldn't be installed for any reason.
+ if (needsPhaseTwo) {
+ intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
+ } else {
+ // We have all of the data we need; just start the installer without a second phase
if (failureIntent != null || installFailureActivity != null) {
+ // Intent that is launched if the package couldn't be installed for any reason.
try {
final Intent onFailureIntent;
if (installFailureActivity != null) {
onFailureIntent = new Intent();
onFailureIntent.setComponent(installFailureActivity);
- onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName);
+ if (filters != null && filters.size() == 1) {
+ onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME,
+ filters.get(0).splitName);
+ }
onFailureIntent.putExtra(Intent.EXTRA_INTENT, origIntent);
} else {
onFailureIntent = failureIntent;
@@ -288,8 +309,10 @@ public abstract class InstantAppResolver {
| PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_IMMUTABLE,
null /*bOptions*/, userId);
- intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE,
- new IntentSender(failureIntentTarget));
+ IntentSender failureSender = new IntentSender(failureIntentTarget);
+ // TODO(b/72700831): remove populating old extra
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, failureSender);
+ intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender);
} catch (RemoteException ignore) { /* ignore; same process */ }
}
@@ -306,20 +329,40 @@ public abstract class InstantAppResolver {
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_IMMUTABLE,
null /*bOptions*/, userId);
- intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS,
- new IntentSender(successIntentTarget));
+ IntentSender successSender = new IntentSender(successIntentTarget);
+ // TODO(b/72700831): remove populating old extra
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, successSender);
+ intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender);
} catch (RemoteException ignore) { /* ignore; same process */ }
-
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName);
- intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName);
- intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) (versionCode & 0x7fffffff));
- intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, versionCode);
- intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
if (verificationBundle != null) {
intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle);
}
- }
+ intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
+ if (filters != null) {
+ Bundle resolvableFilters[] = new Bundle[filters.size()];
+ for (int i = 0, max = filters.size(); i < max; i++) {
+ Bundle resolvableFilter = new Bundle();
+ AuxiliaryResolveInfo.AuxiliaryFilter filter = filters.get(i);
+ resolvableFilter.putBoolean(Intent.EXTRA_UNKNOWN_INSTANT_APP,
+ filter.resolveInfo != null
+ && filter.resolveInfo.shouldLetInstallerDecide());
+ resolvableFilter.putString(Intent.EXTRA_PACKAGE_NAME, filter.packageName);
+ resolvableFilter.putString(Intent.EXTRA_SPLIT_NAME, filter.splitName);
+ resolvableFilter.putLong(Intent.EXTRA_LONG_VERSION_CODE, filter.versionCode);
+ resolvableFilter.putBundle(Intent.EXTRA_INSTANT_APP_EXTRAS, filter.extras);
+ resolvableFilters[i] = resolvableFilter;
+ if (i == 0) {
+ // for backwards compat, always set the first result on the intent and add
+ // the int version code
+ intent.putExtras(resolvableFilter);
+ intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) filter.versionCode);
+ }
+ }
+ intent.putExtra(Intent.EXTRA_INSTANT_APP_BUNDLES, resolvableFilters);
+ }
+ intent.setAction(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
+ }
return intent;
}
@@ -330,69 +373,134 @@ public abstract class InstantAppResolver {
final int[] shaPrefix = digest.getDigestPrefix();
final byte[][] digestBytes = digest.getDigestBytes();
final Intent failureIntent = new Intent(origIntent);
+ boolean requiresSecondPhase = false;
failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
failureIntent.setLaunchToken(token);
- // Go in reverse order so we match the narrowest scope first.
- for (int i = shaPrefix.length - 1; i >= 0 ; --i) {
- for (InstantAppResolveInfo instantAppInfo : instantAppResolveInfoList) {
- if (!Arrays.equals(digestBytes[i], instantAppInfo.getDigestBytes())) {
- continue;
+ ArrayList<AuxiliaryResolveInfo.AuxiliaryFilter> filters = null;
+ boolean isWebIntent = origIntent.isBrowsableWebIntent();
+ for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) {
+ if (shaPrefix.length > 0 && instantAppResolveInfo.shouldLetInstallerDecide()) {
+ Slog.e(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest"
+ + " provided; ignoring");
+ continue;
+ }
+ byte[] filterDigestBytes = instantAppResolveInfo.getDigestBytes();
+ // Only include matching digests if we have a prefix and we're either dealing with a
+ // web intent or the resolveInfo specifies digest details.
+ if (shaPrefix.length > 0 && (isWebIntent || filterDigestBytes.length > 0)) {
+ boolean matchFound = false;
+ // Go in reverse order so we match the narrowest scope first.
+ for (int i = shaPrefix.length - 1; i >= 0; --i) {
+ if (Arrays.equals(digestBytes[i], filterDigestBytes)) {
+ matchFound = true;
+ break;
+ }
}
- if (packageName != null
- && !packageName.equals(instantAppInfo.getPackageName())) {
+ if (!matchFound) {
continue;
}
- final List<InstantAppIntentFilter> instantAppFilters =
- instantAppInfo.getIntentFilters();
- // No filters; we need to start phase two
- if (instantAppFilters == null || instantAppFilters.isEmpty()) {
- if (DEBUG_EPHEMERAL) {
- Log.d(TAG, "No app filters; go to phase 2");
- }
- return new AuxiliaryResolveInfo(instantAppInfo,
- new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/,
- null /*splitName*/, token, true /*needsPhase2*/,
- null /*failureIntent*/);
- }
- // We have a domain match; resolve the filters to see if anything matches.
- final PackageManagerService.EphemeralIntentResolver instantAppResolver =
- new PackageManagerService.EphemeralIntentResolver();
- for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
- final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
- final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
- if (splitFilters == null || splitFilters.isEmpty()) {
- continue;
- }
- for (int k = splitFilters.size() - 1; k >= 0; --k) {
- final AuxiliaryResolveInfo intentInfo =
- new AuxiliaryResolveInfo(instantAppInfo,
- splitFilters.get(k), instantAppFilter.getSplitName(),
- token, false /*needsPhase2*/, failureIntent);
- instantAppResolver.addFilter(intentInfo);
- }
+ }
+ // We matched a resolve info; resolve the filters to see if anything matches completely.
+ List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters(
+ origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo);
+ if (matchFilters != null) {
+ if (matchFilters.isEmpty()) {
+ requiresSecondPhase = true;
}
- List<AuxiliaryResolveInfo> matchedResolveInfoList = instantAppResolver.queryIntent(
- origIntent, resolvedType, false /*defaultOnly*/, userId);
- if (!matchedResolveInfoList.isEmpty()) {
- if (DEBUG_EPHEMERAL) {
- final AuxiliaryResolveInfo info = matchedResolveInfoList.get(0);
- Log.d(TAG, "[" + token + "] Found match;"
- + " package: " + info.packageName
- + ", split: " + info.splitName
- + ", versionCode: " + info.versionCode);
- }
- return matchedResolveInfoList.get(0);
- } else if (DEBUG_EPHEMERAL) {
- Log.d(TAG, "[" + token + "] No matches found"
- + " package: " + instantAppInfo.getPackageName()
- + ", versionCode: " + instantAppInfo.getVersionCode());
+ if (filters == null) {
+ filters = new ArrayList<>(matchFilters);
+ } else {
+ filters.addAll(matchFilters);
}
}
}
+ if (filters != null && !filters.isEmpty()) {
+ return new AuxiliaryResolveInfo(token, requiresSecondPhase, failureIntent, filters);
+ }
// Hash or filter mis-match; no instant apps for this domain.
return null;
}
+ /**
+ * Returns one of three states: <p/>
+ * <ul>
+ * <li>{@code null} if there are no matches will not be; resolution is unnecessary.</li>
+ * <li>An empty list signifying that a 2nd phase of resolution is required.</li>
+ * <li>A populated list meaning that matches were found and should be sent directly to the
+ * installer</li>
+ * </ul>
+ *
+ */
+ private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters(
+ Intent origIntent, String resolvedType, int userId, String packageName, String token,
+ InstantAppResolveInfo instantAppInfo) {
+ if (instantAppInfo.shouldLetInstallerDecide()) {
+ return Collections.singletonList(
+ new AuxiliaryResolveInfo.AuxiliaryFilter(
+ instantAppInfo, null /* splitName */,
+ instantAppInfo.getExtras()));
+ }
+ if (packageName != null
+ && !packageName.equals(instantAppInfo.getPackageName())) {
+ return null;
+ }
+ final List<InstantAppIntentFilter> instantAppFilters =
+ instantAppInfo.getIntentFilters();
+ if (instantAppFilters == null || instantAppFilters.isEmpty()) {
+ // No filters on web intent; no matches, 2nd phase unnecessary.
+ if (origIntent.isBrowsableWebIntent()) {
+ return null;
+ }
+ // No filters; we need to start phase two
+ if (DEBUG_INSTANT) {
+ Log.d(TAG, "No app filters; go to phase 2");
+ }
+ return Collections.emptyList();
+ }
+ final PackageManagerService.InstantAppIntentResolver instantAppResolver =
+ new PackageManagerService.InstantAppIntentResolver();
+ for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
+ final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
+ final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
+ if (splitFilters == null || splitFilters.isEmpty()) {
+ continue;
+ }
+ for (int k = splitFilters.size() - 1; k >= 0; --k) {
+ IntentFilter filter = splitFilters.get(k);
+ Iterator<IntentFilter.AuthorityEntry> authorities =
+ filter.authoritiesIterator();
+ // ignore http/s-only filters.
+ if ((authorities == null || !authorities.hasNext())
+ && (filter.hasDataScheme("http") || filter.hasDataScheme("https"))
+ && filter.hasAction(Intent.ACTION_VIEW)
+ && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) {
+ continue;
+ }
+ instantAppResolver.addFilter(
+ new AuxiliaryResolveInfo.AuxiliaryFilter(
+ filter,
+ instantAppInfo,
+ instantAppFilter.getSplitName(),
+ instantAppInfo.getExtras()
+ ));
+ }
+ }
+ List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList =
+ instantAppResolver.queryIntent(
+ origIntent, resolvedType, false /*defaultOnly*/, userId);
+ if (!matchedResolveInfoList.isEmpty()) {
+ if (DEBUG_INSTANT) {
+ Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList);
+ }
+ return matchedResolveInfoList;
+ } else if (DEBUG_INSTANT) {
+ Log.d(TAG, "[" + token + "] No matches found"
+ + " package: " + instantAppInfo.getPackageName()
+ + ", versionCode: " + instantAppInfo.getVersionCode());
+ }
+ return null;
+ }
+
private static void logMetrics(int action, long startTime, String token,
@ResolutionStatus int status) {
final LogMaker logMaker = new LogMaker(action)
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index b5ddf8c511f1..a9ee41162ae1 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -37,33 +37,29 @@ import android.util.Slog;
import android.util.TimedRemoteCaller;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.TransferPipe;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeoutException;
/**
- * Represents a remote ephemeral resolver. It is responsible for binding to the remote
+ * Represents a remote instant app resolver. It is responsible for binding to the remote
* service and handling all interactions in a timely manner.
* @hide
*/
-final class EphemeralResolverConnection implements DeathRecipient {
+final class InstantAppResolverConnection implements DeathRecipient {
private static final String TAG = "PackageManager";
// This is running in a critical section and the timeout must be sufficiently low
private static final long BIND_SERVICE_TIMEOUT_MS =
Build.IS_ENG ? 500 : 300;
private static final long CALL_SERVICE_TIMEOUT_MS =
Build.IS_ENG ? 200 : 100;
- private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+ private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
private final Object mLock = new Object();
- private final GetEphemeralResolveInfoCaller mGetEphemeralResolveInfoCaller =
- new GetEphemeralResolveInfoCaller();
+ private final GetInstantAppResolveInfoCaller mGetInstantAppResolveInfoCaller =
+ new GetInstantAppResolveInfoCaller();
private final ServiceConnection mServiceConnection = new MyServiceConnection();
private final Context mContext;
/** Intent used to bind to the service */
@@ -78,14 +74,14 @@ final class EphemeralResolverConnection implements DeathRecipient {
@GuardedBy("mLock")
private IInstantAppResolver mRemoteInstance;
- public EphemeralResolverConnection(
+ public InstantAppResolverConnection(
Context context, ComponentName componentName, String action) {
mContext = context;
mIntent = new Intent(action).setComponent(componentName);
}
- public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[],
- String token) throws ConnectionException {
+ public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(Intent sanitizedIntent,
+ int hashPrefix[], String token) throws ConnectionException {
throwIfCalledOnMainThread();
IInstantAppResolver target = null;
try {
@@ -97,8 +93,8 @@ final class EphemeralResolverConnection implements DeathRecipient {
throw new ConnectionException(ConnectionException.FAILURE_INTERRUPTED);
}
try {
- return mGetEphemeralResolveInfoCaller
- .getEphemeralResolveInfoList(target, hashPrefix, token);
+ return mGetInstantAppResolveInfoCaller
+ .getInstantAppResolveInfoList(target, sanitizedIntent, hashPrefix, token);
} catch (TimeoutException e) {
throw new ConnectionException(ConnectionException.FAILURE_CALL);
} catch (RemoteException ignore) {
@@ -111,26 +107,22 @@ final class EphemeralResolverConnection implements DeathRecipient {
return null;
}
- public final void getInstantAppIntentFilterList(int hashPrefix[], String token,
- String hostName, PhaseTwoCallback callback, Handler callbackHandler,
- final long startTime) throws ConnectionException {
+ public final void getInstantAppIntentFilterList(Intent sanitizedIntent, int hashPrefix[],
+ String token, PhaseTwoCallback callback, Handler callbackHandler, final long startTime)
+ throws ConnectionException {
final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle data) throws RemoteException {
final ArrayList<InstantAppResolveInfo> resolveList =
data.getParcelableArrayList(
InstantAppResolverService.EXTRA_RESOLVE_INFO);
- callbackHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onPhaseTwoResolved(resolveList, startTime);
- }
- });
+ callbackHandler.post(() -> callback.onPhaseTwoResolved(resolveList, startTime));
}
};
try {
getRemoteInstanceLazy(token)
- .getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback);
+ .getInstantAppIntentFilterList(sanitizedIntent, hashPrefix, token,
+ remoteCallback);
} catch (TimeoutException e) {
throw new ConnectionException(ConnectionException.FAILURE_BIND);
} catch (InterruptedException e) {
@@ -174,7 +166,7 @@ final class EphemeralResolverConnection implements DeathRecipient {
if (mBindState == STATE_PENDING) {
// there is a pending bind, let's see if we can use it.
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection");
}
try {
@@ -191,7 +183,7 @@ final class EphemeralResolverConnection implements DeathRecipient {
if (mBindState == STATE_BINDING) {
// someone was binding when we called bind(), or they raced ahead while we were
// waiting in the PENDING case; wait for their result instead. Last chance!
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.i(TAG, "[" + token + "] Another thread is binding; waiting for connection");
}
waitForBindLocked(token);
@@ -209,12 +201,12 @@ final class EphemeralResolverConnection implements DeathRecipient {
IInstantAppResolver instance = null;
try {
if (doUnbind) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.i(TAG, "[" + token + "] Previous connection never established; rebinding");
}
mContext.unbindService(mServiceConnection);
}
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "[" + token + "] Binding to instant app resolver");
}
final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
@@ -250,7 +242,7 @@ final class EphemeralResolverConnection implements DeathRecipient {
@Override
public void binderDied() {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Binder to instant app resolver died");
}
synchronized (mLock) {
@@ -289,7 +281,7 @@ final class EphemeralResolverConnection implements DeathRecipient {
private final class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Connected to instant app resolver");
}
synchronized (mLock) {
@@ -298,7 +290,7 @@ final class EphemeralResolverConnection implements DeathRecipient {
mBindState = STATE_IDLE;
}
try {
- service.linkToDeath(EphemeralResolverConnection.this, 0 /*flags*/);
+ service.linkToDeath(InstantAppResolverConnection.this, 0 /*flags*/);
} catch (RemoteException e) {
handleBinderDiedLocked();
}
@@ -308,7 +300,7 @@ final class EphemeralResolverConnection implements DeathRecipient {
@Override
public void onServiceDisconnected(ComponentName name) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Disconnected from instant app resolver");
}
synchronized (mLock) {
@@ -317,11 +309,11 @@ final class EphemeralResolverConnection implements DeathRecipient {
}
}
- private static final class GetEphemeralResolveInfoCaller
+ private static final class GetInstantAppResolveInfoCaller
extends TimedRemoteCaller<List<InstantAppResolveInfo>> {
private final IRemoteCallback mCallback;
- public GetEphemeralResolveInfoCaller() {
+ public GetInstantAppResolveInfoCaller() {
super(CALL_SERVICE_TIMEOUT_MS);
mCallback = new IRemoteCallback.Stub() {
@Override
@@ -336,11 +328,12 @@ final class EphemeralResolverConnection implements DeathRecipient {
};
}
- public List<InstantAppResolveInfo> getEphemeralResolveInfoList(
- IInstantAppResolver target, int hashPrefix[], String token)
+ public List<InstantAppResolveInfo> getInstantAppResolveInfoList(
+ IInstantAppResolver target, Intent sanitizedIntent, int hashPrefix[], String token)
throws RemoteException, TimeoutException {
final int sequence = onBeforeRemoteCall();
- target.getInstantAppResolveInfoList(hashPrefix, token, sequence, mCallback);
+ target.getInstantAppResolveInfoList(sanitizedIntent, hashPrefix, token, sequence,
+ mCallback);
return getResultTimed(sequence);
}
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 10e05cf34955..fc73142c4858 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -261,12 +261,12 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
@Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
- int targetSdkVersion, @Nullable String profileName)
- throws InstallerException {
+ int targetSdkVersion, @Nullable String profileName,
+ @Nullable String dexMetadataPath) throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 5.
- builder.append("5 ");
+ // The version. Right now it's 6.
+ builder.append("6 ");
builder.append("dexopt");
@@ -284,6 +284,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
encodeParameter(builder, downgrade);
encodeParameter(builder, targetSdkVersion);
encodeParameter(builder, profileName);
+ encodeParameter(builder, dexMetadataPath);
commands.add(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cde8cb740590..2c68e67a3bd5 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.dex.ArtManager;
+import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -209,6 +210,13 @@ public class PackageDexOptimizer {
String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]);
+ String dexMetadataPath = null;
+ if (options.isDexoptInstallWithDexMetadata()) {
+ File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
+ dexMetadataPath = dexMetadataFile == null
+ ? null : dexMetadataFile.getAbsolutePath();
+ }
+
final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
|| packageUseInfo.isUsedByOtherApps(path);
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
@@ -223,7 +231,7 @@ public class PackageDexOptimizer {
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
- packageStats, options.isDowngrade(), profileName);
+ packageStats, options.isDowngrade(), profileName, dexMetadataPath);
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
@@ -248,7 +256,7 @@ public class PackageDexOptimizer {
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
String compilerFilter, boolean profileUpdated, String classLoaderContext,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
- String profileName) {
+ String profileName, String dexMetadataPath) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -275,7 +283,7 @@ public class PackageDexOptimizer {
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
- profileName);
+ profileName, dexMetadataPath);
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -396,7 +404,8 @@ public class PackageDexOptimizer {
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
- options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null);
+ options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
+ /*dexMetadataPath*/ null);
}
return DEX_OPT_PERFORMED;
@@ -511,9 +520,13 @@ public class PackageDexOptimizer {
private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- // Profile guide compiled oat files should not be public.
+ // Profile guide compiled oat files should not be public unles they are based
+ // on profiles from dex metadata archives.
+ // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
+ // the user does not have an existing profile.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
+ boolean isPublic = !info.isForwardLocked() &&
+ (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// System apps are invoked with a runtime flag which exempts them from
// restrictions on hidden API usage. We dexopt with the same runtime flag
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a6ff4f7e5f70..3dd5a3415e35 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -77,6 +77,7 @@ import android.os.RevocableFileDescriptor;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
+import android.system.Int64Ref;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
@@ -575,14 +576,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
try {
- return openWriteInternal(name, offsetBytes, lengthBytes);
+ return doWriteInternal(name, offsetBytes, lengthBytes, null);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
- private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
- throws IOException {
+ @Override
+ public void write(String name, long offsetBytes, long lengthBytes,
+ ParcelFileDescriptor fd) {
+ try {
+ doWriteInternal(name, offsetBytes, lengthBytes, fd);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
+ ParcelFileDescriptor incomingFd) throws IOException {
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
@@ -636,7 +647,44 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
- if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ if (incomingFd != null) {
+ switch (Binder.getCallingUid()) {
+ case android.os.Process.SHELL_UID:
+ case android.os.Process.ROOT_UID:
+ break;
+ default:
+ throw new SecurityException("Reverse mode only supported from shell");
+ }
+
+ // In "reverse" mode, we're streaming data ourselves from the
+ // incoming FD, which means we never have to hand out our
+ // sensitive internal FD. We still rely on a "bridge" being
+ // inserted above to hold the session active.
+ try {
+ final Int64Ref last = new Int64Ref(0);
+ FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> {
+ if (params.sizeBytes > 0) {
+ final long delta = progress - last.value;
+ last.value = progress;
+ addClientProgress((float) delta / (float) params.sizeBytes);
+ }
+ }, null, lengthBytes);
+ } finally {
+ IoUtils.closeQuietly(targetFd);
+ IoUtils.closeQuietly(incomingFd);
+
+ // We're done here, so remove the "bridge" that was holding
+ // the session active.
+ synchronized (mLock) {
+ if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ mFds.remove(fd);
+ } else {
+ mBridges.remove(bridge);
+ }
+ }
+ }
+ return null;
+ } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd.init(mContext, targetFd);
return fd.getRevocableFileDescriptor();
} else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 01c44af586ed..a0cb7227ad9f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -423,7 +423,7 @@ public class PackageManagerService extends IPackageManager.Stub
public static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
- private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+ private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_TRIAGED_MISSING = false;
private static final boolean DEBUG_APP_DATA = false;
@@ -582,6 +582,7 @@ public class PackageManagerService extends IPackageManager.Stub
sBrowserIntent.setAction(Intent.ACTION_VIEW);
sBrowserIntent.addCategory(Intent.CATEGORY_BROWSABLE);
sBrowserIntent.setData(Uri.parse("http:"));
+ sBrowserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL);
}
/**
@@ -980,7 +981,7 @@ public class PackageManagerService extends IPackageManager.Stub
private int mIntentFilterVerificationToken = 0;
/** The service connection to the ephemeral resolver */
- final EphemeralResolverConnection mInstantAppResolverConnection;
+ final InstantAppResolverConnection mInstantAppResolverConnection;
/** Component used to show resolver settings for Instant Apps */
final ComponentName mInstantAppResolverSettingsComponent;
@@ -3148,10 +3149,10 @@ Slog.e("TODD",
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
if (instantAppResolverComponent != null) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
}
- mInstantAppResolverConnection = new EphemeralResolverConnection(
+ mInstantAppResolverConnection = new InstantAppResolverConnection(
mContext, instantAppResolverComponent.first,
instantAppResolverComponent.second);
mInstantAppResolverSettingsComponent =
@@ -3531,7 +3532,7 @@ Slog.e("TODD",
final String[] packageArray =
mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
}
return null;
@@ -3546,19 +3547,9 @@ Slog.e("TODD",
final Intent resolverIntent = new Intent(actionName);
List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
- // temporarily look for the old action
- if (resolvers.size() == 0) {
- if (DEBUG_EPHEMERAL) {
- Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
- }
- actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE;
- resolverIntent.setAction(actionName);
- resolvers = queryIntentServicesInternal(resolverIntent, null,
- resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
- }
final int N = resolvers.size();
if (N == 0) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters");
}
return null;
@@ -3574,44 +3565,53 @@ Slog.e("TODD",
final String packageName = info.serviceInfo.packageName;
if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
+ " pkg: " + packageName + ", info:" + info);
}
continue;
}
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "Ephemeral resolver found;"
+ " pkg: " + packageName + ", info:" + info);
}
return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
}
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "Ephemeral resolver NOT found");
}
return null;
}
private @Nullable ActivityInfo getInstantAppInstallerLPr() {
- final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+ String[] orderedActions = Build.IS_ENG
+ ? new String[]{
+ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST",
+ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}
+ : new String[]{
+ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE};
final int resolveFlags =
MATCH_DIRECT_BOOT_AWARE
- | MATCH_DIRECT_BOOT_UNAWARE
- | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
- List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
- resolveFlags, UserHandle.USER_SYSTEM);
- // temporarily look for the old action
- if (matches.isEmpty()) {
- if (DEBUG_EPHEMERAL) {
- Slog.d(TAG, "Ephemeral installer not found with new action; try old one");
- }
- intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
+ | MATCH_DIRECT_BOOT_UNAWARE
+ | Intent.FLAG_IGNORE_EPHEMERAL
+ | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0);
+ final Intent intent = new Intent();
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+ List<ResolveInfo> matches = null;
+ for (String action : orderedActions) {
+ intent.setAction(action);
matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
resolveFlags, UserHandle.USER_SYSTEM);
+ if (matches.isEmpty()) {
+ if (DEBUG_INSTANT) {
+ Slog.d(TAG, "Instant App installer not found with " + action);
+ }
+ } else {
+ break;
+ }
}
Iterator<ResolveInfo> iter = matches.iterator();
while (iter.hasNext()) {
@@ -3619,7 +3619,8 @@ Slog.e("TODD",
final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName);
if (ps != null) {
final PermissionsState permissionsState = ps.getPermissionsState();
- if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)) {
+ if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)
+ || Build.IS_ENG) {
continue;
}
}
@@ -3643,15 +3644,6 @@ Slog.e("TODD",
final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
UserHandle.USER_SYSTEM);
- // temporarily look for the old action
- if (matches.isEmpty()) {
- if (DEBUG_EPHEMERAL) {
- Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one");
- }
- intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS);
- matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
- UserHandle.USER_SYSTEM);
- }
if (matches.isEmpty()) {
return null;
}
@@ -3848,10 +3840,6 @@ Slog.e("TODD",
if (ps == null) {
return null;
}
- PackageParser.Package p = ps.pkg;
- if (p == null) {
- return null;
- }
final int callingUid = Binder.getCallingUid();
// Filter out ephemeral app metadata:
// * The system/shell/root can see metadata for any app
@@ -3863,32 +3851,58 @@ Slog.e("TODD",
return null;
}
- final PermissionsState permissionsState = ps.getPermissionsState();
-
- // Compute GIDs only if requested
- final int[] gids = (flags & PackageManager.GET_GIDS) == 0
- ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
- // Compute granted permissions only if package has requested permissions
- final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
- ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
- final PackageUserState state = ps.readUserState(userId);
-
if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
&& ps.isSystem()) {
flags |= MATCH_ANY_USER;
}
- PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
+ final PackageUserState state = ps.readUserState(userId);
+ PackageParser.Package p = ps.pkg;
+ if (p != null) {
+ final PermissionsState permissionsState = ps.getPermissionsState();
- if (packageInfo == null) {
- return null;
- }
+ // Compute GIDs only if requested
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0
+ ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
+ // Compute granted permissions only if package has requested permissions
+ final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
+ ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
- packageInfo.packageName = packageInfo.applicationInfo.packageName =
- resolveExternalPackageNameLPr(p);
+ PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
- return packageInfo;
+ if (packageInfo == null) {
+ return null;
+ }
+
+ packageInfo.packageName = packageInfo.applicationInfo.packageName =
+ resolveExternalPackageNameLPr(p);
+
+ return packageInfo;
+ } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) {
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = ps.name;
+ pi.setLongVersionCode(ps.versionCode);
+ pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null;
+ pi.firstInstallTime = ps.firstInstallTime;
+ pi.lastUpdateTime = ps.lastUpdateTime;
+
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = ps.name;
+ ai.uid = UserHandle.getUid(userId, ps.appId);
+ ai.primaryCpuAbi = ps.primaryCpuAbiString;
+ ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
+ ai.versionCode = ps.versionCode;
+ ai.flags = ps.pkgFlags;
+ ai.privateFlags = ps.pkgPrivateFlags;
+ pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId);
+
+ if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ + ps.name + "]. Provides a minimum info.");
+ return pi;
+ } else {
+ return null;
+ }
}
@Override
@@ -4778,10 +4792,7 @@ Slog.e("TODD",
flags |= PackageManager.MATCH_INSTANT;
} else {
final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0;
- final boolean allowMatchInstant =
- (wantInstantApps
- && Intent.ACTION_VIEW.equals(intent.getAction())
- && hasWebURI(intent))
+ final boolean allowMatchInstant = wantInstantApps
|| (wantMatchInstant && canViewInstantApps(callingUid, userId));
flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY
| PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY);
@@ -5971,8 +5982,14 @@ Slog.e("TODD",
if (!skipPackageCheck && intent.getPackage() != null) {
return false;
}
- final boolean isWebUri = hasWebURI(intent);
- if (!isWebUri || intent.getData().getHost() == null) {
+ if (!intent.isBrowsableWebIntent()) {
+ // for non web intents, we should not resolve externally if an app already exists to
+ // handle it or if the caller didn't explicitly request it.
+ if ((resolvedActivities != null && resolvedActivities.size() != 0)
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
+ return false;
+ }
+ } else if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
return false;
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
@@ -5991,7 +6008,7 @@ Slog.e("TODD",
final int status = (int) (packedStatus >> 32);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
|| status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app;"
+ " pkg: " + packageName + ", status: " + status);
}
@@ -5999,7 +6016,7 @@ Slog.e("TODD",
}
}
if (ps.getInstantApp(userId)) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app installed;"
+ " pkg: " + packageName);
}
@@ -6370,7 +6387,7 @@ Slog.e("TODD",
if (matches.get(i).getTargetUserId() == targetUserId) return true;
}
}
- if (hasWebURI(intent)) {
+ if (intent.hasWebURI()) {
// cross-profile app linking works only towards the parent.
final int callingUid = Binder.getCallingUid();
final UserInfo parent = getProfileParent(sourceUserId);
@@ -6545,7 +6562,7 @@ Slog.e("TODD",
sortResult = true;
}
}
- if (hasWebURI(intent)) {
+ if (intent.hasWebURI()) {
CrossProfileDomainInfo xpDomainInfo = null;
final UserInfo parent = getProfileParent(userId);
if (parent != null) {
@@ -6631,12 +6648,11 @@ Slog.e("TODD",
if (ps.getInstantApp(userId)) {
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int)(packedStatus >> 32);
- final int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
// there's a local instant application installed, but, the user has
// chosen to never use it; skip resolution and don't acknowledge
// an instant application is even available
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
}
blockResolution = true;
@@ -6644,7 +6660,7 @@ Slog.e("TODD",
} else {
// we have a locally installed instant application; skip resolution
// but acknowledge there's an instant application available
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
}
localInstantApp = info;
@@ -6663,9 +6679,8 @@ Slog.e("TODD",
null /*responseObj*/, intent /*origIntent*/, resolvedType,
null /*callingPackage*/, userId, null /*verificationBundle*/,
resolveForStart);
- auxiliaryResponse =
- InstantAppResolver.doInstantAppResolutionPhaseOne(
- mContext, mInstantAppResolverConnection, requestObject);
+ auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
+ mInstantAppResolverConnection, requestObject);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
} else {
// we have an instant application locally, but, we can't admit that since
@@ -6674,35 +6689,40 @@ Slog.e("TODD",
// instant application available externally. when it comes time to start
// the instant application, we'll do the right thing.
final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
- auxiliaryResponse = new AuxiliaryResolveInfo(
- ai.packageName, null /*splitName*/, null /*failureActivity*/,
- ai.versionCode, null /*failureIntent*/);
+ auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
+ ai.packageName, ai.versionCode, null /* splitName */);
}
}
- if (auxiliaryResponse != null) {
- if (DEBUG_EPHEMERAL) {
- Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
- }
- final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
- final PackageSetting ps =
- mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
- if (ps != null) {
- ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
- mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
- ephemeralInstaller.activityInfo.launchToken = auxiliaryResponse.token;
- ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
- // make sure this resolver is the default
- ephemeralInstaller.isDefault = true;
- ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
- | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
- // add a non-generic filter
- ephemeralInstaller.filter = new IntentFilter(intent.getAction());
- ephemeralInstaller.filter.addDataPath(
- intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
- ephemeralInstaller.isInstantAppAvailable = true;
- result.add(ephemeralInstaller);
- }
+ if (intent.isBrowsableWebIntent() && auxiliaryResponse == null) {
+ return result;
}
+ final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
+ if (ps == null) {
+ return result;
+ }
+ final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
+ ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
+ mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
+ ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // add a non-generic filter
+ ephemeralInstaller.filter = new IntentFilter();
+ if (intent.getAction() != null) {
+ ephemeralInstaller.filter.addAction(intent.getAction());
+ }
+ if (intent.getData() != null && intent.getData().getPath() != null) {
+ ephemeralInstaller.filter.addDataPath(
+ intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
+ }
+ ephemeralInstaller.isInstantAppAvailable = true;
+ // make sure this resolver is the default
+ ephemeralInstaller.isDefault = true;
+ ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+
+ result.add(ephemeralInstaller);
return result;
}
@@ -6817,10 +6837,11 @@ Slog.e("TODD",
final ResolveInfo info = resolveInfos.get(i);
// allow activities that are defined in the provided package
if (allowDynamicSplits
+ && info.activityInfo != null
&& info.activityInfo.splitName != null
&& !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
info.activityInfo.splitName)) {
- if (mInstantAppInstallerInfo == null) {
+ if (mInstantAppInstallerActivity == null) {
if (DEBUG_INSTALL) {
Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
}
@@ -6832,14 +6853,15 @@ Slog.e("TODD",
if (DEBUG_INSTALL) {
Slog.v(TAG, "Adding installer to the ResolveInfo list");
}
- final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ final ResolveInfo installerInfo = new ResolveInfo(
+ mInstantAppInstallerInfo);
final ComponentName installFailureActivity = findInstallFailureActivity(
info.activityInfo.packageName, filterCallingUid, userId);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
- info.activityInfo.packageName, info.activityInfo.splitName,
installFailureActivity,
+ info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode,
- null /*failureIntent*/);
+ info.activityInfo.splitName);
installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
@@ -6856,6 +6878,7 @@ Slog.e("TODD",
installerInfo.priority = info.priority;
installerInfo.preferredOrder = info.preferredOrder;
installerInfo.isDefault = info.isDefault;
+ installerInfo.isInstantAppAvailable = true;
resolveInfos.set(i, installerInfo);
continue;
}
@@ -6913,17 +6936,6 @@ Slog.e("TODD",
return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
}
- private static boolean hasWebURI(Intent intent) {
- if (intent.getData() == null) {
- return false;
- }
- final String scheme = intent.getScheme();
- if (TextUtils.isEmpty(scheme)) {
- return false;
- }
- return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
- }
-
private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
@@ -7593,14 +7605,16 @@ Slog.e("TODD",
info.serviceInfo.splitName)) {
// requested service is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
- final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ final ResolveInfo installerInfo = new ResolveInfo(
+ mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
- info.serviceInfo.packageName, info.serviceInfo.splitName,
- null /*failureActivity*/, info.serviceInfo.applicationInfo.versionCode,
- null /*failureIntent*/);
+ null /* installFailureActivity */,
+ info.serviceInfo.packageName,
+ info.serviceInfo.applicationInfo.versionCode,
+ info.serviceInfo.splitName);
// make sure this resolver is the default
installerInfo.isDefault = true;
installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -7713,14 +7727,16 @@ Slog.e("TODD",
info.providerInfo.splitName)) {
// requested provider is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
- final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ final ResolveInfo installerInfo = new ResolveInfo(
+ mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
- info.providerInfo.packageName, info.providerInfo.splitName,
- null /*failureActivity*/, info.providerInfo.applicationInfo.versionCode,
- null /*failureIntent*/);
+ null /*failureActivity*/,
+ info.providerInfo.packageName,
+ info.providerInfo.applicationInfo.versionCode,
+ info.providerInfo.splitName);
// make sure this resolver is the default
installerInfo.isDefault = true;
installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -9991,8 +10007,7 @@ Slog.e("TODD",
// priv-apps.
synchronized (mPackages) {
PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
- if (!pkg.packageName.equals("android")
- && (compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
+ if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
@@ -10459,7 +10474,20 @@ Slog.e("TODD",
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- SELinuxMMAC.assignSeInfoValue(pkg);
+ // SELinux sandboxes become more restrictive as targetSdkVersion increases.
+ // To ensure that apps with sharedUserId are placed in the same selinux domain
+ // without breaking any assumptions about access, put them into the least
+ // restrictive targetSdkVersion=25 domain.
+ // TODO(b/72290969): Base this on the actual targetSdkVersion(s) of the apps within the
+ // sharedUserSetting, instead of defaulting to the least restrictive domain.
+ final int targetSdk = (sharedUserSetting != null) ? 25
+ : pkg.applicationInfo.targetSdkVersion;
+ // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
+ // They currently can be if the sharedUser apps are signed with the platform key.
+ final boolean isPrivileged = (sharedUserSetting != null) ?
+ sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+
+ SELinuxMMAC.assignSeInfoValue(pkg, isPrivileged, targetSdk);
pkg.mExtras = pkgSetting;
pkg.applicationInfo.processName = fixProcessName(
@@ -11776,14 +11804,14 @@ Slog.e("TODD",
private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
if (installerActivity == null) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Clear ephemeral installer activity");
}
mInstantAppInstallerActivity = null;
return;
}
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.d(TAG, "Set ephemeral installer activity: "
+ installerActivity.getComponentName());
}
@@ -11794,7 +11822,7 @@ Slog.e("TODD",
mInstantAppInstallerActivity.exported = true;
mInstantAppInstallerActivity.enabled = true;
mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity;
- mInstantAppInstallerInfo.priority = 0;
+ mInstantAppInstallerInfo.priority = 1;
mInstantAppInstallerInfo.preferredOrder = 1;
mInstantAppInstallerInfo.isDefault = true;
mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -13243,8 +13271,9 @@ Slog.e("TODD",
private int mFlags;
}
- static final class EphemeralIntentResolver
- extends IntentResolver<AuxiliaryResolveInfo, AuxiliaryResolveInfo> {
+ static final class InstantAppIntentResolver
+ extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter,
+ AuxiliaryResolveInfo.AuxiliaryFilter> {
/**
* The result that has the highest defined order. Ordering applies on a
* per-package basis. Mapping is from package name to Pair of order and
@@ -13259,18 +13288,19 @@ Slog.e("TODD",
final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult = new ArrayMap<>();
@Override
- protected AuxiliaryResolveInfo[] newArray(int size) {
- return new AuxiliaryResolveInfo[size];
+ protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) {
+ return new AuxiliaryResolveInfo.AuxiliaryFilter[size];
}
@Override
- protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo responseObj) {
+ protected boolean isPackageForFilter(String packageName,
+ AuxiliaryResolveInfo.AuxiliaryFilter responseObj) {
return true;
}
@Override
- protected AuxiliaryResolveInfo newResult(AuxiliaryResolveInfo responseObj, int match,
- int userId) {
+ protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(
+ AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) {
if (!sUserManager.exists(userId)) {
return null;
}
@@ -13291,7 +13321,7 @@ Slog.e("TODD",
}
@Override
- protected void filterResults(List<AuxiliaryResolveInfo> results) {
+ protected void filterResults(List<AuxiliaryResolveInfo.AuxiliaryFilter> results) {
// only do work if ordering is enabled [most of the time it won't be]
if (mOrderResult.size() == 0) {
return;
@@ -13611,7 +13641,7 @@ Slog.e("TODD",
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
PackageParser.SigningDetails signingDetails) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
Slog.d(TAG, "Ephemeral install of " + packageName);
}
@@ -15095,7 +15125,7 @@ Slog.e("TODD",
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
- if (DEBUG_EPHEMERAL && ephemeral) {
+ if (DEBUG_INSTANT && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
}
@@ -15162,7 +15192,7 @@ Slog.e("TODD",
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
- if (DEBUG_EPHEMERAL) {
+ if (DEBUG_INSTANT) {
Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
}
installFlags |= PackageManager.INSTALL_INSTANT_APP;
@@ -17301,7 +17331,8 @@ Slog.e("TODD",
// Also, don't fail application installs if the dexopt step fails.
DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE);
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index c02331da4ed6..5060c4df655e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -693,7 +693,7 @@ public class PackageManagerServiceUtils {
InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
) {
- Streams.copy(fileIn, fileOut);
+ FileUtils.copy(fileIn, fileOut);
Os.chmod(dstFile.getAbsolutePath(), 0644);
return PackageManager.INSTALL_SUCCEEDED;
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 686c4a5eb321..758c9d56d32a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -16,6 +16,12 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+
import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -32,8 +38,10 @@ import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
@@ -41,9 +49,6 @@ import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
@@ -73,7 +78,6 @@ import android.util.PrintWriterPrinter;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.SizedInputStream;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
@@ -81,9 +85,8 @@ import dalvik.system.DexFile;
import libcore.io.IoUtils;
+import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
@@ -95,12 +98,6 @@ import java.util.WeakHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-
class PackageManagerShellCommand extends ShellCommand {
/** Path for streaming APK content */
private static final String STDIN_PATH = "-";
@@ -2213,7 +2210,7 @@ class PackageManagerShellCommand extends ShellCommand {
final PrintWriter pw = getOutPrintWriter();
final ParcelFileDescriptor fd;
if (STDIN_PATH.equals(inPath)) {
- fd = null;
+ fd = new ParcelFileDescriptor(getInFileDescriptor());
} else if (inPath != null) {
fd = openFileForSystem(inPath, "r");
if (fd == null) {
@@ -2225,53 +2222,27 @@ class PackageManagerShellCommand extends ShellCommand {
return -1;
}
} else {
- fd = null;
+ fd = new ParcelFileDescriptor(getInFileDescriptor());
}
if (sizeBytes <= 0) {
getErrPrintWriter().println("Error: must specify a APK size");
return 1;
}
- final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
-
PackageInstaller.Session session = null;
- InputStream in = null;
- OutputStream out = null;
try {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
-
- if (fd != null) {
- in = new ParcelFileDescriptor.AutoCloseInputStream(fd);
- } else {
- in = new SizedInputStream(getRawInputStream(), sizeBytes);
- }
- out = session.openWrite(splitName, 0, sizeBytes);
-
- int total = 0;
- byte[] buffer = new byte[1024 * 1024];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
-
- if (info.sizeBytes > 0) {
- final float fraction = ((float) c / (float) info.sizeBytes);
- session.addProgress(fraction);
- }
- }
- session.fsync(out);
+ session.write(splitName, 0, sizeBytes, fd);
if (logSuccess) {
- pw.println("Success: streamed " + total + " bytes");
+ pw.println("Success: streamed " + sizeBytes + " bytes");
}
return 0;
} catch (IOException e) {
getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
return 1;
} finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
IoUtils.closeQuietly(session);
}
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index fe5b3d456b87..a9f15282133f 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -315,7 +315,8 @@ public final class SELinuxMMAC {
*
* @param pkg object representing the package to be labeled.
*/
- public static void assignSeInfoValue(PackageParser.Package pkg) {
+ public static void assignSeInfoValue(PackageParser.Package pkg, boolean isPrivileged,
+ int targetSdkVersion) {
synchronized (sPolicies) {
if (!sPolicyRead) {
if (DEBUG_POLICY) {
@@ -335,10 +336,11 @@ public final class SELinuxMMAC {
if (pkg.applicationInfo.targetSandboxVersion == 2)
pkg.applicationInfo.seInfo += SANDBOX_V2_STR;
- if (pkg.applicationInfo.isPrivilegedApp())
+ if (isPrivileged) {
pkg.applicationInfo.seInfo += PRIVILEGED_APP_STR;
+ }
- pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + pkg.applicationInfo.targetSdkVersion;
+ pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + targetSdkVersion;
if (DEBUG_POLICY_INSTALL) {
Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ebf6672cf57e..e4c74edf0f0f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -703,7 +703,7 @@ class ShortcutPackage extends ShortcutPackageItem {
*/
public boolean rescanPackageIfNeeded(boolean isNewApp, boolean forceRescan) {
final ShortcutService s = mShortcutUser.mService;
- final long start = s.injectElapsedRealtime();
+ final long start = s.getStatStartTime();
final PackageInfo pi;
try {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d2bc6d24e2e9..a85d6d838045 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -99,6 +99,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.StatLogger;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -367,7 +368,7 @@ public class ShortcutService extends IShortcutService.Stub {
int COUNT = GET_DEFAULT_LAUNCHER + 1;
}
- private static final String[] STAT_LABELS = {
+ private final StatLogger mStatLogger = new StatLogger(new String[] {
"getHomeActivities()",
"Launcher permission check",
"getPackageInfo()",
@@ -385,15 +386,7 @@ public class ShortcutService extends IShortcutService.Stub {
"packageUpdateCheck",
"asyncPreloadUserDelay",
"getDefaultLauncher()"
- };
-
- final Object mStatLock = new Object();
-
- @GuardedBy("mStatLock")
- private final int[] mCountStats = new int[Stats.COUNT];
-
- @GuardedBy("mStatLock")
- private final long[] mDurationStats = new long[Stats.COUNT];
+ });
private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
@@ -480,11 +473,12 @@ public class ShortcutService extends IShortcutService.Stub {
| ActivityManager.UID_OBSERVER_GONE);
}
+ long getStatStartTime() {
+ return mStatLogger.getTime();
+ }
+
void logDurationStat(int statId, long start) {
- synchronized (mStatLock) {
- mCountStats[statId]++;
- mDurationStats[statId] += (injectElapsedRealtime() - start);
- }
+ mStatLogger.logDurationStat(statId, start);
}
public String injectGetLocaleTagsForUser(@UserIdInt int userId) {
@@ -621,7 +615,7 @@ public class ShortcutService extends IShortcutService.Stub {
// late since the launcher would already have started.
// So we just create a new thread. This code runs rarely, so we don't use a thread pool
// or anything.
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
injectRunOnNewThread(() -> {
synchronized (mLock) {
logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
@@ -1289,7 +1283,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG) {
Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -1485,7 +1479,7 @@ public class ShortcutService extends IShortcutService.Stub {
final Resources publisherRes = injectGetResourcesForApplicationAsUser(
si.getPackage(), si.getUserId());
if (publisherRes != null) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
si.lookupAndFillInResourceNames(publisherRes);
} finally {
@@ -2264,7 +2258,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (canSeeAnyPinnedShortcut(callingPackage, userId, callingPid, callingUid)) {
return true;
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return hasShortcutHostPermissionInner(callingPackage, userId);
} finally {
@@ -2327,7 +2321,7 @@ public class ShortcutService extends IShortcutService.Stub {
@Nullable
ComponentName getDefaultLauncher(@UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
synchronized (mLock) {
@@ -2338,7 +2332,7 @@ public class ShortcutService extends IShortcutService.Stub {
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
// Default launcher from package manager.
- final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
+ final long startGetHomeActivitiesAsUser = getStatStartTime();
final ComponentName defaultLauncher = mPackageManagerInternal
.getHomeActivitiesAsUser(allHomeCandidates, userId);
logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
@@ -2910,7 +2904,7 @@ public class ShortcutService extends IShortcutService.Stub {
return;
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
@@ -3087,7 +3081,7 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
boolean getSignatures) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getPackageInfo(
@@ -3122,7 +3116,7 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
ApplicationInfo injectApplicationInfoWithUninstalled(
String packageName, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
@@ -3153,7 +3147,7 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(
ComponentName activity, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getActivityInfo(activity,
@@ -3175,7 +3169,7 @@ public class ShortcutService extends IShortcutService.Stub {
@NonNull
@VisibleForTesting
final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId);
@@ -3280,7 +3274,7 @@ public class ShortcutService extends IShortcutService.Stub {
@Nullable
Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mContext.getPackageManager().getResourcesForApplicationAsUser(
@@ -3348,7 +3342,7 @@ public class ShortcutService extends IShortcutService.Stub {
*/
@Nullable
ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
final List<ResolveInfo> resolved =
queryActivities(getMainActivityIntent(), packageName, null, userId);
@@ -3362,7 +3356,7 @@ public class ShortcutService extends IShortcutService.Stub {
* Return whether an activity is enabled, exported and main.
*/
boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
if (activity == null) {
wtf("null activity detected");
@@ -3397,7 +3391,7 @@ public class ShortcutService extends IShortcutService.Stub {
*/
@NonNull
List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return queryActivities(getMainActivityIntent(), packageName, null, userId);
} finally {
@@ -3411,7 +3405,7 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
boolean injectIsActivityEnabledAndExported(
@NonNull ComponentName activity, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
.size() > 0;
@@ -3831,12 +3825,7 @@ public class ShortcutService extends IShortcutService.Stub {
pw.println(mMaxShortcuts);
pw.println();
- pw.println(" Stats:");
- synchronized (mStatLock) {
- for (int i = 0; i < Stats.COUNT; i++) {
- dumpStatLS(pw, " ", i);
- }
- }
+ mStatLogger.dump(pw, " ");
pw.println();
pw.print(" #Failures: ");
@@ -3902,15 +3891,6 @@ public class ShortcutService extends IShortcutService.Stub {
pw.print(formatTime(injectCurrentTimeMillis()));
}
- private void dumpStatLS(PrintWriter pw, String prefix, int statId) {
- pw.print(prefix);
- final int count = mCountStats[statId];
- final long dur = mDurationStats[statId];
- pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
- STAT_LABELS[statId], count, dur,
- (count == 0 ? 0 : ((double) dur) / count)));
- }
-
/**
* Dumpsys for checkin.
*
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 92fd9041b32d..b53d83b1291c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -88,6 +88,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -387,7 +388,9 @@ public class UserManagerService extends IUserManager.Stub {
}
final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
- setQuietModeEnabled(userHandle, false, target);
+ // Call setQuietModeEnabled on bg thread to avoid ANR
+ BackgroundThread.getHandler()
+ .post(() -> setQuietModeEnabled(userHandle, false, target));
}
};
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index a42fcbdd94a0..842f8d0a42f5 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -117,7 +117,7 @@ public class UserRestrictionsUtils {
UserManager.DISALLOW_AUTOFILL,
UserManager.DISALLOW_USER_SWITCH,
UserManager.DISALLOW_UNIFIED_PASSWORD,
- UserManager.DISALLOW_CONFIG_LOCATION_MODE,
+ UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_CONFIG_BRIGHTNESS,
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index 0966770d4897..d4f95cb6b99f 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -59,6 +59,10 @@ public final class DexoptOptions {
// When set, indicates that dexopt is invoked from the background service.
public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+ // When set, indicates that dexopt is invoked from the install time flow and
+ // should get the dex metdata file if present.
+ public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
+
// The name of package to optimize.
private final String mPackageName;
@@ -90,7 +94,8 @@ public final class DexoptOptions {
DEXOPT_ONLY_SHARED_DEX |
DEXOPT_DOWNGRADE |
DEXOPT_AS_SHARED_LIBRARY |
- DEXOPT_IDLE_BACKGROUND_JOB;
+ DEXOPT_IDLE_BACKGROUND_JOB |
+ DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
if ((flags & (~validityMask)) != 0) {
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
}
@@ -141,6 +146,10 @@ public final class DexoptOptions {
return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
}
+ public boolean isDexoptInstallWithDexMetadata() {
+ return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
+ }
+
public String getSplitName() {
return mSplitName;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0502848d698e..177d6af489b3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5603,9 +5603,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final int fl = PolicyControl.getWindowFlags(null,
mTopFullscreenOpaqueWindowState.getAttrs());
if (localLOGV) {
- Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
- + " shown position: "
- + mTopFullscreenOpaqueWindowState.getShownPositionLw());
+ Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(fl));
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9c4c5c8138f..3af3fcbbf7a8 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -232,14 +232,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
public Rect getFrameLw();
/**
- * Retrieve the current position of the window that is actually shown.
- * Must be called with the window manager lock held.
- *
- * @return Point The point holding the shown window position.
- */
- public Point getShownPositionLw();
-
- /**
* Retrieve the frame of the display that this window was last
* laid out in. Must be called with the
* window manager lock held.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 946635043c12..b0b07ea767f6 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -32,6 +32,8 @@ import java.io.PrintWriter;
/**
* This class keeps track of battery drain rate.
*
+ * TODO: The use of the terms "percent" and "level" in this class is not standard. Fix it.
+ *
* Test:
atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
*/
@@ -96,8 +98,12 @@ public class BatterySavingStats {
public int startBatteryLevel;
public int endBatteryLevel;
+ public int startBatteryPercent;
+ public int endBatteryPercent;
+
public long totalTimeMillis;
public int totalBatteryDrain;
+ public int totalBatteryDrainPercent;
public long totalMinutes() {
return totalTimeMillis / 60_000;
@@ -110,14 +116,26 @@ public class BatterySavingStats {
return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000));
}
+ public double drainPercentPerHour() {
+ if (totalTimeMillis == 0) {
+ return 0;
+ }
+ return (double) totalBatteryDrainPercent / (totalTimeMillis / (60.0 * 60 * 1000));
+ }
+
@VisibleForTesting
String toStringForTest() {
return "{" + totalMinutes() + "m," + totalBatteryDrain + ","
- + String.format("%.2f", drainPerHour()) + "}";
+ + String.format("%.2f", drainPerHour()) + "uA/H,"
+ + String.format("%.2f", drainPercentPerHour()) + "%"
+ + "}";
}
}
@VisibleForTesting
+ static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_";
+
+ @VisibleForTesting
static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_";
@VisibleForTesting
@@ -166,6 +184,9 @@ public class BatterySavingStats {
private BatteryManagerInternal getBatteryManagerInternal() {
if (mBatteryManagerInternal == null) {
mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ if (mBatteryManagerInternal == null) {
+ Slog.wtf(TAG, "BatteryManagerInternal not initialized");
+ }
}
return mBatteryManagerInternal;
}
@@ -229,12 +250,20 @@ public class BatterySavingStats {
int injectBatteryLevel() {
final BatteryManagerInternal bmi = getBatteryManagerInternal();
if (bmi == null) {
- Slog.wtf(TAG, "BatteryManagerInternal not initialized");
return 0;
}
return bmi.getBatteryChargeCounter();
}
+ @VisibleForTesting
+ int injectBatteryPercent() {
+ final BatteryManagerInternal bmi = getBatteryManagerInternal();
+ if (bmi == null) {
+ return 0;
+ }
+ return bmi.getBatteryLevel();
+ }
+
/**
* Called from the outside whenever any of the states changes, when the device is not plugged
* in.
@@ -262,33 +291,39 @@ public class BatterySavingStats {
}
final long now = injectCurrentTime();
final int batteryLevel = injectBatteryLevel();
+ final int batteryPercent = injectBatteryPercent();
- endLastStateLocked(now, batteryLevel);
- startNewStateLocked(newState, now, batteryLevel);
- mMetricsLoggerHelper.transitionState(newState, now, batteryLevel);
+ endLastStateLocked(now, batteryLevel, batteryPercent);
+ startNewStateLocked(newState, now, batteryLevel, batteryPercent);
+ mMetricsLoggerHelper.transitionState(newState, now, batteryLevel, batteryPercent);
}
- private void endLastStateLocked(long now, int batteryLevel) {
+ private void endLastStateLocked(long now, int batteryLevel, int batteryPercent) {
if (mCurrentState < 0) {
return;
}
final Stat stat = getStat(mCurrentState);
stat.endBatteryLevel = batteryLevel;
+ stat.endBatteryPercent = batteryPercent;
stat.endTime = now;
final long deltaTime = stat.endTime - stat.startTime;
final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel;
+ final int deltaPercent = stat.startBatteryPercent - stat.endBatteryPercent;
stat.totalTimeMillis += deltaTime;
stat.totalBatteryDrain += deltaDrain;
+ stat.totalBatteryDrainPercent += deltaPercent;
if (DEBUG) {
Slog.d(TAG, "State summary: " + stateToString(mCurrentState)
+ ": " + (deltaTime / 1_000) + "s "
+ "Start level: " + stat.startBatteryLevel + "uA "
+ "End level: " + stat.endBatteryLevel + "uA "
- + deltaDrain + "uA");
+ + "Start percent: " + stat.startBatteryPercent + "% "
+ + "End percent: " + stat.endBatteryPercent + "% "
+ + "Drain " + deltaDrain + "uA");
}
EventLogTags.writeBatterySavingStats(
BatterySaverState.fromIndex(mCurrentState),
@@ -296,12 +331,14 @@ public class BatterySavingStats {
DozeState.fromIndex(mCurrentState),
deltaTime,
deltaDrain,
+ deltaPercent,
stat.totalTimeMillis,
- stat.totalBatteryDrain);
+ stat.totalBatteryDrain,
+ stat.totalBatteryDrainPercent);
}
- private void startNewStateLocked(int newState, long now, int batteryLevel) {
+ private void startNewStateLocked(int newState, long now, int batteryLevel, int batteryPercent) {
if (DEBUG) {
Slog.d(TAG, "New state: " + stateToString(newState));
}
@@ -313,6 +350,7 @@ public class BatterySavingStats {
final Stat stat = getStat(mCurrentState);
stat.startBatteryLevel = batteryLevel;
+ stat.startBatteryPercent = batteryPercent;
stat.startTime = now;
stat.endTime = 0;
}
@@ -325,7 +363,7 @@ public class BatterySavingStats {
indent = indent + " ";
pw.print(indent);
- pw.println("Battery Saver: Off On");
+ pw.println("Battery Saver: Off On");
dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.NOT_DOZING, "NonDoze");
dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
@@ -357,12 +395,14 @@ public class BatterySavingStats {
final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
- pw.println(String.format("%6dm %6dmA %8.1fmA/h %6dm %6dmA %8.1fmA/h",
+ pw.println(String.format("%6dm %6dmA (%3d%%) %8.1fmA/h %6dm %6dmA (%3d%%) %8.1fmA/h",
offStat.totalMinutes(),
offStat.totalBatteryDrain / 1000,
+ offStat.totalBatteryDrainPercent,
offStat.drainPerHour() / 1000.0,
onStat.totalMinutes(),
onStat.totalBatteryDrain / 1000,
+ onStat.totalBatteryDrainPercent,
onStat.drainPerHour() / 1000.0));
}
@@ -371,12 +411,13 @@ public class BatterySavingStats {
private int mLastState = STATE_NOT_INITIALIZED;
private long mStartTime;
private int mStartBatteryLevel;
+ private int mStartPercent;
private static final int STATE_CHANGE_DETECT_MASK =
(BatterySaverState.MASK << BatterySaverState.SHIFT) |
(InteractiveState.MASK << InteractiveState.SHIFT);
- public void transitionState(int newState, long now, int batteryLevel) {
+ public void transitionState(int newState, long now, int batteryLevel, int batteryPercent) {
final boolean stateChanging =
((mLastState >= 0) ^ (newState >= 0)) ||
(((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0);
@@ -384,11 +425,13 @@ public class BatterySavingStats {
if (mLastState >= 0) {
final long deltaTime = now - mStartTime;
final int deltaBattery = mStartBatteryLevel - batteryLevel;
+ final int deltaPercent = mStartPercent - batteryPercent;
- report(mLastState, deltaTime, deltaBattery);
+ report(mLastState, deltaTime, deltaBattery, deltaPercent);
}
mStartTime = now;
mStartBatteryLevel = batteryLevel;
+ mStartPercent = batteryPercent;
}
mLastState = newState;
}
@@ -405,9 +448,10 @@ public class BatterySavingStats {
}
}
- void report(int state, long deltaTimeMs, int deltaBatteryUa) {
+ void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) {
final String suffix = getCounterSuffix(state);
mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
+ mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent);
mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000));
}
}
diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java
index 591e809ad3d1..6f5afa207d31 100644
--- a/services/core/java/com/android/server/slice/SliceFullAccessList.java
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -16,9 +16,9 @@ package com.android.server.slice;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
-import android.util.Log;
import android.util.SparseArray;
import com.android.internal.util.XmlUtils;
@@ -63,7 +63,16 @@ public class SliceFullAccessList {
pkgs.add(pkg);
}
- public void writeXml(XmlSerializer out) throws IOException {
+ public void removeGrant(String pkg, int userId) {
+ ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mFullAccessPkgs.put(userId, pkgs);
+ }
+ pkgs.remove(pkg);
+ }
+
+ public void writeXml(XmlSerializer out, int user) throws IOException {
out.startTag(null, TAG_LIST);
out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
@@ -71,6 +80,9 @@ public class SliceFullAccessList {
for (int i = 0 ; i < N; i++) {
final int userId = mFullAccessPkgs.keyAt(i);
final ArraySet<String> pkgs = mFullAccessPkgs.valueAt(i);
+ if (user != UserHandle.USER_ALL && user != userId) {
+ continue;
+ }
out.startTag(null, TAG_USER);
out.attribute(null, ATT_USER_ID, Integer.toString(userId));
if (pkgs != null) {
@@ -79,7 +91,6 @@ public class SliceFullAccessList {
out.startTag(null, TAG_PKG);
out.text(pkgs.valueAt(j));
out.endTag(null, TAG_PKG);
-
}
}
out.endTag(null, TAG_USER);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5db0fc0d9e00..a1def440a007 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -21,6 +21,7 @@ import static android.content.ContentProvider.getUserIdFromUri;
import static android.content.ContentProvider.maybeAddUserId;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
import android.Manifest.permission;
import android.app.ActivityManager;
@@ -31,9 +32,11 @@ import android.app.slice.ISliceListener;
import android.app.slice.ISliceManager;
import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
@@ -66,8 +69,9 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -91,7 +95,9 @@ public class SliceManagerService extends ISliceManager.Stub {
private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
private final Handler mHandler;
private final ContentObserver mObserver;
+ @GuardedBy("mSliceAccessFile")
private final AtomicFile mSliceAccessFile;
+ @GuardedBy("mAccessList")
private final SliceFullAccessList mAccessList;
public SliceManagerService(Context context) {
@@ -127,11 +133,19 @@ public class SliceManagerService extends ISliceManager.Stub {
InputStream input = mSliceAccessFile.openRead();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(input, Encoding.UTF_8.name());
- mAccessList.readXml(parser);
+ synchronized (mAccessList) {
+ mAccessList.readXml(parser);
+ }
} catch (IOException | XmlPullParserException e) {
Slog.d(TAG, "Can't read slice access file", e);
}
}
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
}
/// ----- Lifecycle stuff -----
@@ -223,7 +237,9 @@ public class SliceManagerService extends ISliceManager.Stub {
getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
"Slice granting requires MANAGE_SLICE_PERMISSIONS");
if (allSlices) {
- mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ synchronized (mAccessList) {
+ mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ }
mHandler.post(mSaveAccessList);
} else {
synchronized (mLock) {
@@ -244,7 +260,71 @@ public class SliceManagerService extends ISliceManager.Stub {
}
}
+ // Backup/restore interface
+ @Override
+ public byte[] getBackupPayload(int user) {
+ if (Binder.getCallingUid() != SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ //TODO: http://b/22388012
+ if (user != UserHandle.USER_SYSTEM) {
+ Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
+ return null;
+ }
+ synchronized(mSliceAccessFile) {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+ out.setOutput(baos, Encoding.UTF_8.name());
+ synchronized (mAccessList) {
+ mAccessList.writeXml(out, user);
+ }
+ out.flush();
+ return baos.toByteArray();
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void applyRestore(byte[] payload, int user) {
+ if (Binder.getCallingUid() != SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ if (payload == null) {
+ Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
+ return;
+ }
+ //TODO: http://b/22388012
+ if (user != UserHandle.USER_SYSTEM) {
+ Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
+ return;
+ }
+ synchronized(mSliceAccessFile) {
+ final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+ try {
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(bais, Encoding.UTF_8.name());
+ synchronized (mAccessList) {
+ mAccessList.readXml(parser);
+ }
+ mHandler.post(mSaveAccessList);
+ } catch (NumberFormatException | XmlPullParserException | IOException e) {
+ Slog.w(TAG, "applyRestore: error reading payload", e);
+ }
+ }
+ }
+
/// ----- internal code -----
+ private void removeFullAccess(String pkg, int userId) {
+ synchronized (mAccessList) {
+ mAccessList.removeGrant(pkg, userId);
+ }
+ mHandler.post(mSaveAccessList);
+ }
+
protected void removePinnedSlice(Uri uri) {
synchronized (mLock) {
mPinnedSlicesByUri.remove(uri).destroy();
@@ -444,7 +524,9 @@ public class SliceManagerService extends ISliceManager.Stub {
}
private boolean isGrantedFullAccess(String pkg, int userId) {
- return mAccessList.hasFullAccess(pkg, userId);
+ synchronized (mAccessList) {
+ return mAccessList.hasFullAccess(pkg, userId);
+ }
}
private static ServiceThread createHandler() {
@@ -469,7 +551,9 @@ public class SliceManagerService extends ISliceManager.Stub {
try {
XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
out.setOutput(stream, Encoding.UTF_8.name());
- mAccessList.writeXml(out);
+ synchronized (mAccessList) {
+ mAccessList.writeXml(out, UserHandle.USER_ALL);
+ }
out.flush();
mSliceAccessFile.finishWrite(stream);
} catch (IOException | XmlPullParserException e) {
@@ -480,6 +564,35 @@ public class SliceManagerService extends ISliceManager.Stub {
}
};
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
+ }
+ Uri data = intent.getData();
+ String pkg = data != null ? data.getSchemeSpecificPart() : null;
+ if (pkg == null) {
+ Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+ return;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED:
+ final boolean replacing =
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (!replacing) {
+ removeFullAccess(pkg, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_DATA_CLEARED:
+ removeFullAccess(pkg, userId);
+ break;
+ }
+ }
+ };
+
public static class Lifecycle extends SystemService {
private SliceManagerService mService;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 648fa6af48d1..f498cdd85ee8 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -161,7 +161,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
@Override
public void sendBroadcast(String pkg, String cls) {
- // TODO: Use a pending intent, and enfoceCallingPermission.
+ // TODO: Use a pending intent.
+ enforceCallingPermission();
mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
UserHandle.SYSTEM);
}
@@ -239,7 +240,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- public final static class AppUpdateReceiver extends BroadcastReceiver {
+ private final static class AppUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/**
@@ -284,7 +285,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
+ private final static class AnomalyAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
@@ -304,7 +305,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- public final static class PullingAlarmReceiver extends BroadcastReceiver {
+ private final static class PullingAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG)
@@ -325,7 +326,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- public final static class ShutdownEventReceiver extends BroadcastReceiver {
+ private final static class ShutdownEventReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/**
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index adb368b074c0..7c170aee92fa 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -56,7 +56,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-
/**
* A note on locking: We rely on the fact that calls onto mBar are oneway or
* if they are local, that they just enqueue messages to not deadlock.
@@ -525,6 +524,26 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
+ public void showPinningEnterExitToast(boolean entering) throws RemoteException {
+ if (mBar != null) {
+ try {
+ mBar.showPinningEnterExitToast(entering);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() throws RemoteException {
+ if (mBar != null) {
+ try {
+ mBar.showPinningEscapeToast();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
if (mBar != null) {
try {
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 3f320793826f..b00e595db7a1 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -72,20 +72,23 @@ class AlertWindowNotification {
}
/** Cancels the notification */
- void cancel() {
+ void cancel(boolean deleteChannel) {
// We can't call into NotificationManager with WM lock held since it might call into AM.
// So, we post a message to do it later.
- mService.mH.post(this::onCancelNotification);
+ mService.mH.post(() -> onCancelNotification(deleteChannel));
}
/** Don't call with the window manager lock held! */
- private void onCancelNotification() {
+ private void onCancelNotification(boolean deleteChannel) {
if (!mPosted) {
// Notification isn't currently posted...
return;
}
mPosted = false;
mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
+ if (deleteChannel) {
+ mNotificationManager.deleteNotificationChannel(mNotificationTag);
+ }
}
/** Don't call with the window manager lock held! */
@@ -146,8 +149,12 @@ class AlertWindowNotification {
final String nameChannel =
context.getString(R.string.alert_windows_notification_channel_name, appName);
- final NotificationChannel channel =
- new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
+
+ NotificationChannel channel = mNotificationManager.getNotificationChannel(mNotificationTag);
+ if (channel != null) {
+ return;
+ }
+ channel = new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
channel.enableLights(false);
channel.enableVibration(false);
channel.setBlockableSystem(true);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7674b5e9ed2c..2512dbd6a12b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -662,7 +662,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWallpaperController.updateWallpaperVisibility();
}
- w.handleWindowMovedIfNeeded(mPendingTransaction);
+ w.handleWindowMovedIfNeeded();
final WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -1542,11 +1542,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) {
+ void updateStackBoundsAfterConfigChange(@NonNull List<TaskStack> changedStackList) {
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.getChildAt(i);
if (stack.updateBoundsAfterConfigChange()) {
- changedStackList.add(stack.mStackId);
+ changedStackList.add(stack);
}
}
@@ -3599,8 +3599,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
private final class AboveAppWindowContainers extends NonAppWindowContainers {
- private final Dimmer mDimmer = new Dimmer(this);
- private final Rect mTmpDimBoundsRect = new Rect();
AboveAppWindowContainers(String name, WindowManagerService service) {
super(name, service);
}
@@ -3632,22 +3630,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
}
}
-
- @Override
- Dimmer getDimmer() {
- return mDimmer;
- }
-
- @Override
- void prepareSurfaces() {
- mDimmer.resetDimStates();
- super.prepareSurfaces();
- getBounds(mTmpDimBoundsRect);
-
- if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
- scheduleAnimation();
- }
- }
}
/**
@@ -3679,6 +3661,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
};
private final String mName;
+ private final Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
NonAppWindowContainers(String name, WindowManagerService service) {
super(service);
mName = name;
@@ -3722,6 +3707,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
String getName() {
return mName;
}
+
+ @Override
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getBounds(mTmpDimBoundsRect);
+
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
}
private class NonMagnifiableWindowContainers extends NonAppWindowContainers {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8269a3b783c4..5bc739ee33b2 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -92,14 +92,21 @@ class RemoteAnimationController {
onAnimationFinished();
return;
}
- mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
- try {
- mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
- mFinishedCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to start remote animation", e);
- onAnimationFinished();
- }
+
+ // Scale the timeout with the animator scale the controlling app is using.
+ mHandler.postDelayed(mTimeoutRunnable,
+ (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
+
+ final RemoteAnimationTarget[] animations = createAnimations();
+ mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
+ mFinishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to start remote animation", e);
+ onAnimationFinished();
+ }
+ });
}
private RemoteAnimationTarget[] createAnimations() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index deed7f17e4e6..8d1a82250206 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -47,6 +47,7 @@ import com.android.server.EventLogTags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -126,7 +127,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
boolean mOrientationChangeComplete = true;
boolean mWallpaperActionPending = false;
- private final ArrayList<Integer> mChangedStackList = new ArrayList();
+ private final ArrayList<TaskStack> mTmpStackList = new ArrayList();
+ private final ArrayList<Integer> mTmpStackIds = new ArrayList<>();
// State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
// instances will be replaced with an instance that writes a binary representation of all
@@ -333,7 +335,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
/**
* Set new display override config and return array of ids of stacks that were changed during
- * update. If called for the default display, global configuration will also be updated.
+ * update. If called for the default display, global configuration will also be updated. Stacks
+ * that are marked for deferred removal are excluded from the returned array.
*/
int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) {
final DisplayContent displayContent = getDisplayContent(displayId);
@@ -346,24 +349,42 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
if (!configChanged) {
return null;
}
+
displayContent.onOverrideConfigurationChanged(newConfiguration);
+ mTmpStackList.clear();
if (displayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config. In this case
// we also want to update the global config.
- return setGlobalConfigurationIfNeeded(newConfiguration);
+ setGlobalConfigurationIfNeeded(newConfiguration, mTmpStackList);
} else {
- return updateStackBoundsAfterConfigChange(displayId);
+ updateStackBoundsAfterConfigChange(displayId, mTmpStackList);
+ }
+
+ mTmpStackIds.clear();
+ final int stackCount = mTmpStackList.size();
+
+ for (int i = 0; i < stackCount; ++i) {
+ final TaskStack stack = mTmpStackList.get(i);
+
+ // We only include stacks that are not marked for removal as they do not exist outside
+ // of WindowManager at this point.
+ if (!stack.mDeferRemoval) {
+ mTmpStackIds.add(stack.mStackId);
+ }
}
+
+ return mTmpStackIds.isEmpty() ? null : ArrayUtils.convertToIntArray(mTmpStackIds);
}
- private int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
+ private void setGlobalConfigurationIfNeeded(Configuration newConfiguration,
+ List<TaskStack> changedStacks) {
final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
if (!configChanged) {
- return null;
+ return;
}
onConfigurationChanged(newConfiguration);
- return updateStackBoundsAfterConfigChange();
+ updateStackBoundsAfterConfigChange(changedStacks);
}
@Override
@@ -378,26 +399,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- private int[] updateStackBoundsAfterConfigChange() {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(List<TaskStack> changedStacks) {
final int numDisplays = mChildren.size();
for (int i = 0; i < numDisplays; ++i) {
final DisplayContent dc = mChildren.get(i);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
}
/** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */
- private int[] updateStackBoundsAfterConfigChange(int displayId) {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) {
final DisplayContent dc = getDisplayContent(displayId);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
private void prepareFreezingTaskBounds() {
@@ -599,6 +612,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
+ mService.mAnimator.executeAfterPrepareSurfacesRunnables();
+
final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
// If we are ready to perform an app transition, check through all of the app tokens to be
@@ -903,10 +918,26 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
final WindowManager.LayoutParams attrs = w.mAttrs;
final int attrFlags = attrs.flags;
+ final boolean onScreen = w.isOnScreen();
final boolean canBeSeen = w.isDisplayedLw();
final int privateflags = attrs.privateFlags;
boolean displayHasContent = false;
+ if (DEBUG_KEEP_SCREEN_ON) {
+ Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked w: " + w
+ + ", w.mHasSurface: " + w.mHasSurface
+ + ", w.isOnScreen(): " + onScreen
+ + ", w.isDisplayedLw(): " + w.isDisplayedLw()
+ + ", w.mAttrs.userActivityTimeout: " + w.mAttrs.userActivityTimeout);
+ }
+ if (w.mHasSurface && onScreen) {
+ if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
+ mUserActivityTimeout = w.mAttrs.userActivityTimeout;
+ if (DEBUG_KEEP_SCREEN_ON) {
+ Slog.d(TAG, "mUserActivityTimeout set to " + mUserActivityTimeout);
+ }
+ }
+ }
if (w.mHasSurface && canBeSeen) {
if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
mHoldScreen = w.mSession;
@@ -919,9 +950,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
mScreenBrightness = w.mAttrs.screenBrightness;
}
- if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
- mUserActivityTimeout = w.mAttrs.userActivityTimeout;
- }
final int type = attrs.type;
// This function assumes that the contents of the default display are processed first
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 04ae38ec33b1..f09a294be75b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -547,7 +547,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
if (allowed) {
mAlertWindowNotification.post();
} else {
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(false /* deleteChannel */);
}
}
}
@@ -586,7 +586,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
if (mAlertWindowNotification == null) {
return;
}
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(true /* deleteChannel */);
mAlertWindowNotification = null;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 212a0d70927a..a7a2b534131d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -27,6 +27,7 @@ import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Environment;
@@ -40,6 +41,7 @@ import android.view.ThreadedRenderer;
import android.view.WindowManager.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
@@ -324,7 +326,8 @@ class TaskSnapshotController {
if (mainWindow == null) {
return null;
}
- final int color = task.getTaskDescription().getBackgroundColor();
+ final int color = ColorUtils.setAlphaComponent(
+ task.getTaskDescription().getBackgroundColor(), 255);
final int statusBarColor = task.getTaskDescription().getStatusBarColor();
final int navigationBarColor = task.getTaskDescription().getNavigationBarColor();
final LayoutParams attrs = mainWindow.getAttrs();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 259f8df15e31..e4db3b075e91 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -47,6 +47,7 @@ import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityThread;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -516,7 +517,7 @@ class TaskSnapshotSurface implements StartingSurface {
@VisibleForTesting
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
int statusBarHeight) {
- if (statusBarHeight > 0
+ if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
mContentInsets.right);
@@ -531,7 +532,7 @@ class TaskSnapshotSurface implements StartingSurface {
getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
navigationBarRect);
final boolean visible = isNavigationBarColorViewVisible();
- if (visible && !navigationBarRect.isEmpty()) {
+ if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
c.drawRect(navigationBarRect, mNavigationBarPaint);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ae5341bd8e45..a0d1480711a7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -21,6 +21,7 @@ import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
@@ -53,6 +54,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.RemoteException;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -741,14 +743,32 @@ public class TaskStack extends WindowContainer<Task> implements
scheduleAnimation();
}
+ /**
+ * Calculate an amount by which to expand the stack bounds in each direction.
+ * Used to make room for shadows in the pinned windowing mode.
+ */
+ int getStackOutset() {
+ if (inPinnedWindowingMode()) {
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ return mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+ displayMetrics);
+ }
+ return 0;
+ }
+
private void updateSurfaceSize(SurfaceControl.Transaction transaction) {
if (mSurfaceControl == null) {
return;
}
final Rect stackBounds = getBounds();
- final int width = stackBounds.width();
- final int height = stackBounds.height();
+ int width = stackBounds.width();
+ int height = stackBounds.height();
+
+ final int outset = getStackOutset();
+ width += 2*outset;
+ height += 2*outset;
+
if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
return;
}
@@ -1749,4 +1769,12 @@ public class TaskStack extends WindowContainer<Task> implements
mDimmer.stopDim(getPendingTransaction());
scheduleAnimation();
}
+
+ @Override
+ void getRelativePosition(Point outPos) {
+ super.getRelativePosition(outPos);
+ final int outset = getStackOutset();
+ outPos.x -= outset;
+ outPos.y -= outset;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index f2ad6fb7a888..da3a035ad8df 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -272,6 +272,8 @@ class WallpaperController {
}
boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
+ int xOffset = 0;
+ int yOffset = 0;
boolean rawChanged = false;
// Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to
// match the behavior of most Launchers
@@ -283,11 +285,8 @@ class WallpaperController {
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetX;
}
- boolean changed = wallpaperWin.mXOffset != offset;
- if (changed) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset);
- wallpaperWin.mXOffset = offset;
- }
+ xOffset = offset;
+
if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
wallpaperWin.mWallpaperX = wpx;
wallpaperWin.mWallpaperXStep = wpxs;
@@ -301,17 +300,16 @@ class WallpaperController {
if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetY;
}
- if (wallpaperWin.mYOffset != offset) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset);
- changed = true;
- wallpaperWin.mYOffset = offset;
- }
+ yOffset = offset;
+
if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
wallpaperWin.mWallpaperY = wpy;
wallpaperWin.mWallpaperYStep = wpys;
rawChanged = true;
}
+ boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset);
+
if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
try {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 2ae5c7bd9c25..ddda027595da 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -74,10 +74,6 @@ class WallpaperWindowToken extends WindowToken {
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
- final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
- winAnimator.computeShownFrameLocked();
- // No need to lay out the windows - we can just set the wallpaper position directly.
- winAnimator.setWallpaperOffset(wallpaper.mShownPosition);
// We only want to be synchronous with one wallpaper.
sync = false;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index cec13abd823d..b0d42f21fac3 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -92,6 +92,7 @@ public class WindowAnimator {
* executed and the corresponding transaction is closed and applied.
*/
private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
+ private boolean mInExecuteAfterPrepareSurfacesRunnables;
WindowAnimator(final WindowManagerService service) {
mService = service;
@@ -438,7 +439,13 @@ public class WindowAnimator {
scheduleAnimation();
}
- private void executeAfterPrepareSurfacesRunnables() {
+ void executeAfterPrepareSurfacesRunnables() {
+
+ // Don't even think about to start recursing!
+ if (mInExecuteAfterPrepareSurfacesRunnables) {
+ return;
+ }
+ mInExecuteAfterPrepareSurfacesRunnables = true;
// Traverse in order they were added.
final int size = mAfterPrepareSurfacesRunnables.size();
@@ -446,5 +453,6 @@ public class WindowAnimator {
mAfterPrepareSurfacesRunnables.get(i).run();
}
mAfterPrepareSurfacesRunnables.clear();
+ mInExecuteAfterPrepareSurfacesRunnables = false;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 066e4e6a8c67..d565a6a7a476 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,8 +24,6 @@ import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_USER_HANDLE;
@@ -125,7 +123,6 @@ import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IAssistDataReceiver;
-import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -2466,6 +2463,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
+
return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 53a8d82f551e..3bee1e8ab628 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -137,7 +137,6 @@ import static com.android.server.wm.proto.WindowStateProto.REMOVED;
import static com.android.server.wm.proto.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.proto.WindowStateProto.REQUESTED_HEIGHT;
import static com.android.server.wm.proto.WindowStateProto.REQUESTED_WIDTH;
-import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION;
import static com.android.server.wm.proto.WindowStateProto.STABLE_INSETS;
import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
@@ -297,12 +296,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
/**
- * Actual position of the surface shown on-screen (may be modified by animation). These are
- * in the screen's coordinate space (WITH the compatibility scale applied).
- */
- final Point mShownPosition = new Point();
-
- /**
* Insets that determine the actually visible area. These are in the application's
* coordinate space (without compatibility scale applied).
*/
@@ -461,10 +454,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
int mWallpaperDisplayOffsetX = Integer.MIN_VALUE;
int mWallpaperDisplayOffsetY = Integer.MIN_VALUE;
- // Wallpaper windows: pixels offset based on above variables.
- int mXOffset;
- int mYOffset;
-
/**
* This is set after IWindowSession.relayout() has been called at
* least once for the window. It allows us to detect the situation
@@ -745,8 +734,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
- mXOffset = 0;
- mYOffset = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
@@ -1112,11 +1099,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- public Point getShownPositionLw() {
- return mShownPosition;
- }
-
- @Override
public Rect getDisplayFrameLw() {
return mDisplayFrame;
}
@@ -1758,7 +1740,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* listeners and optionally animate it. Simply checking a change of position is not enough,
* because being move due to dock divider is not a trigger for animation.
*/
- void handleWindowMovedIfNeeded(Transaction t) {
+ void handleWindowMovedIfNeeded() {
if (!hasMoved()) {
return;
}
@@ -1776,7 +1758,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
&& !isDragResizing() && !adjustedForMinimizedDockOrIme
&& getWindowConfiguration().hasMovementAnimations()
&& !mWinAnimator.mLastHidden) {
- startMoveAnimation(t, left, top);
+ startMoveAnimation(left, top);
}
//TODO (multidisplay): Accessibility supported only for the default display.
@@ -3134,7 +3116,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
- mShownPosition.writeToProto(proto, SHOWN_POSITION);
mWinAnimator.writeToProto(proto, ANIMATOR);
proto.write(ANIMATING_EXIT, mAnimatingExit);
for (int i = 0; i < mChildren.size(); i++) {
@@ -3250,10 +3231,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled);
pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
}
- if (mXOffset != 0 || mYOffset != 0) {
- pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
- pw.print(" y="); pw.println(mYOffset);
- }
if (dumpAll) {
pw.print(prefix); pw.print("mGivenContentInsets=");
mGivenContentInsets.printShortString(pw);
@@ -3272,7 +3249,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.println(getLastReportedConfiguration());
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
- pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
if (dumpAll) {
@@ -4195,9 +4171,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int width = mFrame.width();
final int height = mFrame.height();
- // Compute the offset of the window in relation to the decor rect.
- final int left = mXOffset + mFrame.left;
- final int top = mYOffset + mFrame.top;
+ final int left = mFrame.left;
+ final int top = mFrame.top;
// Initialize the decor rect to the entire frame.
if (isDockedResizing()) {
@@ -4360,7 +4335,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
commitPendingTransaction();
}
- private void startMoveAnimation(Transaction t, int left, int top) {
+ private void startMoveAnimation(int left, int top) {
if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
@@ -4369,7 +4344,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final AnimationAdapter adapter = new LocalAnimationAdapter(
new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
mService.mSurfaceAnimationRunner);
- startAnimation(t, adapter);
+ startAnimation(getPendingTransaction(), adapter);
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
@@ -4392,8 +4367,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
- int x = mSurfacePosition.x + mShownPosition.x;
- int y = mSurfacePosition.y + mShownPosition.y;
+ int x = mSurfacePosition.x;
+ int y = mSurfacePosition.y;
// If changed, also adjust transformFrameToSurfacePosition
final WindowContainer parent = getParent();
@@ -4576,6 +4551,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
+ TaskStack stack = getStack();
+
+ // If we have stack outsets, that means the top-left
+ // will be outset, and we need to inset ourselves
+ // to account for it. If we actually have shadows we will
+ // then un-inset ourselves by the surfaceInsets.
+ if (stack != null) {
+ final int outset = stack.getStackOutset();
+ outPoint.offset(outset, outset);
+ }
+
// Expand for surface insets. See WindowState.expandForSurfaceInsets.
outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1cfa95625a88..499322c0b17a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -99,7 +99,6 @@ class WindowStateAnimator {
// Unchanging local convenience fields.
final WindowManagerService mService;
final WindowState mWin;
- private final WindowStateAnimator mParentWinAnimator;
final WindowAnimator mAnimator;
final Session mSession;
final WindowManagerPolicy mPolicy;
@@ -135,8 +134,6 @@ class WindowStateAnimator {
float mAlpha = 0;
float mLastAlpha = 0;
- boolean mHasClipRect;
- Rect mClipRect = new Rect();
Rect mTmpClipRect = new Rect();
Rect mTmpFinalClipRect = new Rect();
Rect mLastClipRect = new Rect();
@@ -150,7 +147,6 @@ class WindowStateAnimator {
* system decorations.
*/
private final Rect mSystemDecorRect = new Rect();
- private final Rect mLastSystemDecorRect = new Rect();
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
private float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
@@ -213,6 +209,12 @@ class WindowStateAnimator {
float mExtraHScale = (float) 1.0;
float mExtraVScale = (float) 1.0;
+ // An offset in pixel of the surface contents from the window position. Used for Wallpaper
+ // to provide the effect of scrolling within a large surface. We just use these values as
+ // a cache.
+ int mXOffset = 0;
+ int mYOffset = 0;
+
private final Rect mTmpSize = new Rect();
WindowStateAnimator(final WindowState win) {
@@ -224,7 +226,6 @@ class WindowStateAnimator {
mContext = service.mContext;
mWin = win;
- mParentWinAnimator = !win.isChildWindow() ? null : win.getParentWindow().mWinAnimator;
mSession = win.mSession;
mAttrType = win.mAttrs.type;
mIsWallpaper = win.mIsWallpaper;
@@ -441,7 +442,7 @@ class WindowStateAnimator {
flags |= SurfaceControl.SECURE;
}
- mTmpSize.set(w.mFrame.left + w.mXOffset, w.mFrame.top + w.mYOffset, 0, 0);
+ mTmpSize.set(0, 0, 0, 0);
calculateSurfaceBounds(w, attrs);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
@@ -455,9 +456,6 @@ class WindowStateAnimator {
}
// We may abort, so initialize to defaults.
- mLastSystemDecorRect.set(0, 0, 0, 0);
- mHasClipRect = false;
- mClipRect.set(0, 0, 0, 0);
mLastClipRect.set(0, 0, 0, 0);
// Set up surface control with initial size.
@@ -649,14 +647,12 @@ class WindowStateAnimator {
}
void computeShownFrameLocked() {
-
final int displayId = mWin.getDisplayId();
final ScreenRotationAnimation screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(displayId);
final boolean screenAnimation =
screenRotationAnimation != null && screenRotationAnimation.isAnimating();
- mHasClipRect = false;
if (screenAnimation) {
// cache often used attributes locally
final Rect frame = mWin.mFrame;
@@ -687,8 +683,8 @@ class WindowStateAnimator {
// WindowState.prepareSurfaces expands for surface insets (in order they don't get
// clipped by the WindowState surface), so we need to go into the other direction here.
- tmpMatrix.postTranslate(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
- mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
+ tmpMatrix.postTranslate(mWin.mAttrs.surfaceInsets.left,
+ mWin.mAttrs.surfaceInsets.top);
// "convert" it into SurfaceFlinger's format
@@ -703,9 +699,6 @@ class WindowStateAnimator {
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));
// Now set the alpha... but because our current hardware
// can't do alpha transformation on a non-opaque surface,
@@ -715,8 +708,7 @@ class WindowStateAnimator {
mShownAlpha = mAlpha;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
- || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)
- && x == frame.left && y == frame.top))) {
+ || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)))) {
//Slog.i(TAG_WM, "Applying alpha transform");
if (screenAnimation) {
mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
@@ -746,10 +738,6 @@ class WindowStateAnimator {
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- // WindowState.prepareSurfaces expands for surface insets (in order they don't get
- // clipped by the WindowState surface), so we need to go into the other direction here.
- mWin.mShownPosition.set(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
- mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
mShownAlpha = mAlpha;
mHaveMatrix = false;
mDsDx = mWin.mGlobalScale;
@@ -796,24 +784,12 @@ class WindowStateAnimator {
// We use the clip rect as provided by the tranformation for non-fullscreen windows to
// avoid premature clipping with the system decor rect.
- clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : mSystemDecorRect);
+ clipRect.set(mSystemDecorRect);
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect
- + " mHasClipRect=" + mHasClipRect + " fullscreen=" + fullscreen);
-
- if (isFreeformResizing && !w.isChildWindow()) {
- // For freeform resizing non child windows, we are using the big surface positioned
- // at 0,0. Thus we must express the crop in that coordinate space.
- clipRect.offset(w.mShownPosition.x, w.mShownPosition.y);
- }
+ + " fullscreen=" + fullscreen);
w.expandForSurfaceInsets(clipRect);
- if (mHasClipRect && fullscreen) {
- // We intersect the clip rect specified by the transformation with the expanded system
- // decor rect to prevent artifacts from drawing during animation if the transformation
- // clip rect extends outside the system decor rect.
- clipRect.intersect(mClipRect);
- }
// The clip rect was generated assuming (0,0) as the window origin,
// so we need to translate to match the actual surface coordinates.
clipRect.offset(w.mAttrs.surfaceInsets.left, w.mAttrs.surfaceInsets.top);
@@ -853,7 +829,7 @@ class WindowStateAnimator {
return;
}
- mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
+ mTmpSize.set(0, 0, 0, 0);
calculateSurfaceBounds(w, attrs);
mExtraHScale = (float) 1.0;
@@ -987,11 +963,6 @@ class WindowStateAnimator {
// then take over the scaling until the new buffer arrives, and things
// will be seamless.
mForceScaleUntilResize = true;
- } else {
- if (!w.mSeamlesslyRotated) {
- mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
- recoveringMemory);
- }
}
// If we are ending the scaling mode. We switch to SCALING_MODE_FREEZE
@@ -1183,24 +1154,26 @@ class WindowStateAnimator {
mSurfaceController.setTransparentRegionHint(region);
}
- void setWallpaperOffset(Point shownPosition) {
- final LayoutParams attrs = mWin.getAttrs();
- final int left = shownPosition.x - attrs.surfaceInsets.left;
- final int top = shownPosition.y - attrs.surfaceInsets.top;
+ boolean setWallpaperOffset(int dx, int dy) {
+ if (mXOffset == dx && mYOffset == dy) {
+ return false;
+ }
+ mXOffset = dx;
+ mYOffset = dy;
try {
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
mService.openSurfaceTransaction();
- mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
- mWin.mFrame.top + top, false);
+ mSurfaceController.setPositionInTransaction(dx, dy, false);
applyCrop(null, false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
- + " pos=(" + left + "," + top + ")", e);
+ + " pos=(" + dx + "," + dy + ")", e);
} finally {
mService.closeSurfaceTransaction("setWallpaperOffset");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setWallpaperOffset");
+ return true;
}
}
@@ -1378,8 +1351,6 @@ class WindowStateAnimator {
pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString());
pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
- pw.print(" last="); mLastSystemDecorRect.printShortString(pw);
- pw.print(" mHasClipRect="); pw.print(mHasClipRect);
pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
if (!mLastFinalClipRect.isEmpty()) {
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 8c38e0a9e363..847222ae4ba1 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -395,7 +395,8 @@ static JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Propert
gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported,
- jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), jVendorInfo.get()));
+ jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), nullptr,
+ jVendorInfo.get()));
}
JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
@@ -712,7 +713,7 @@ void register_android_server_broadcastradio_convert(JNIEnv *env) {
gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
"(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
"Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
- "[I[ILjava/util/Map;)V");
+ "[I[ILjava/util/Map;Ljava/util/Map;)V");
auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 210fd473ccd4..d5ed98e373bc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -723,21 +723,9 @@ public final class SystemServer {
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
- boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
- boolean disableMediaProjection = SystemProperties.getBoolean("config.disable_mediaproj",
- false);
- boolean disableSerial = SystemProperties.getBoolean("config.disable_serial", false);
- boolean disableSearchManager = SystemProperties.getBoolean("config.disable_searchmanager",
- false);
- boolean disableTrustManager = SystemProperties.getBoolean("config.disable_trustmanager",
- false);
- boolean disableTextServices = SystemProperties.getBoolean("config.disable_textservices",
- false);
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
- boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false);
- boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
@@ -745,6 +733,9 @@ public final class SystemServer {
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
+ boolean isWatch = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
+
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) {
throw new RuntimeException();
@@ -819,7 +810,7 @@ public final class SystemServer {
ServiceManager.addService("vibrator", vibrator);
traceEnd();
- if (!disableConsumerIr) {
+ if (!isWatch) {
traceBeginAndSlog("StartConsumerIrService");
consumerIr = new ConsumerIrService(context);
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
@@ -862,7 +853,7 @@ public final class SystemServer {
traceLog.traceEnd();
}, START_HIDL_SERVICES);
- if (!disableVrManager) {
+ if (!isWatch) {
traceBeginAndSlog("StartVrManagerService");
mSystemServiceManager.startService(VrManagerService.class);
traceEnd();
@@ -1030,7 +1021,7 @@ public final class SystemServer {
mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
traceEnd();
- if (!disableSystemUI) {
+ if (!isWatch) {
traceBeginAndSlog("StartStatusBarManagerService");
try {
statusBar = new StatusBarManagerService(context, wm);
@@ -1063,11 +1054,9 @@ public final class SystemServer {
}
traceEnd();
- if (!disableTextServices) {
- traceBeginAndSlog("StartTextServicesManager");
- mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartTextServicesManager");
+ mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
+ traceEnd();
if (!disableSystemTextClassifier) {
traceBeginAndSlog("StartTextClassificationManagerService");
@@ -1229,7 +1218,7 @@ public final class SystemServer {
}
traceEnd();
- if (!disableSearchManager) {
+ if (!isWatch) {
traceBeginAndSlog("StartSearchManagerService");
try {
mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS);
@@ -1259,7 +1248,7 @@ public final class SystemServer {
mSystemServiceManager.startService(DockObserver.class);
traceEnd();
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (isWatch) {
traceBeginAndSlog("StartThermalObserver");
mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
traceEnd();
@@ -1291,7 +1280,7 @@ public final class SystemServer {
traceEnd();
}
- if (!disableSerial) {
+ if (!isWatch) {
traceBeginAndSlog("StartSerialService");
try {
// Serial port support
@@ -1331,11 +1320,9 @@ public final class SystemServer {
mSystemServiceManager.startService(SoundTriggerService.class);
traceEnd();
- if (!disableTrustManager) {
- traceBeginAndSlog("StartTrustManager");
- mSystemServiceManager.startService(TrustManagerService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartTrustManager");
+ mSystemServiceManager.startService(TrustManagerService.class);
+ traceEnd();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
traceBeginAndSlog("StartBackupManager");
@@ -1392,14 +1379,16 @@ public final class SystemServer {
traceEnd();
}
- traceBeginAndSlog("StartNetworkTimeUpdateService");
- try {
- networkTimeUpdater = new NetworkTimeUpdateService(context);
- ServiceManager.addService("network_time_update_service", networkTimeUpdater);
- } catch (Throwable e) {
- reportWtf("starting NetworkTimeUpdate service", e);
+ if (!isWatch) {
+ traceBeginAndSlog("StartNetworkTimeUpdateService");
+ try {
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ ServiceManager.addService("network_time_update_service", networkTimeUpdater);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkTimeUpdate service", e);
+ }
+ traceEnd();
}
- traceEnd();
traceBeginAndSlog("StartCommonTimeManagementService");
try {
@@ -1535,13 +1524,13 @@ public final class SystemServer {
traceEnd();
}
- if (!disableMediaProjection) {
+ if (!isWatch) {
traceBeginAndSlog("StartMediaProjectionManager");
mSystemServiceManager.startService(MediaProjectionManagerService.class);
traceEnd();
}
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (isWatch) {
traceBeginAndSlog("StartWearConnectivityService");
mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
traceEnd();
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index d2ae22b43445..1cbb3991e461 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -90,7 +90,6 @@ public class LockTaskControllerTest {
@Mock private IStatusBarService mStatusBarService;
@Mock private WindowManagerService mWindowManager;
@Mock private LockPatternUtils mLockPatternUtils;
- @Mock private LockTaskNotify mLockTaskNotify;
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
@Mock private TelecomManager mTelecomManager;
@Mock private RecentTasks mRecentTasks;
@@ -123,7 +122,6 @@ public class LockTaskControllerTest {
mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
mLockTaskController.mTelecomManager = mTelecomManager;
mLockTaskController.mLockPatternUtils = mLockPatternUtils;
- mLockTaskController.mLockTaskNotify = mLockTaskNotify;
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
@@ -208,7 +206,7 @@ public class LockTaskControllerTest {
// THEN lock task mode should be started
verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
// THEN screen pinning toast should be shown
- verify(mLockTaskNotify).showPinningStartToast();
+ verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
}
@Test
@@ -377,7 +375,7 @@ public class LockTaskControllerTest {
// THEN the keyguard should be shown
verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
// THEN screen pinning toast should be shown
- verify(mLockTaskNotify).showPinningExitToast();
+ verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
new file mode 100644
index 000000000000..8502e69dd060
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AmbientBrightnessStatsTrackerTest {
+
+ private TestInjector mTestInjector;
+
+ @Before
+ public void setUp() {
+ mTestInjector = new TestInjector();
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverSingleDay() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Test case where no user data
+ userStats = statsTracker.getUserStats(0);
+ assertNull(userStats);
+ // Test after adding some user data
+ statsTracker.start();
+ statsTracker.add(0, 0);
+ mTestInjector.incrementTime(1000);
+ statsTracker.stop();
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ // Test after adding some more user data
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 50000);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 2;
+ expectedStats[1] = 1.5f;
+ expectedStats[11] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverMultipleDays() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Add data for day 1
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Add data for day 2
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(0, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(0, 5);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Test that the data is tracked as expected
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(2, userStats.size());
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ expectedStats[1] = 1.5f;
+ expectedStats[3] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[4] = 5;
+ assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverMultipleUsers() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Add data for user 1
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Add data for user 2
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(1, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(1, 5);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Test that the data is tracked as expected
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ expectedStats[1] = 1.5f;
+ expectedStats[3] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ userStats = statsTracker.getUserStats(1);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[4] = 5;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverMaxDays() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ // Add 10 extra days of data over the buffer limit
+ for (int i = 0; i < AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK + 10; i++) {
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(0, 10);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 20);
+ mTestInjector.incrementTime(1000);
+ statsTracker.stop();
+ }
+ // Assert that we are only tracking last "MAX_DAYS_TO_TRACK"
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK, userStats.size());
+ LocalDate runningDate = mTestInjector.getLocalDate();
+ for (int i = AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1; i >= 0; i--) {
+ assertEquals(runningDate, userStats.get(i).getLocalDate());
+ runningDate = runningDate.minusDays(1);
+ }
+ }
+
+ @Test
+ public void testReadAmbientBrightnessStats() throws IOException {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ LocalDate date = mTestInjector.getLocalDate();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ String statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ // Old stats that shouldn't be read
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+ + date.minusDays(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK)
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ // Valid stats that should get read
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+ + date.minusDays(1)
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ // Valid stats that should get read
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
+ + "0.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ statsTracker.readStats(getInputStream(statsFile));
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(2, userStats.size());
+ assertEquals(new AmbientBrightnessDayStats(date.minusDays(1),
+ new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
+ new float[]{1.088f, 0, 0.726f, 0, 25.868f, 0, 0, 0, 0, 0}), userStats.get(0));
+ assertEquals(new AmbientBrightnessDayStats(date,
+ new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
+ new float[]{0, 0, 0, 0, 4.482f, 0, 0, 0, 0, 0}), userStats.get(1));
+ }
+
+ @Test
+ public void testFailedReadAmbientBrightnessStatsWithException() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ LocalDate date = mTestInjector.getLocalDate();
+ String statsFile;
+ // Test with parse error
+ statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ // Incorrect since bucket boundaries not parsable
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"asdf,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ try {
+ statsTracker.readStats(getInputStream(statsFile));
+ } catch (IOException e) {
+ // Expected
+ }
+ assertNull(statsTracker.getUserStats(0));
+ // Test with incorrect data (bucket boundaries length not equal to stats length)
+ statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ // Correct data
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+ + date.minusDays(1)
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
+ + "0.0\" />\r\n"
+ // Incorrect data
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ try {
+ statsTracker.readStats(getInputStream(statsFile));
+ } catch (Exception e) {
+ // Expected
+ }
+ assertNull(statsTracker.getUserStats(0));
+ // Test with missing attribute
+ statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ + "<ambientBrightnessDayStats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ try {
+ statsTracker.readStats(getInputStream(statsFile));
+ } catch (Exception e) {
+ // Expected
+ }
+ assertNull(statsTracker.getUserStats(0));
+ }
+
+ @Test
+ public void testWriteThenReadAmbientBrightnessStats() throws IOException {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Generate some dummy data
+ // Data: very old which should not be read
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Data: day 1 user 1
+ mTestInjector.incrementDate(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1);
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Data: day 1 user 2
+ statsTracker.start();
+ statsTracker.add(1, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(1, 5);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Data: day 2 user 1
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(0, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(0, 50000);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Write them
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ statsTracker.writeStats(baos);
+ baos.flush();
+ // Read them back and assert that it's the same
+ ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
+ AmbientBrightnessStatsTracker newStatsTracker = getTestStatsTracker();
+ newStatsTracker.readStats(input);
+ userStats = newStatsTracker.getUserStats(0);
+ assertEquals(2, userStats.size());
+ // Check day 1 user 1
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ expectedStats[1] = 1.5f;
+ expectedStats[3] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ // Check day 2 user 1
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[11] = 5;
+ assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
+ userStats = newStatsTracker.getUserStats(1);
+ assertEquals(1, userStats.size());
+ // Check day 1 user 2
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[4] = 5;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ }
+
+ @Test
+ public void testTimer() {
+ AmbientBrightnessStatsTracker.Timer timer = new AmbientBrightnessStatsTracker.Timer(
+ () -> mTestInjector.elapsedRealtimeMillis());
+ assertEquals(0, timer.totalDurationSec(), 0);
+ mTestInjector.incrementTime(1000);
+ assertEquals(0, timer.totalDurationSec(), 0);
+ assertFalse(timer.isRunning());
+ // Start timer
+ timer.start();
+ assertTrue(timer.isRunning());
+ assertEquals(0, timer.totalDurationSec(), 0);
+ mTestInjector.incrementTime(1000);
+ assertTrue(timer.isRunning());
+ assertEquals(1, timer.totalDurationSec(), 0);
+ // Reset timer
+ timer.reset();
+ assertEquals(0, timer.totalDurationSec(), 0);
+ assertFalse(timer.isRunning());
+ // Start again
+ timer.start();
+ assertTrue(timer.isRunning());
+ assertEquals(0, timer.totalDurationSec(), 0);
+ mTestInjector.incrementTime(2000);
+ assertTrue(timer.isRunning());
+ assertEquals(2, timer.totalDurationSec(), 0);
+ // Reset again
+ timer.reset();
+ assertEquals(0, timer.totalDurationSec(), 0);
+ assertFalse(timer.isRunning());
+ }
+
+ private class TestInjector extends AmbientBrightnessStatsTracker.Injector {
+
+ private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+ private LocalDate mLocalDate = LocalDate.now();
+
+ public void incrementTime(long timeMillis) {
+ mElapsedRealtimeMillis += timeMillis;
+ }
+
+ public void incrementDate(int numDays) {
+ mLocalDate = mLocalDate.plusDays(numDays);
+ }
+
+ @Override
+ public long elapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public int getUserSerialNumber(UserManager userManager, int userId) {
+ return userId + 10;
+ }
+
+ @Override
+ public int getUserId(UserManager userManager, int userSerialNumber) {
+ return userSerialNumber - 10;
+ }
+
+ @Override
+ public LocalDate getLocalDate() {
+ return LocalDate.from(mLocalDate);
+ }
+ }
+
+ private AmbientBrightnessStatsTracker getTestStatsTracker() {
+ return new AmbientBrightnessStatsTracker(
+ InstrumentationRegistry.getContext().getSystemService(UserManager.class),
+ mTestInjector);
+ }
+
+ private float[] getEmptyStatsArray() {
+ return new float[AmbientBrightnessStatsTracker.BUCKET_BOUNDARIES_FOR_NEW_STATS.length];
+ }
+
+ private InputStream getInputStream(String data) {
+ return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index edc7d74b47cb..501f9666f5c4 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -30,7 +30,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.database.ContentObserver;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.display.BrightnessChangeEvent;
@@ -195,7 +194,9 @@ public class BrightnessTrackerTest {
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
final int systemUpdatedBrightness = 20;
- notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/);
+ notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/,
+ 0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/,
+ false /*isDefaultBrightnessConfig*/);
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
// No events because we filtered out our change.
assertEquals(0, events.size());
@@ -285,7 +286,8 @@ public class BrightnessTrackerTest {
+ "lastNits=\"32.333\" "
+ "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n"
+ "lux=\"32.2,31.1\" luxTimestamps=\""
- + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
+ + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+ + "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />"
+ "<event nits=\"71\" timestamp=\""
+ Long.toString(someTimeAgo) + "\" packageName=\""
+ "com.android.anapp\" user=\"11\" "
@@ -315,6 +317,9 @@ public class BrightnessTrackerTest {
assertFalse(event.nightMode);
assertEquals(1.0f, event.batteryLevel, FLOAT_DELTA);
assertEquals("com.example.app", event.packageName);
+ assertTrue(event.isDefaultBrightnessConfig);
+ assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
+ assertTrue(event.isUserSetBrightness);
events = tracker.getEvents(1, true).getList();
assertEquals(1, events.size());
@@ -329,6 +334,10 @@ public class BrightnessTrackerTest {
assertEquals(3235, event.colorTemperature);
assertEquals(0.5f, event.batteryLevel, FLOAT_DELTA);
assertEquals("com.android.anapp", event.packageName);
+ // Not present in the event so default to false.
+ assertFalse(event.isDefaultBrightnessConfig);
+ assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA);
+ assertFalse(event.isUserSetBrightness);
}
@Test
@@ -379,7 +388,9 @@ public class BrightnessTrackerTest {
mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
final long secondSensorTime = mInjector.currentTimeMillis();
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
- notifyBrightnessChanged(mTracker, brightness);
+ notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/,
+ 0.5f /*powerPolicyDim(*/, true /*hasUserBrightnessPoints*/,
+ false /*isDefaultBrightnessConfig*/);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mTracker.writeEventsLocked(baos);
mTracker.stop();
@@ -399,6 +410,9 @@ public class BrightnessTrackerTest {
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3339, event.colorTemperature);
+ assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
+ assertTrue(event.isUserSetBrightness);
+ assertFalse(event.isDefaultBrightnessConfig);
}
@Test
@@ -539,12 +553,16 @@ public class BrightnessTrackerTest {
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) {
- notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/);
+ notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/,
+ 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
+ false /*isDefaultBrightnessConfig*/);
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
- boolean userInitiated) {
- tracker.notifyBrightnessChanged(brightness, userInitiated);
+ boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness,
+ boolean isDefaultBrightnessConfig) {
+ tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor,
+ isUserSetBrightness, isDefaultBrightnessConfig);
mInjector.waitForHandler();
}
@@ -573,7 +591,6 @@ public class BrightnessTrackerTest {
private class TestInjector extends BrightnessTracker.Injector {
SensorEventListener mSensorListener;
BroadcastReceiver mBroadcastReceiver;
- Map<String, Integer> mSystemIntSettings = new HashMap<>();
Map<String, Integer> mSecureIntSettings = new HashMap<>();
long mCurrentTimeMillis = System.currentTimeMillis();
long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -639,7 +656,7 @@ public class BrightnessTrackerTest {
}
@Override
- public AtomicFile getFile() {
+ public AtomicFile getFile(String filename) {
// Don't have the test write / read from anywhere.
return null;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index c863aabb905f..473a813c3838 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -39,6 +39,7 @@ import android.Manifest;
import android.os.Binder;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
+import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
@@ -49,6 +50,7 @@ import android.support.test.filters.SmallTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -135,6 +137,8 @@ public class RecoverableKeyStoreManagerTest {
@Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
@Mock private KeyguardManager mKeyguardManager;
@Mock private PlatformKeyManager mPlatformKeyManager;
+ @Mock private KeyStore mKeyStore;
+ @Mock private ApplicationKeyStorage mApplicationKeyStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
@@ -164,12 +168,14 @@ public class RecoverableKeyStoreManagerTest {
mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
mMockContext,
+ mKeyStore,
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
Executors.newSingleThreadExecutor(),
mRecoverySnapshotStorage,
mMockListenersStorage,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mApplicationKeyStorage);
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 56d4b7e8c1e8..857925b3ed17 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -5149,7 +5149,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.forAllShortcuts(si -> {
switch (package1DisabledReason) {
case ShortcutInfo.DISABLED_REASON_VERSION_LOWER:
- assertEquals("This shortcut requires latest app",
+ assertEquals("App version downgraded, or isn’t compatible"
+ + " with this shortcut",
si.getDisabledMessage());
break;
case ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH:
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index c2072df75bd8..f559986a6f15 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -53,6 +53,7 @@ public class DexoptOptionsTests {
assertFalse(opt.isDowngrade());
assertFalse(opt.isForce());
assertFalse(opt.isDexoptIdleBackgroundJob());
+ assertFalse(opt.isDexoptInstallWithDexMetadata());
}
@Test
@@ -65,7 +66,8 @@ public class DexoptOptionsTests {
DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
DexoptOptions.DEXOPT_DOWNGRADE |
DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
- DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
+ DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
assertEquals(mPackageName, opt.getPackageName());
@@ -79,6 +81,7 @@ public class DexoptOptionsTests {
assertTrue(opt.isForce());
assertTrue(opt.isDexoptAsSharedLibrary());
assertTrue(opt.isDexoptIdleBackgroundJob());
+ assertTrue(opt.isDexoptInstallWithDexMetadata());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index a628b7b70c15..5de393c7ae2b 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -79,11 +79,6 @@ public class FakeWindowState implements WindowManagerPolicy.WindowState {
}
@Override
- public Point getShownPositionLw() {
- return new Point(parentFrame.left, parentFrame.top);
- }
-
- @Override
public Rect getDisplayFrameLw() {
return displayFrame;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index c3714c85e895..f7516b2c3694 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -63,6 +63,11 @@ public class BatterySavingStatsTest {
return mBatteryLevel;
}
+ @Override
+ int injectBatteryPercent() {
+ return mBatteryLevel / 10;
+ }
+
void assertDumpable() {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
dump(new PrintWriter(out), ""); // Just make sure it won't crash.
@@ -102,7 +107,7 @@ public class BatterySavingStatsTest {
target.assertDumpable();
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.OFF,
@@ -110,7 +115,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -118,7 +123,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -126,7 +131,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -134,7 +139,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -142,7 +147,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -150,7 +155,7 @@ public class BatterySavingStatsTest {
DozeState.LIGHT);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -158,7 +163,7 @@ public class BatterySavingStatsTest {
DozeState.DEEP);
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.ON,
@@ -166,7 +171,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(1);
- target.drainBattery(3);
+ target.drainBattery(300);
target.transitionState(
BatterySaverState.OFF,
@@ -174,7 +179,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.ON,
@@ -182,12 +187,12 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.startCharging();
target.advanceClock(5);
- target.drainBattery(10);
+ target.drainBattery(1000);
target.transitionState(
BatterySaverState.ON,
@@ -195,25 +200,25 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.startCharging();
target.assertDumpable();
assertEquals(
- "BS=0,I=0,D=0:{4m,10,150.00}\n" +
- "BS=1,I=0,D=0:{0m,0,0.00}\n" +
- "BS=0,I=1,D=0:{14m,8,34.29}\n" +
- "BS=1,I=1,D=0:{9m,9,60.00}\n" +
- "BS=0,I=0,D=1:{5m,1,12.00}\n" +
- "BS=1,I=0,D=1:{0m,0,0.00}\n" +
- "BS=0,I=1,D=1:{0m,0,0.00}\n" +
- "BS=1,I=1,D=1:{0m,0,0.00}\n" +
- "BS=0,I=0,D=2:{1m,2,120.00}\n" +
- "BS=1,I=0,D=2:{0m,0,0.00}\n" +
- "BS=0,I=1,D=2:{0m,0,0.00}\n" +
- "BS=1,I=1,D=2:{0m,0,0.00}",
+ "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" +
+ "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" +
+ "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" +
+ "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" +
+ "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" +
+ "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}",
target.toDebugString());
}
@@ -245,6 +250,7 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60);
target.advanceClock(1);
@@ -277,15 +283,17 @@ public class BatterySavingStatsTest {
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3);
target.advanceClock(10);
- target.drainBattery(10_000);
+ target.drainBattery(10000);
reset(mMetricsLogger);
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10);
target.advanceClock(1);
@@ -305,6 +313,7 @@ public class BatterySavingStatsTest {
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60);
}
}
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 40964c03db81..78b6077dc1bd 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -24,6 +24,7 @@ import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_USAGE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -35,6 +36,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
@@ -80,6 +82,8 @@ public class AppStandbyControllerTests {
private static final String PACKAGE_1 = "com.example.foo";
private static final int UID_1 = 10000;
+ private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted";
+ private static final int UID_EXEMPTED_1 = 10001;
private static final int USER_ID = 0;
private static final int USER_ID2 = 10;
@@ -116,7 +120,7 @@ public class AppStandbyControllerTests {
List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
boolean mDisplayOn;
DisplayManager.DisplayListener mDisplayListener;
- String mBoundWidgetPackage;
+ String mBoundWidgetPackage = PACKAGE_EXEMPTED_1;
MyInjector(Context context, Looper looper) {
super(context, looper);
@@ -223,10 +227,21 @@ public class AppStandbyControllerTests {
pi.packageName = PACKAGE_1;
packages.add(pi);
+ PackageInfo pie = new PackageInfo();
+ pie.applicationInfo = new ApplicationInfo();
+ pie.applicationInfo.uid = UID_EXEMPTED_1;
+ pie.packageName = PACKAGE_EXEMPTED_1;
+ packages.add(pie);
+
doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
try {
- doReturn(UID_1).when(mockPm).getPackageUidAsUser(anyString(), anyInt(), anyInt());
- doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(anyString(), anyInt());
+ doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
+ doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
+ anyInt(), anyInt());
+ doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
+ anyInt());
+ doReturn(pie.applicationInfo).when(mockPm).getApplicationInfo(eq(pie.packageName),
+ anyInt());
} catch (PackageManager.NameNotFoundException nnfe) {}
}
@@ -239,14 +254,21 @@ public class AppStandbyControllerTests {
private AppStandbyController setupController() throws Exception {
mInjector.mElapsedRealtime = 0;
+ setupPm(mInjector.getContext().getPackageManager());
AppStandbyController controller = new AppStandbyController(mInjector);
+ controller.initializeDefaultsForSystemApps(USER_ID);
controller.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mInjector.setDisplayOn(false);
mInjector.setDisplayOn(true);
setChargingState(controller, false);
- setupPm(mInjector.getContext().getPackageManager());
controller.checkIdleStates(USER_ID);
+ assertEquals(STANDBY_BUCKET_EXEMPTED,
+ controller.getAppStandbyBucket(PACKAGE_EXEMPTED_1, USER_ID,
+ mInjector.mElapsedRealtime, false));
+ assertNotEquals(STANDBY_BUCKET_EXEMPTED,
+ controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
+ mInjector.mElapsedRealtime, false));
return controller;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index f860195bd6ea..26a7313b71d8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -19,12 +19,12 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.graphics.Point;
import android.graphics.Rect;
-import android.platform.test.annotations.Postsubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -137,6 +137,32 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
@Test
+ public void testTimeout_scaled() throws Exception {
+ sWm.setAnimationScale(2, 5.0f);
+ try{
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ mClock.fastForward(2500);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner, never()).onAnimationCancelled();
+
+ mClock.fastForward(10000);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner).onAnimationCancelled();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ } finally {
+ sWm.setAnimationScale(2, 1.0f);
+ }
+
+ }
+
+ @Test
public void testZeroAnimations() throws Exception {
mController.goodToGo();
verifyZeroInteractions(mMockRunner);
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 000000000000..51b019a4d61e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,50 @@
+package com.android.server.wm;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link RootWindowContainer} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RootWindowContainerTests extends WindowTestsBase {
+ @Test
+ public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
+ // Add first stack we expect to be updated with configuration change.
+ final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+ stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
+
+ // Add second task that will be set for deferred removal that should not be returned
+ // with the configuration change.
+ final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
+ deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
+ new Rect(0, 0, 5, 5));
+ deferredDeletedStack.mDeferRemoval = true;
+
+ final Configuration override = new Configuration(
+ mDisplayContent.getOverrideConfiguration());
+ override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+
+ // Set display override.
+ final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
+ mDisplayContent.getDisplayId());
+
+ // Ensure only first stack is returned.
+ assertTrue(results.length == 1);
+ assertTrue(results[0] == stack.mStackId);
+ }
+}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 99eb8468c20d..e36586eba26c 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -210,6 +210,9 @@ public class ShortcutManagerTestUtils {
runCommand(instrumentation, "cmd package set-home-activity --user "
+ instrumentation.getContext().getUserId() + " " + component,
result -> result.contains("Success"));
+ runCommand(instrumentation, "cmd shortcut clear-default-launcher --user "
+ + instrumentation.getContext().getUserId(),
+ result -> result.contains("Success"));
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9ae6f00f44ca..6b6df294e47c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -117,7 +117,7 @@ import java.util.Set;
public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int mUid = Binder.getCallingUid();
- private NotificationManagerService mService;
+ private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
@Mock
@@ -152,17 +152,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
+ int countSystemChecks = 0;
+
public TestableNotificationManagerService(Context context) {
super(context);
}
@Override
protected boolean isCallingUidSystem() {
+ countSystemChecks++;
return true;
}
@Override
protected boolean isCallerSystemOrPhone() {
+ countSystemChecks++;
return true;
}
@@ -2429,4 +2433,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setUid(user2.sbn.getUid())
.setLastNotified(user2.sbn.getPostTime())));
}
+
+ @Test
+ public void testRestore() throws Exception {
+ int systemChecks = mService.countSystemChecks;
+ mBinderService.applyRestore(null, UserHandle.USER_SYSTEM);
+ assertEquals(1, mService.countSystemChecks - systemChecks);
+ }
+
+ @Test
+ public void testBackup() throws Exception {
+ int systemChecks = mService.countSystemChecks;
+ mBinderService.getBackupPayload(1);
+ assertEquals(1, mService.countSystemChecks - systemChecks);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
index 7c14d083d3c9..bc2815099fdb 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.util.Xml.Encoding;
@@ -67,6 +68,15 @@ public class SliceFullAccessListTest extends UiServiceTestCase {
}
@Test
+ public void testRemoveAccess() {
+ mAccessList.grantFullAccess("pkg", 0);
+ assertTrue(mAccessList.hasFullAccess("pkg", 0));
+
+ mAccessList.removeGrant("pkg", 0);
+ assertFalse(mAccessList.hasFullAccess("pkg", 0));
+ }
+
+ @Test
public void testSerialization() throws XmlPullParserException, IOException {
mAccessList.grantFullAccess("pkg", 0);
mAccessList.grantFullAccess("pkg1", 0);
@@ -76,7 +86,7 @@ public class SliceFullAccessListTest extends UiServiceTestCase {
ByteArrayOutputStream output = new ByteArrayOutputStream();
XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
out.setOutput(output, Encoding.UTF_8.name());
- mAccessList.writeXml(out);
+ mAccessList.writeXml(out, UserHandle.USER_ALL);
out.flush();
assertEquals(TEST_XML, output.toString(Encoding.UTF_8.name()));
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 2becdf230dbc..b654a66f7d30 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -228,7 +228,9 @@ public class AppIdleHistory {
}
if (timeout > elapsedRealtime) {
// Convert to elapsed timebase
- appUsageHistory.bucketTimeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
+ appUsageHistory.bucketTimeoutTime =
+ Math.max(appUsageHistory.bucketTimeoutTime,
+ mElapsedDuration + (timeout - mElapsedSnapshot));
}
}
appUsageHistory.bucketingReason = REASON_USAGE;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 8cb2eecc654a..cc21199edfd9 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -183,6 +183,8 @@ public class AppStandbyController {
boolean mCharging;
private long mLastAppIdleParoledTime;
private boolean mSystemServicesReady = false;
+ // There was a system update, defaults need to be initialized after services are ready
+ private boolean mPendingInitializeDefaults;
private final DeviceStateReceiver mDeviceStateReceiver;
@@ -279,6 +281,7 @@ public class AppStandbyController {
public void onBootPhase(int phase) {
mInjector.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ Slog.d(TAG, "Setting app idle enabled state");
setAppIdleEnabled(mInjector.isAppIdleEnabled());
// Observe changes to the threshold
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
@@ -293,11 +296,15 @@ public class AppStandbyController {
mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
}
+ mSystemServicesReady = true;
+
+ if (mPendingInitializeDefaults) {
+ initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
+ }
+
if (mPendingOneTimeCheckIdleStates) {
postOneTimeCheckIdleStates();
}
-
- mSystemServicesReady = true;
} else if (phase == PHASE_BOOT_COMPLETED) {
setChargingState(mInjector.isCharging());
}
@@ -451,7 +458,8 @@ public class AppStandbyController {
UserHandle.getAppId(pi.applicationInfo.uid),
userId);
if (DEBUG) {
- Slog.d(TAG, " Checking idle state for " + packageName);
+ Slog.d(TAG, " Checking idle state for " + packageName + " special=" +
+ isSpecial);
}
if (isSpecial) {
synchronized (mAppIdleLock) {
@@ -523,6 +531,7 @@ public class AppStandbyController {
elapsedRealtime, bucket)) {
StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
bucket, userStartedInteracting);
+ if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
StandbyUpdateRecord.obtain(packageName, userId,
bucket, userStartedInteracting)));
@@ -1087,7 +1096,13 @@ public class AppStandbyController {
}
void initializeDefaultsForSystemApps(int userId) {
- Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+ if (!mSystemServicesReady) {
+ // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
+ mPendingInitializeDefaults = true;
+ return;
+ }
+ Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", "
+ + "appIdleEnabled=" + mAppIdleEnabled);
final long elapsedRealtime = mInjector.elapsedRealtime();
List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
PackageManager.MATCH_DISABLED_COMPONENTS,
@@ -1102,11 +1117,6 @@ public class AppStandbyController {
// past usage pattern was.
mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
elapsedRealtime + 4 * ONE_HOUR);
- if (isAppSpecial(packageName, UserHandle.getAppId(pi.applicationInfo.uid),
- userId)) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
- }
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
new file mode 100644
index 000000000000..74280fff2dfc
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -0,0 +1,160 @@
+/*
+ * 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.usb;
+
+/**
+ * Represents the ALSA specification, and attributes of an ALSA device.
+ */
+public final class UsbAlsaDevice {
+ private static final String TAG = "UsbAlsaDevice";
+ protected static final boolean DEBUG = false;
+
+ private final int mCardNum;
+ private final int mDeviceNum;
+ private final boolean mHasPlayback;
+ private final boolean mHasCapture;
+
+ private final boolean mIsInputHeadset;
+ private final boolean mIsOutputHeadset;
+
+ private final String mDeviceAddress;
+
+ private String mDeviceName = "";
+ private String mDeviceDescription = "";
+
+ public UsbAlsaDevice(int card, int device, String deviceAddress,
+ boolean hasPlayback, boolean hasCapture,
+ boolean isInputHeadset, boolean isOutputHeadset) {
+ mCardNum = card;
+ mDeviceNum = device;
+ mDeviceAddress = deviceAddress;
+ mHasPlayback = hasPlayback;
+ mHasCapture = hasCapture;
+ mIsInputHeadset = isInputHeadset;
+ mIsOutputHeadset = isOutputHeadset;
+ }
+
+ /**
+ * @returns the ALSA card number associated with this peripheral.
+ */
+ public int getCardNum() {
+ return mCardNum;
+ }
+
+ /**
+ * @returns the ALSA device number associated with this peripheral.
+ */
+ public int getDeviceNum() {
+ return mDeviceNum;
+ }
+
+ /**
+ * @returns the USB device device address associated with this peripheral.
+ */
+ public String getDeviceAddress() {
+ return mDeviceAddress;
+ }
+
+ /**
+ * @returns true if the device supports playback.
+ */
+ public boolean hasPlayback() {
+ return mHasPlayback;
+ }
+
+ /**
+ * @returns true if the device supports capture (recording).
+ */
+ public boolean hasCapture() {
+ return mHasCapture;
+ }
+
+ /**
+ * @returns true if the device is a headset for purposes of capture.
+ */
+ public boolean isInputHeadset() {
+ return mIsInputHeadset;
+ }
+
+ /**
+ * @returns true if the device is a headset for purposes of playback.
+ */
+ public boolean isOutputHeadset() {
+ return mIsOutputHeadset;
+ }
+
+ /**
+ * @Override
+ * @returns a string representation of the object.
+ */
+ public String toString() {
+ return "UsbAlsaDevice: [card: " + mCardNum
+ + ", device: " + mDeviceNum
+ + ", name: " + mDeviceName
+ + ", hasPlayback: " + mHasPlayback
+ + ", hasCapture: " + mHasCapture + "]";
+ }
+
+ // called by logDevices
+ String toShortString() {
+ return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]";
+ }
+
+ String getDeviceName() {
+ return mDeviceName;
+ }
+
+ void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
+ mDeviceName = deviceName;
+ mDeviceDescription = deviceDescription;
+ }
+
+ /**
+ * @Override
+ * @returns true if the objects are equivalent.
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof UsbAlsaDevice)) {
+ return false;
+ }
+ UsbAlsaDevice other = (UsbAlsaDevice) obj;
+ return (mCardNum == other.mCardNum
+ && mDeviceNum == other.mDeviceNum
+ && mHasPlayback == other.mHasPlayback
+ && mHasCapture == other.mHasCapture
+ && mIsInputHeadset == other.mIsInputHeadset
+ && mIsOutputHeadset == other.mIsOutputHeadset);
+ }
+
+ /**
+ * @Override
+ * @returns a hash code generated from the object contents.
+ */
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mCardNum;
+ result = prime * result + mDeviceNum;
+ result = prime * result + (mHasPlayback ? 0 : 1);
+ result = prime * result + (mHasCapture ? 0 : 1);
+ result = prime * result + (mIsInputHeadset ? 0 : 1);
+ result = prime * result + (mIsOutputHeadset ? 0 : 1);
+
+ return result;
+ }
+}
+
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 7bea8a11133b..0a94828e595e 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -10,7 +10,7 @@
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions an
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
@@ -19,28 +19,24 @@ package com.android.server.usb;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.midi.MidiDeviceInfo;
import android.os.Bundle;
-import android.os.FileObserver;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.provider.Settings;
import android.util.Slog;
import com.android.internal.alsa.AlsaCardsParser;
-import com.android.internal.alsa.AlsaDevicesParser;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.audio.AudioService;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
import libcore.io.IoUtils;
-import java.io.File;
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -57,107 +53,36 @@ public final class UsbAlsaManager {
private final boolean mHasMidiFeature;
private final AlsaCardsParser mCardsParser = new AlsaCardsParser();
- private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser();
// this is needed to map USB devices to ALSA Audio Devices, especially to remove an
// ALSA device when we are notified that its associated USB device has been removed.
+ private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>();
- private final HashMap<UsbDevice,UsbAudioDevice>
- mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>();
-
- private boolean mIsInputHeadset; // as reported by UsbDescriptorParser
- private boolean mIsOutputHeadset; // as reported by UsbDescriptorParser
-
- private final HashMap<UsbDevice,UsbMidiDevice>
- mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>();
-
- private final HashMap<String,AlsaDevice>
- mAlsaDevices = new HashMap<String,AlsaDevice>();
-
- private UsbAudioDevice mAccessoryAudioDevice = null;
+ /**
+ * List of connected MIDI devices
+ */
+ private final HashMap<String, UsbMidiDevice>
+ mMidiDevices = new HashMap<String, UsbMidiDevice>();
// UsbMidiDevice for USB peripheral mode (gadget) device
private UsbMidiDevice mPeripheralMidiDevice = null;
- private final class AlsaDevice {
- public static final int TYPE_UNKNOWN = 0;
- public static final int TYPE_PLAYBACK = 1;
- public static final int TYPE_CAPTURE = 2;
- public static final int TYPE_MIDI = 3;
-
- public int mCard;
- public int mDevice;
- public int mType;
-
- public AlsaDevice(int type, int card, int device) {
- mType = type;
- mCard = card;
- mDevice = device;
- }
-
- public boolean equals(Object obj) {
- if (! (obj instanceof AlsaDevice)) {
- return false;
- }
- AlsaDevice other = (AlsaDevice)obj;
- return (mType == other.mType && mCard == other.mCard && mDevice == other.mDevice);
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("AlsaDevice: [card: " + mCard);
- sb.append(", device: " + mDevice);
- sb.append(", type: " + mType);
- sb.append("]");
- return sb.toString();
- }
- }
-
- private final FileObserver mAlsaObserver = new FileObserver(ALSA_DIRECTORY,
- FileObserver.CREATE | FileObserver.DELETE) {
- public void onEvent(int event, String path) {
- switch (event) {
- case FileObserver.CREATE:
- alsaFileAdded(path);
- break;
- case FileObserver.DELETE:
- alsaFileRemoved(path);
- break;
- }
- }
- };
-
/* package */ UsbAlsaManager(Context context) {
mContext = context;
mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
-
- // initial scan
- if (mCardsParser.scan() != AlsaCardsParser.SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error scanning ASLA cards file.");
- }
}
public void systemReady() {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
-
- mAlsaObserver.startWatching();
-
- // add existing alsa devices
- File[] files = new File(ALSA_DIRECTORY).listFiles();
- if (files != null) {
- for (int i = 0; i < files.length; i++) {
- alsaFileAdded(files[i].getName());
- }
- }
}
// Notifies AudioService when a device is added or removed
// audioDevice - the AudioDevice that was added or removed
// enabled - if true, we're connecting a device (it's arrived), else disconnecting
- private void notifyDeviceState(UsbAudioDevice audioDevice, boolean enabled) {
+ private void notifyDeviceState(UsbAlsaDevice alsaDevice, boolean enabled) {
if (DEBUG) {
- Slog.d(TAG, "notifyDeviceState " + enabled + " " + audioDevice);
+ Slog.d(TAG, "notifyDeviceState " + enabled + " " + alsaDevice);
}
if (mAudioService == null) {
@@ -177,276 +102,151 @@ public final class UsbAlsaManager {
}
int state = (enabled ? 1 : 0);
- int alsaCard = audioDevice.mCard;
- int alsaDevice = audioDevice.mDevice;
- if (alsaCard < 0 || alsaDevice < 0) {
- Slog.e(TAG, "Invalid alsa card or device alsaCard: " + alsaCard +
- " alsaDevice: " + alsaDevice);
+ int cardNum = alsaDevice.getCardNum();
+ int deviceNum = alsaDevice.getDeviceNum();
+ if (cardNum < 0 || deviceNum < 0) {
+ Slog.e(TAG, "Invalid alsa card or device alsaCard: " + cardNum
+ + " alsaDevice: " + deviceNum);
return;
}
- String address = AudioService.makeAlsaAddressString(alsaCard, alsaDevice);
+ String address = AudioService.makeAlsaAddressString(cardNum, deviceNum);
try {
// Playback Device
- if (audioDevice.mHasPlayback) {
- int device;
- if (mIsOutputHeadset) {
- device = AudioSystem.DEVICE_OUT_USB_HEADSET;
- } else {
- device = (audioDevice == mAccessoryAudioDevice
- ? AudioSystem.DEVICE_OUT_USB_ACCESSORY
- : AudioSystem.DEVICE_OUT_USB_DEVICE);
- }
+ if (alsaDevice.hasPlayback()) {
+ int device = alsaDevice.isOutputHeadset()
+ ? AudioSystem.DEVICE_OUT_USB_HEADSET
+ : AudioSystem.DEVICE_OUT_USB_DEVICE;
if (DEBUG) {
Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
- " addr:" + address + " name:" + audioDevice.getDeviceName());
+ " addr:" + address + " name:" + alsaDevice.getDeviceName());
}
mAudioService.setWiredDeviceConnectionState(
- device, state, address, audioDevice.getDeviceName(), TAG);
+ device, state, address, alsaDevice.getDeviceName(), TAG);
}
// Capture Device
- if (audioDevice.mHasCapture) {
- int device;
- if (mIsInputHeadset) {
- device = AudioSystem.DEVICE_IN_USB_HEADSET;
- } else {
- device = (audioDevice == mAccessoryAudioDevice
- ? AudioSystem.DEVICE_IN_USB_ACCESSORY
- : AudioSystem.DEVICE_IN_USB_DEVICE);
- }
+ if (alsaDevice.hasCapture()) {
+ int device = alsaDevice.isInputHeadset()
+ ? AudioSystem.DEVICE_IN_USB_HEADSET
+ : AudioSystem.DEVICE_IN_USB_DEVICE;
mAudioService.setWiredDeviceConnectionState(
- device, state, address, audioDevice.getDeviceName(), TAG);
+ device, state, address, alsaDevice.getDeviceName(), TAG);
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
}
}
- private AlsaDevice waitForAlsaDevice(int card, int device, int type) {
- if (DEBUG) {
- Slog.e(TAG, "waitForAlsaDevice(c:" + card + " d:" + device + ")");
- }
-
- AlsaDevice testDevice = new AlsaDevice(type, card, device);
-
- // This value was empirically determined.
- final int kWaitTimeMs = 2500;
-
- synchronized(mAlsaDevices) {
- long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs;
- do {
- if (mAlsaDevices.values().contains(testDevice)) {
- return testDevice;
- }
- long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime();
- if (waitTimeMs > 0) {
- try {
- mAlsaDevices.wait(waitTimeMs);
- } catch (InterruptedException e) {
- Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
- }
- }
- } while (timeoutMs > SystemClock.elapsedRealtime());
- }
-
- Slog.e(TAG, "waitForAlsaDevice failed for " + testDevice);
- return null;
- }
-
- private void alsaFileAdded(String name) {
- Slog.i(TAG, "alsaFileAdded(" + name + ")");
- int type = AlsaDevice.TYPE_UNKNOWN;
- int card = -1, device = -1;
-
- if (name.startsWith("pcmC")) {
- if (name.endsWith("p")) {
- type = AlsaDevice.TYPE_PLAYBACK;
- } else if (name.endsWith("c")) {
- type = AlsaDevice.TYPE_CAPTURE;
- }
- } else if (name.startsWith("midiC")) {
- type = AlsaDevice.TYPE_MIDI;
- }
-
- if (type != AlsaDevice.TYPE_UNKNOWN) {
- try {
- int c_index = name.indexOf('C');
- int d_index = name.indexOf('D');
- int end = name.length();
- if (type == AlsaDevice.TYPE_PLAYBACK || type == AlsaDevice.TYPE_CAPTURE) {
- // skip trailing 'p' or 'c'
- end--;
- }
- card = Integer.parseInt(name.substring(c_index + 1, d_index));
- device = Integer.parseInt(name.substring(d_index + 1, end));
- } catch (Exception e) {
- Slog.e(TAG, "Could not parse ALSA file name " + name, e);
- return;
- }
- synchronized(mAlsaDevices) {
- if (mAlsaDevices.get(name) == null) {
- AlsaDevice alsaDevice = new AlsaDevice(type, card, device);
- Slog.d(TAG, "Adding ALSA device " + alsaDevice);
- mAlsaDevices.put(name, alsaDevice);
- mAlsaDevices.notifyAll();
- }
+ private int getAlsaDeviceListIndexFor(String deviceAddress) {
+ for (int index = 0; index < mAlsaDevices.size(); index++) {
+ if (mAlsaDevices.get(index).getDeviceAddress().equals(deviceAddress)) {
+ return index;
}
}
+ return -1;
}
- private void alsaFileRemoved(String path) {
- synchronized(mAlsaDevices) {
- AlsaDevice device = mAlsaDevices.remove(path);
- if (device != null) {
- Slog.d(TAG, "ALSA device removed: " + device);
- }
- }
- }
-
- /*
- * Select the default device of the specified card.
- */
- /* package */ UsbAudioDevice selectAudioCard(int card) {
- if (DEBUG) {
- Slog.d(TAG, "selectAudioCard() card:" + card
- + " isCardUsb(): " + mCardsParser.isCardUsb(card));
- }
- if (!mCardsParser.isCardUsb(card)) {
- // Don't. AudioPolicyManager has logic for falling back to internal devices.
- return null;
- }
-
- if (mDevicesParser.scan() != AlsaDevicesParser.SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error parsing ALSA devices file.");
+ private UsbAlsaDevice removeAlsaDeviceFromList(String deviceAddress) {
+ int index = getAlsaDeviceListIndexFor(deviceAddress);
+ if (index > -1) {
+ return mAlsaDevices.remove(index);
+ } else {
return null;
}
+ }
- int device = mDevicesParser.getDefaultDeviceNum(card);
-
- boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
- boolean hasCapture = mDevicesParser.hasCaptureDevices(card);
+ /* package */ UsbAlsaDevice selectDefaultDevice() {
if (DEBUG) {
- Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
- }
-
- int deviceClass =
- (mCardsParser.isCardUsb(card)
- ? UsbAudioDevice.kAudioDeviceClass_External
- : UsbAudioDevice.kAudioDeviceClass_Internal) |
- UsbAudioDevice.kAudioDeviceMeta_Alsa;
-
- // Playback device file needed/present?
- if (hasPlayback && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null)) {
- return null;
+ Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
}
- // Capture device file needed/present?
- if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) {
+ if (mAlsaDevices.size() > 0) {
+ UsbAlsaDevice alsaDevice = mAlsaDevices.get(0);
+ if (DEBUG) {
+ Slog.d(TAG, " alsaDevice:" + alsaDevice);
+ }
+ if (alsaDevice != null) {
+ notifyDeviceState(alsaDevice, true /*enabled*/);
+ }
+ return alsaDevice;
+ } else {
return null;
}
-
- UsbAudioDevice audioDevice =
- new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass);
- AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card);
- audioDevice.setDeviceNameAndDescription(cardRecord.mCardName, cardRecord.mCardDescription);
-
- notifyDeviceState(audioDevice, true /*enabled*/);
-
- return audioDevice;
- }
-
- /* package */ UsbAudioDevice selectDefaultDevice() {
- if (DEBUG) {
- Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
- }
- return selectAudioCard(mCardsParser.getDefaultCard());
}
- /* package */ void usbDeviceAdded(UsbDevice usbDevice,
- boolean isInputHeadset, boolean isOutputHeadset) {
+ /* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice,
+ UsbDescriptorParser parser) {
if (DEBUG) {
- Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName()
+ Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName()
+ " nm:" + usbDevice.getProductName());
}
- mIsInputHeadset = isInputHeadset;
- mIsOutputHeadset = isOutputHeadset;
-
- // Is there an audio interface in there?
- boolean isAudioDevice = false;
+ // Scan the Alsa File Space
+ mCardsParser.scan();
- // FIXME - handle multiple configurations?
- int interfaceCount = usbDevice.getInterfaceCount();
- for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < interfaceCount;
- ntrfaceIndex++) {
- UsbInterface ntrface = usbDevice.getInterface(ntrfaceIndex);
- if (ntrface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) {
- isAudioDevice = true;
- }
+ // Find the ALSA spec for this device address
+ AlsaCardsParser.AlsaCardRecord cardRec =
+ mCardsParser.findCardNumFor(deviceAddress);
+ if (cardRec == null) {
+ return;
}
+ // Add it to the devices list
+ boolean hasInput = parser.hasInput();
+ boolean hasOutput = parser.hasOutput();
if (DEBUG) {
- Slog.d(TAG, " isAudioDevice: " + isAudioDevice);
- }
- if (!isAudioDevice) {
- return;
+ Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput);
+ }
+ if (hasInput || hasOutput) {
+ boolean isInputHeadset = parser.isInputHeadset();
+ boolean isOutputHeadset = parser.isOutputHeadset();
+ UsbAlsaDevice alsaDevice =
+ new UsbAlsaDevice(cardRec.getCardNum(), 0 /*device*/, deviceAddress,
+ hasOutput, hasInput, isInputHeadset, isOutputHeadset);
+ alsaDevice.setDeviceNameAndDescription(
+ cardRec.getCardName(), cardRec.getCardDescription());
+ mAlsaDevices.add(0, alsaDevice);
+
+ // Select it
+ if (alsaDevice != null) {
+ notifyDeviceState(alsaDevice, true /*enabled*/);
+ }
}
- int addedCard = mCardsParser.getDefaultUsbCard();
-
- // If the default isn't a USB device, let the existing "select internal mechanism"
- // handle the selection.
+ // look for MIDI devices
+ boolean hasMidi = parser.hasMIDIInterface();
if (DEBUG) {
- Slog.d(TAG, " mCardsParser.isCardUsb(" + addedCard + ") = "
- + mCardsParser.isCardUsb(addedCard));
+ Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
}
- if (mCardsParser.isCardUsb(addedCard)) {
- UsbAudioDevice audioDevice = selectAudioCard(addedCard);
- if (audioDevice != null) {
- mAudioDevices.put(usbDevice, audioDevice);
- Slog.i(TAG, "USB Audio Device Added: " + audioDevice);
+ if (hasMidi && mHasMidiFeature) {
+ int device = 0;
+ Bundle properties = new Bundle();
+ String manufacturer = usbDevice.getManufacturerName();
+ String product = usbDevice.getProductName();
+ String version = usbDevice.getVersion();
+ String name;
+ if (manufacturer == null || manufacturer.isEmpty()) {
+ name = product;
+ } else if (product == null || product.isEmpty()) {
+ name = manufacturer;
+ } else {
+ name = manufacturer + " " + product;
}
-
- // look for MIDI devices
-
- // Don't need to call mDevicesParser.scan() because selectAudioCard() does this above.
- // Uncomment this next line if that behavior changes in the fugure.
- // mDevicesParser.scan()
-
- boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard);
- if (hasMidi && mHasMidiFeature) {
- int device = mDevicesParser.getDefaultDeviceNum(addedCard);
- AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
- if (alsaDevice != null) {
- Bundle properties = new Bundle();
- String manufacturer = usbDevice.getManufacturerName();
- String product = usbDevice.getProductName();
- String version = usbDevice.getVersion();
- String name;
- if (manufacturer == null || manufacturer.isEmpty()) {
- name = product;
- } else if (product == null || product.isEmpty()) {
- name = manufacturer;
- } else {
- name = manufacturer + " " + product;
- }
- properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
- properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
- properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
- properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
- properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
- usbDevice.getSerialNumber());
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, alsaDevice.mCard);
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, alsaDevice.mDevice);
- properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
-
- UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
- alsaDevice.mCard, alsaDevice.mDevice);
- if (usbMidiDevice != null) {
- mMidiDevices.put(usbDevice, usbMidiDevice);
- }
- }
+ properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
+ properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
+ properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
+ properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
+ properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+ usbDevice.getSerialNumber());
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
+ properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
+
+ UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
+ cardRec.getCardNum(), 0 /*device*/);
+ if (usbMidiDevice != null) {
+ mMidiDevices.put(deviceAddress, usbMidiDevice);
}
}
@@ -455,42 +255,31 @@ public final class UsbAlsaManager {
}
}
- /* package */ void usbDeviceRemoved(UsbDevice usbDevice) {
+ /* package */ void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) {
if (DEBUG) {
- Slog.d(TAG, "deviceRemoved(): " + usbDevice.getManufacturerName() +
- " " + usbDevice.getProductName());
+ Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")");
}
- UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice);
- Slog.i(TAG, "USB Audio Device Removed: " + audioDevice);
- if (audioDevice != null) {
- if (audioDevice.mHasPlayback || audioDevice.mHasCapture) {
- notifyDeviceState(audioDevice, false /*enabled*/);
+ // Audio
+ UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress);
+ Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
+ if (alsaDevice != null) {
+ if (alsaDevice.hasPlayback() || alsaDevice.hasCapture()) {
+ notifyDeviceState(alsaDevice, false /*enabled*/);
// if there any external devices left, select one of them
selectDefaultDevice();
}
}
- UsbMidiDevice usbMidiDevice = mMidiDevices.remove(usbDevice);
+
+ // MIDI
+ UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
if (usbMidiDevice != null) {
+ Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
IoUtils.closeQuietly(usbMidiDevice);
}
}
- /* package */ void setAccessoryAudioState(boolean enabled, int card, int device) {
- if (DEBUG) {
- Slog.d(TAG, "setAccessoryAudioState " + enabled + " " + card + " " + device);
- }
- if (enabled) {
- mAccessoryAudioDevice = new UsbAudioDevice(card, device, true, false,
- UsbAudioDevice.kAudioDeviceClass_External);
- notifyDeviceState(mAccessoryAudioDevice, true /*enabled*/);
- } else if (mAccessoryAudioDevice != null) {
- notifyDeviceState(mAccessoryAudioDevice, false /*enabled*/);
- mAccessoryAudioDevice = null;
- }
- }
-
/* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
if (!mHasMidiFeature) {
return;
@@ -515,44 +304,31 @@ public final class UsbAlsaManager {
}
//
- // Devices List
- //
-/*
- //import java.util.ArrayList;
- public ArrayList<UsbAudioDevice> getConnectedDevices() {
- ArrayList<UsbAudioDevice> devices = new ArrayList<UsbAudioDevice>(mAudioDevices.size());
- for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
- devices.add(entry.getValue());
- }
- return devices;
- }
-*/
-
- //
// Logging
//
// called by UsbService.dump
public void dump(IndentingPrintWriter pw) {
pw.println("Parsers Scan Status:");
pw.println(" Cards Parser: " + mCardsParser.getScanStatus());
- pw.println(" Devices Parser: " + mDevicesParser.getScanStatus());
+// pw.println(" Devices Parser: " + mDevicesParser.getScanStatus());
pw.println("USB Audio Devices:");
- for (UsbDevice device : mAudioDevices.keySet()) {
- pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device));
+ for (UsbAlsaDevice usbAlsaDevice : mAlsaDevices) {
+ pw.println(" " + usbAlsaDevice.getDeviceAddress() + ": " + usbAlsaDevice);
}
pw.println("USB MIDI Devices:");
- for (UsbDevice device : mMidiDevices.keySet()) {
- pw.println(" " + device.getDeviceName() + ": " + mMidiDevices.get(device));
+ for (String deviceAddr : mMidiDevices.keySet()) {
+ UsbMidiDevice midiDevice = mMidiDevices.get(deviceAddr);
+ pw.println(" " + deviceAddr + ": " + midiDevice);
}
}
/*
public void logDevicesList(String title) {
if (DEBUG) {
- for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
+ for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
Slog.i(TAG, "UsbDevice-------------------");
Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]"));
- Slog.i(TAG, "UsbAudioDevice--------------");
+ Slog.i(TAG, "UsbAlsaDevice--------------");
Slog.i(TAG, "" + entry.getValue());
}
}
@@ -564,7 +340,7 @@ public final class UsbAlsaManager {
public void logDevices(String title) {
if (DEBUG) {
Slog.i(TAG, title);
- for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
+ for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
Slog.i(TAG, entry.getValue().toShortString());
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbAudioDevice.java b/services/usb/java/com/android/server/usb/UsbAudioDevice.java
deleted file mode 100644
index 4b17dfe0f27d..000000000000
--- a/services/usb/java/com/android/server/usb/UsbAudioDevice.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions an
- * limitations under the License.
- */
-
-package com.android.server.usb;
-
-public final class UsbAudioDevice {
- private static final String TAG = "UsbAudioDevice";
- protected static final boolean DEBUG = false;
-
- public final int mCard;
- public final int mDevice;
- public final boolean mHasPlayback;
- public final boolean mHasCapture;
-
- // Device "class" flags
- public static final int kAudioDeviceClassMask = 0x00FFFFFF;
- public static final int kAudioDeviceClass_Undefined = 0x00000000;
- public static final int kAudioDeviceClass_Internal = 0x00000001;
- public static final int kAudioDeviceClass_External = 0x00000002;
- // Device meta-data flags
- public static final int kAudioDeviceMetaMask = 0xFF000000;
- public static final int kAudioDeviceMeta_Alsa = 0x80000000;
- // This member is a combination of the above bit-flags
- public final int mDeviceClass;
-
- private String mDeviceName = "";
- private String mDeviceDescription = "";
-
- public UsbAudioDevice(int card, int device,
- boolean hasPlayback, boolean hasCapture, int deviceClass) {
- mCard = card;
- mDevice = device;
- mHasPlayback = hasPlayback;
- mHasCapture = hasCapture;
- mDeviceClass = deviceClass;
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("UsbAudioDevice: [card: " + mCard);
- sb.append(", device: " + mDevice);
- sb.append(", name: " + mDeviceName);
- sb.append(", hasPlayback: " + mHasPlayback);
- sb.append(", hasCapture: " + mHasCapture);
- sb.append(", class: 0x" + Integer.toHexString(mDeviceClass) + "]");
- return sb.toString();
- }
-
- // called by logDevices
- String toShortString() {
- return "[card:" + mCard + " device:" + mDevice + " " + mDeviceName + "]";
- }
-
- String getDeviceName() {
- return mDeviceName;
- }
-
- void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
- mDeviceName = deviceName;
- mDeviceDescription = deviceDescription;
- }
-
-}
-
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index e3e5e3e1b10b..a67e7f37f947 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,7 +41,6 @@ import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
-import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.hardware.usb.gadget.V1_0.IUsbGadget;
import android.hardware.usb.gadget.V1_0.IUsbGadgetCallback;
import android.hardware.usb.gadget.V1_0.Status;
@@ -68,6 +67,8 @@ import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.SomeArgs;
@@ -86,21 +87,20 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Set;
-import java.util.StringJoiner;
/**
* UsbDeviceManager manages USB state in device mode.
*/
public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver {
- private static final String TAG = "UsbDeviceManager";
+ private static final String TAG = UsbDeviceManager.class.getSimpleName();
private static final boolean DEBUG = false;
/**
* The SharedPreference setting per user that stores the screen unlocked functions between
* sessions.
*/
- private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
+ static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
/**
* ro.bootmode value when phone boots into usual Android.
@@ -156,8 +156,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
private UsbHandler mHandler;
- private boolean mBootCompleted;
- private boolean mSystemReady;
private final Object mLock = new Object();
@@ -165,22 +163,13 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
private final ContentResolver mContentResolver;
@GuardedBy("mLock")
private UsbProfileGroupSettingsManager mCurrentSettings;
- private NotificationManager mNotificationManager;
private final boolean mHasUsbAccessory;
- private boolean mUseUsbNotification;
- private boolean mAdbEnabled;
- private boolean mAudioSourceEnabled;
- private boolean mMidiEnabled;
- private int mMidiCard;
- private int mMidiDevice;
+ @GuardedBy("mLock")
private String[] mAccessoryStrings;
private UsbDebuggingManager mDebuggingManager;
- private final UsbAlsaManager mUsbAlsaManager;
- private final UsbSettingsManager mSettingsManager;
- private Intent mBroadcastedIntent;
- private boolean mPendingBootBroadcast;
+ private final UEventObserver mUEventObserver;
+
private static Set<Integer> sBlackListedInterfaces;
- private SharedPreferences mSettings;
static {
sBlackListedInterfaces = new HashSet<>();
@@ -213,7 +202,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
/*
* Listens for uevent messages from the kernel to monitor the USB state
*/
- private final UEventObserver mUEventObserver = new UEventObserver() {
+ private final class UsbUEventObserver extends UEventObserver {
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
@@ -227,7 +216,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
startAccessoryMode();
}
}
- };
+ }
@Override
public void onKeyguardStateChanged(boolean isShowing) {
@@ -257,8 +246,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
UsbSettingsManager settingsManager) {
mContext = context;
- mUsbAlsaManager = alsaManager;
- mSettingsManager = settingsManager;
mContentResolver = context.getContentResolver();
PackageManager pm = mContext.getPackageManager();
mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
@@ -274,16 +261,24 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
Slog.i(TAG, "USB GADGET HAL not present in the device", e);
}
+ boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
+ boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
+ if (secureAdbEnabled && !dataEncrypted) {
+ mDebuggingManager = new UsbDebuggingManager(context);
+ }
+
if (halNotPresent) {
/**
* Initialze the legacy UsbHandler
*/
- mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext);
+ mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
+ mDebuggingManager, alsaManager, settingsManager);
} else {
/**
* Initialize HAL based UsbHandler
*/
- mHandler = new UsbHandlerHal(FgThread.get().getLooper());
+ mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this,
+ mDebuggingManager, alsaManager, settingsManager);
}
if (nativeIsStartRequested()) {
@@ -291,12 +286,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
startAccessoryMode();
}
- boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
- boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
- if (secureAdbEnabled && !dataEncrypted) {
- mDebuggingManager = new UsbDebuggingManager(context);
- }
-
BroadcastReceiver portReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -347,41 +336,35 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
mContext.registerReceiver(languageChangedReceiver,
new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+
+ // Watch for USB configuration changes
+ mUEventObserver = new UsbUEventObserver();
+ mUEventObserver.startObserving(USB_STATE_MATCH);
+ mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+
+ // register observer to listen for settings changes
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+ false, new AdbSettingsObserver());
}
- private UsbProfileGroupSettingsManager getCurrentSettings() {
+ UsbProfileGroupSettingsManager getCurrentSettings() {
synchronized (mLock) {
return mCurrentSettings;
}
}
+ String[] getAccessoryStrings() {
+ synchronized (mLock) {
+ return mAccessoryStrings;
+ }
+ }
+
public void systemReady() {
if (DEBUG) Slog.d(TAG, "systemReady");
LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
- // Ensure that the notification channels are set up
- if (isTv()) {
- // TV-specific notification channel
- mNotificationManager.createNotificationChannel(
- new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
- mContext.getString(
- com.android.internal.R.string
- .adb_debugging_notification_channel_tv),
- NotificationManager.IMPORTANCE_HIGH));
- }
-
- // We do not show the USB notification if the primary volume supports mass storage.
- // The legacy mass storage UI will be used instead.
- boolean massStorageSupported;
- final StorageManager storageManager = StorageManager.from(mContext);
- final StorageVolume primary = storageManager.getPrimaryVolume();
- massStorageSupported = primary != null && primary.allowMassStorage();
- mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_usbChargingMessage);
mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
@@ -410,21 +393,19 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
boolean enableAccessory = (mAccessoryStrings != null &&
mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null &&
mAccessoryStrings[UsbAccessory.MODEL_STRING] != null);
- String functions = null;
- if (enableAccessory && enableAudio) {
- functions = UsbManager.USB_FUNCTION_ACCESSORY + ","
- + UsbManager.USB_FUNCTION_AUDIO_SOURCE;
- } else if (enableAccessory) {
- functions = UsbManager.USB_FUNCTION_ACCESSORY;
- } else if (enableAudio) {
- functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE;
+ long functions = UsbManager.FUNCTION_NONE;
+ if (enableAccessory) {
+ functions |= UsbManager.FUNCTION_ACCESSORY;
+ }
+ if (enableAudio) {
+ functions |= UsbManager.FUNCTION_AUDIO_SOURCE;
}
- if (functions != null) {
+ if (functions != UsbManager.FUNCTION_NONE) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT),
ACCESSORY_REQUEST_TIMEOUT);
- setCurrentFunctions(functions, false);
+ setCurrentFunctions(functions);
}
}
@@ -451,19 +432,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
}
- private boolean isTv() {
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
- }
-
- private SharedPreferences getPinnedSharedPrefs(Context context) {
- final File prefsFile = new File(new File(
- Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
- context.getUserId(), context.getPackageName()), "shared_prefs"),
- UsbDeviceManager.class.getSimpleName() + ".xml");
- return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
- }
-
- private abstract class UsbHandler extends Handler {
+ abstract static class UsbHandler extends Handler {
// current USB state
private boolean mConnected;
@@ -471,21 +440,40 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
private boolean mSourcePower;
private boolean mSinkPower;
private boolean mConfigured;
- protected boolean mUsbDataUnlocked;
private boolean mAudioAccessoryConnected;
private boolean mAudioAccessorySupported;
- protected String mCurrentFunctions;
- protected boolean mCurrentFunctionsApplied;
+
private UsbAccessory mCurrentAccessory;
private int mUsbNotificationId;
private boolean mAdbNotificationShown;
- private int mCurrentUser;
private boolean mUsbCharging;
private boolean mHideUsbNotification;
private boolean mSupportsAllCombinations;
- private String mScreenUnlockedFunctions = UsbManager.USB_FUNCTION_NONE;
private boolean mScreenLocked;
- protected boolean mCurrentUsbFunctionsRequested;
+ private boolean mSystemReady;
+ private Intent mBroadcastedIntent;
+ private boolean mPendingBootBroadcast;
+ private boolean mAudioSourceEnabled;
+ private boolean mMidiEnabled;
+ private int mMidiCard;
+ private int mMidiDevice;
+
+ private final Context mContext;
+ private final UsbDebuggingManager mDebuggingManager;
+ private final UsbAlsaManager mUsbAlsaManager;
+ private final UsbSettingsManager mSettingsManager;
+ private NotificationManager mNotificationManager;
+
+ protected long mScreenUnlockedFunctions;
+ protected boolean mAdbEnabled;
+ protected boolean mBootCompleted;
+ protected boolean mCurrentFunctionsApplied;
+ protected boolean mUseUsbNotification;
+ protected long mCurrentFunctions;
+ protected final UsbDeviceManager mUsbDeviceManager;
+ protected final ContentResolver mContentResolver;
+ protected SharedPreferences mSettings;
+ protected int mCurrentUser;
protected boolean mCurrentUsbFunctionsReceived;
/**
@@ -494,31 +482,36 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
*/
protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
- public UsbHandler(Looper looper) {
+ UsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
+ UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+ UsbSettingsManager settingsManager) {
super(looper);
+ mContext = context;
+ mDebuggingManager = debuggingManager;
+ mUsbDeviceManager = deviceManager;
+ mUsbAlsaManager = alsaManager;
+ mSettingsManager = settingsManager;
+ mContentResolver = context.getContentResolver();
mCurrentUser = ActivityManager.getCurrentUser();
+ mScreenUnlockedFunctions = UsbManager.FUNCTION_NONE;
mScreenLocked = true;
/*
* Use the normal bootmode persistent prop to maintain state of adb across
* all boot modes.
*/
- mAdbEnabled = UsbManager.containsFunction(
- SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY),
- UsbManager.USB_FUNCTION_ADB);
+ mAdbEnabled = UsbHandlerLegacy.containsFunction(getSystemProperty(
+ USB_PERSISTENT_CONFIG_PROPERTY, ""), UsbManager.USB_FUNCTION_ADB);
- /*
- * Previous versions can set persist config to mtp/ptp but it does not
- * get reset on OTA. Reset the property here instead.
- */
- String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
- if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_PTP)) {
- SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
- UsbManager.removeFunction(UsbManager.removeFunction(persisted,
- UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP));
- }
+ // We do not show the USB notification if the primary volume supports mass storage.
+ // The legacy mass storage UI will be used instead.
+ final StorageManager storageManager = StorageManager.from(mContext);
+ final StorageVolume primary = storageManager.getPrimaryVolume();
+
+ boolean massStorageSupported = primary != null && primary.allowMassStorage();
+ mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_usbChargingMessage);
}
public void sendMessage(int what, boolean arg) {
@@ -602,20 +595,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
if (enable != mAdbEnabled) {
mAdbEnabled = enable;
- String oldFunctions = mCurrentFunctions;
-
- // Persist the adb setting
- String newFunction = applyAdbFunction(SystemProperties.get(
- USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
- SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction);
- // Remove mtp from the config if file transfer is not enabled
- if (oldFunctions.equals(UsbManager.USB_FUNCTION_MTP) &&
- !mUsbDataUnlocked && enable) {
- oldFunctions = UsbManager.USB_FUNCTION_NONE;
+ if (enable) {
+ setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_ADB);
+ } else {
+ setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, "");
}
- setEnabledFunctions(oldFunctions, true, mUsbDataUnlocked);
+ setEnabledFunctions(mCurrentFunctions, true);
updateAdbNotification(false);
}
@@ -624,21 +611,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
}
- protected String applyAdbFunction(String functions) {
- // Do not pass null pointer to the UsbManager.
- // There isnt a check there.
- if (functions == null) {
- functions = "";
- }
- if (mAdbEnabled) {
- functions = UsbManager.addFunction(functions, UsbManager.USB_FUNCTION_ADB);
- } else {
- functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
- }
- return functions;
- }
-
- private boolean isUsbTransferAllowed() {
+ protected boolean isUsbTransferAllowed() {
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
return !userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
}
@@ -650,12 +623,13 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
if (mConfigured && enteringAccessoryMode) {
// successfully entered accessory mode
- if (mAccessoryStrings != null) {
- mCurrentAccessory = new UsbAccessory(mAccessoryStrings);
+ String[] accessoryStrings = mUsbDeviceManager.getAccessoryStrings();
+ if (accessoryStrings != null) {
+ mCurrentAccessory = new UsbAccessory(accessoryStrings);
Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
// defer accessoryAttached if system is not ready
if (mBootCompleted) {
- getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
} // else handle in boot completed
} else {
Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -673,17 +647,24 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
// make sure accessory mode is off
// and restore default functions
Slog.d(TAG, "exited USB accessory mode");
- setEnabledFunctions(null, false, false);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
if (mCurrentAccessory != null) {
if (mBootCompleted) {
mSettingsManager.usbAccessoryRemoved(mCurrentAccessory);
}
mCurrentAccessory = null;
- mAccessoryStrings = null;
}
}
+ protected SharedPreferences getPinnedSharedPrefs(Context context) {
+ final File prefsFile = new File(new File(
+ Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+ context.getUserId(), context.getPackageName()), "shared_prefs"),
+ UsbDeviceManager.class.getSimpleName() + ".xml");
+ return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ }
+
private boolean isUsbStateChanged(Intent intent) {
final Set<String> keySet = intent.getExtras().keySet();
if (mBroadcastedIntent == null) {
@@ -706,7 +687,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
return false;
}
- protected void updateUsbStateBroadcastIfNeeded(boolean configChanged) {
+ protected void updateUsbStateBroadcastIfNeeded(long functions,
+ boolean configChanged) {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -716,18 +698,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
- isUsbTransferAllowed() && mUsbDataUnlocked);
+ isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions));
intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);
- if (mCurrentFunctions != null) {
- String[] functions = mCurrentFunctions.split(",");
- for (int i = 0; i < functions.length; i++) {
- final String function = functions[i];
- if (UsbManager.USB_FUNCTION_NONE.equals(function)) {
- continue;
- }
- intent.putExtra(function, true);
- }
+ long remainingFunctions = functions;
+ while (remainingFunctions != 0) {
+ intent.putExtra(UsbManager.usbFunctionsToString(
+ Long.highestOneBit(remainingFunctions)), true);
+ remainingFunctions -= Long.highestOneBit(remainingFunctions);
}
// send broadcast intent only if the USB state has changed
@@ -739,44 +717,20 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ sendStickyBroadcast(intent);
mBroadcastedIntent = intent;
}
- private void updateUsbFunctions() {
- updateAudioSourceFunction();
- updateMidiFunction();
+ protected void sendStickyBroadcast(Intent intent) {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- private void updateAudioSourceFunction() {
- boolean enabled = UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_AUDIO_SOURCE);
- if (enabled != mAudioSourceEnabled) {
- int card = -1;
- int device = -1;
-
- if (enabled) {
- Scanner scanner = null;
- try {
- scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH));
- card = scanner.nextInt();
- device = scanner.nextInt();
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "could not open audio source PCM file", e);
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- }
- }
- mUsbAlsaManager.setAccessoryAudioState(enabled, card, device);
- mAudioSourceEnabled = enabled;
- }
+ private void updateUsbFunctions() {
+ updateMidiFunction();
}
private void updateMidiFunction() {
- boolean enabled = UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_MIDI);
+ boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
if (enabled != mMidiEnabled) {
if (enabled) {
Scanner scanner = null;
@@ -800,11 +754,21 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
private void setScreenUnlockedFunctions() {
- setEnabledFunctions(mScreenUnlockedFunctions, false,
- UsbManager.containsFunction(mScreenUnlockedFunctions,
- UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(mScreenUnlockedFunctions,
- UsbManager.USB_FUNCTION_PTP));
+ setEnabledFunctions(mScreenUnlockedFunctions, false);
+ }
+
+ /**
+ * Returns the functions that are passed down to the low level driver once adb and
+ * charging are accounted for.
+ */
+ long getAppliedFunctions(long functions) {
+ if (functions == UsbManager.FUNCTION_NONE) {
+ return getChargingFunctions();
+ }
+ if (mAdbEnabled) {
+ return functions | UsbManager.FUNCTION_ADB;
+ }
+ return functions;
}
@Override
@@ -817,10 +781,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
updateUsbNotification(false);
updateAdbNotification(false);
if (mBootCompleted) {
- updateUsbStateBroadcastIfNeeded(false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
+ false);
}
- if (UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_ACCESSORY)) {
+ if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
updateCurrentAccessory();
}
if (mBootCompleted) {
@@ -828,11 +792,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
&& !hasMessages(MSG_FUNCTION_SWITCH_TIMEOUT)) {
// restore defaults when USB is disconnected
if (!mScreenLocked
- && !UsbManager.USB_FUNCTION_NONE.equals(
- mScreenUnlockedFunctions)) {
+ && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
setScreenUnlockedFunctions();
} else {
- setEnabledFunctions(null, !mAdbEnabled, false);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
}
}
updateUsbFunctions();
@@ -867,7 +830,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
updateUsbNotification(false);
if (mBootCompleted) {
if (mHostConnected || prevHostConnected) {
- updateUsbStateBroadcastIfNeeded(false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
+ false);
}
} else {
mPendingBootBroadcast = true;
@@ -913,17 +877,17 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
setAdbEnabled(msg.arg1 == 1);
break;
case MSG_SET_CURRENT_FUNCTIONS:
- String functions = (String) msg.obj;
- setEnabledFunctions(functions, false, msg.arg1 == 1);
+ long functions = (Long) msg.obj;
+ setEnabledFunctions(functions, false);
break;
case MSG_SET_SCREEN_UNLOCKED_FUNCTIONS:
- mScreenUnlockedFunctions = (String) msg.obj;
+ mScreenUnlockedFunctions = (Long) msg.obj;
SharedPreferences.Editor editor = mSettings.edit();
editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
- mCurrentUser), mScreenUnlockedFunctions);
+ mCurrentUser),
+ UsbManager.usbFunctionsToString(mScreenUnlockedFunctions));
editor.commit();
- if (!mScreenLocked && !UsbManager.USB_FUNCTION_NONE.equals(
- mScreenUnlockedFunctions)) {
+ if (!mScreenLocked && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
// If the screen is unlocked, also set current functions.
setScreenUnlockedFunctions();
}
@@ -936,22 +900,21 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
if (mSettings == null && !mScreenLocked) {
// Shared preferences aren't accessible until the user has been unlocked.
mSettings = getPinnedSharedPrefs(mContext);
- mScreenUnlockedFunctions = mSettings.getString(
+ mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString(
+ mSettings.getString(
String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
- UsbManager.USB_FUNCTION_NONE);
+ ""));
}
if (!mBootCompleted) {
break;
}
if (mScreenLocked) {
if (!mConnected) {
- setEnabledFunctions(null, false, false);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
} else {
- if (!UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)
- && (UsbManager.USB_FUNCTION_ADB.equals(mCurrentFunctions)
- || (UsbManager.USB_FUNCTION_MTP.equals(mCurrentFunctions)
- && !mUsbDataUnlocked))) {
+ if (mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE
+ && mCurrentFunctions == UsbManager.FUNCTION_NONE) {
// Set the screen unlocked functions if current function is charging.
setScreenUnlockedFunctions();
}
@@ -959,13 +922,24 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
break;
case MSG_UPDATE_USER_RESTRICTIONS:
// Restart the USB stack if USB transfer is enabled but no longer allowed.
- final boolean forceRestart = mUsbDataUnlocked
- && isUsbDataTransferActive()
- && !isUsbTransferAllowed();
- setEnabledFunctions(
- mCurrentFunctions, forceRestart, mUsbDataUnlocked && !forceRestart);
+ if (isUsbDataTransferActive(mCurrentFunctions) && !isUsbTransferAllowed()) {
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, true);
+ }
break;
case MSG_SYSTEM_READY:
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // Ensure that the notification channels are set up
+ if (isTv()) {
+ // TV-specific notification channel
+ mNotificationManager.createNotificationChannel(
+ new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
+ mContext.getString(
+ com.android.internal.R.string
+ .adb_debugging_notification_channel_tv),
+ NotificationManager.IMPORTANCE_HIGH));
+ }
mSystemReady = true;
finishBoot();
break;
@@ -984,10 +958,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
mCurrentUser = msg.arg1;
mScreenLocked = true;
- mScreenUnlockedFunctions = mSettings.getString(
- String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
- UsbManager.USB_FUNCTION_NONE);
- setEnabledFunctions(null, false, false);
+ if (mSettings != null) {
+ mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString(
+ mSettings.getString(
+ String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
+ mCurrentUser),
+ ""));
+ }
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
break;
}
@@ -995,9 +973,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
if (DEBUG) {
Slog.v(TAG, "Accessory mode enter timeout: " + mConnected);
}
- if (!mConnected || !UsbManager.containsFunction(
- mCurrentFunctions,
- UsbManager.USB_FUNCTION_ACCESSORY)) {
+ if (!mConnected || (mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) == 0) {
notifyAccessoryModeExit();
}
break;
@@ -1008,17 +984,17 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
protected void finishBoot() {
if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
if (mPendingBootBroadcast) {
- updateUsbStateBroadcastIfNeeded(false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), false);
mPendingBootBroadcast = false;
}
if (!mScreenLocked
- && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) {
+ && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
setScreenUnlockedFunctions();
} else {
- setEnabledFunctions(null, false, false);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
if (mCurrentAccessory != null) {
- getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
}
if (mDebuggingManager != null) {
mDebuggingManager.setAdbEnabled(mAdbEnabled);
@@ -1026,8 +1002,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
// make sure the ADB_ENABLED setting value matches the current state
try {
- Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+ putGlobalSettings(mContentResolver, Settings.Global.ADB_ENABLED,
+ mAdbEnabled ? 1 : 0);
} catch (SecurityException e) {
// If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
// be changed.
@@ -1040,9 +1016,9 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
}
- private boolean isUsbDataTransferActive() {
- return UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP);
+ protected boolean isUsbDataTransferActive(long functions) {
+ return (functions & UsbManager.FUNCTION_MTP) != 0
+ || (functions & UsbManager.FUNCTION_PTP) != 0;
}
public UsbAccessory getCurrentAccessory() {
@@ -1051,7 +1027,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
protected void updateUsbNotification(boolean force) {
if (mNotificationManager == null || !mUseUsbNotification
- || ("0".equals(SystemProperties.get("persist.charging.notify")))) {
+ || ("0".equals(getSystemProperty("persist.charging.notify", "")))) {
return;
}
@@ -1070,30 +1046,37 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
int id = 0;
int titleRes = 0;
Resources r = mContext.getResources();
+ CharSequence message = r.getText(
+ com.android.internal.R.string.usb_notification_message);
if (mAudioAccessoryConnected && !mAudioAccessorySupported) {
titleRes = com.android.internal.R.string.usb_unsupported_audio_accessory_title;
id = SystemMessage.NOTE_USB_AUDIO_ACCESSORY_NOT_SUPPORTED;
} else if (mConnected) {
- if (UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_MTP) && mUsbDataUnlocked) {
+ if (mCurrentFunctions == UsbManager.FUNCTION_MTP) {
titleRes = com.android.internal.R.string.usb_mtp_notification_title;
id = SystemMessage.NOTE_USB_MTP;
- } else if (UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_PTP) && mUsbDataUnlocked) {
+ } else if (mCurrentFunctions == UsbManager.FUNCTION_PTP) {
titleRes = com.android.internal.R.string.usb_ptp_notification_title;
id = SystemMessage.NOTE_USB_PTP;
- } else if (UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_MIDI)) {
+ } else if (mCurrentFunctions == UsbManager.FUNCTION_MIDI) {
titleRes = com.android.internal.R.string.usb_midi_notification_title;
id = SystemMessage.NOTE_USB_MIDI;
- } else if (UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_ACCESSORY)) {
+ } else if (mCurrentFunctions == UsbManager.FUNCTION_RNDIS) {
+ titleRes = com.android.internal.R.string.usb_tether_notification_title;
+ id = SystemMessage.NOTE_USB_TETHER;
+ } else if (mCurrentFunctions == UsbManager.FUNCTION_ACCESSORY) {
titleRes = com.android.internal.R.string.usb_accessory_notification_title;
id = SystemMessage.NOTE_USB_ACCESSORY;
- } else if (mSourcePower) {
- titleRes = com.android.internal.R.string.usb_supplying_notification_title;
- id = SystemMessage.NOTE_USB_SUPPLYING;
- } else {
+ }
+ if (mSourcePower) {
+ if (titleRes != 0) {
+ message = r.getText(
+ com.android.internal.R.string.usb_power_notification_message);
+ } else {
+ titleRes = com.android.internal.R.string.usb_supplying_notification_title;
+ id = SystemMessage.NOTE_USB_SUPPLYING;
+ }
+ } else if (titleRes == 0) {
titleRes = com.android.internal.R.string.usb_charging_notification_title;
id = SystemMessage.NOTE_USB_CHARGING;
}
@@ -1113,7 +1096,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
mUsbNotificationId = 0;
}
if (id != 0) {
- CharSequence message;
CharSequence title = r.getText(titleRes);
PendingIntent pi;
String channel;
@@ -1123,12 +1105,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
.usb_unsupported_audio_accessory_title) {
Intent intent = Intent.makeRestartActivityTask(
new ComponentName("com.android.settings",
- "com.android.settings.deviceinfo.UsbModeChooserActivity"));
+ "com.android.settings.Settings$UsbDetailsActivity"));
pi = PendingIntent.getActivityAsUser(mContext, 0,
intent, 0, null, UserHandle.CURRENT);
channel = SystemNotificationChannels.USB;
- message = r.getText(
- com.android.internal.R.string.usb_notification_message);
} else {
final Intent intent = new Intent();
intent.setClassName("com.android.settings",
@@ -1184,7 +1164,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
final int titleRes = com.android.internal.R.string.adb_active_notification_title;
if (mAdbEnabled && mConnected) {
- if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
+ if ("0".equals(getSystemProperty("persist.adb.notify", ""))) return;
if (force && mAdbNotificationShown) {
mAdbNotificationShown = false;
@@ -1230,20 +1210,41 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
}
- protected String getChargingFunctions() {
+ private boolean isTv() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
+ protected long getChargingFunctions() {
// if ADB is enabled, reset functions to ADB
// else enable MTP as usual.
if (mAdbEnabled) {
- return UsbManager.USB_FUNCTION_ADB;
+ return UsbManager.FUNCTION_ADB;
} else {
- return UsbManager.USB_FUNCTION_MTP;
+ return UsbManager.FUNCTION_MTP;
}
}
- public boolean isFunctionEnabled(String function) {
- return UsbManager.containsFunction(mCurrentFunctions, function);
+ protected void setSystemProperty(String prop, String val) {
+ SystemProperties.set(prop, val);
+ }
+
+ protected String getSystemProperty(String prop, String def) {
+ return SystemProperties.get(prop, def);
+ }
+
+ protected void putGlobalSettings(ContentResolver contentResolver, String setting, int val) {
+ Settings.Global.putInt(contentResolver, setting, val);
+ }
+
+ public long getEnabledFunctions() {
+ return mCurrentFunctions;
}
+ public long getScreenUnlockedFunctions() {
+ return mScreenUnlockedFunctions;
+ }
+
+
public void dump(IndentingPrintWriter pw) {
pw.println("USB Device State:");
pw.println(" mCurrentFunctions: " + mCurrentFunctions);
@@ -1252,7 +1253,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
pw.println(" mScreenLocked: " + mScreenLocked);
pw.println(" mConnected: " + mConnected);
pw.println(" mConfigured: " + mConfigured);
- pw.println(" mUsbDataUnlocked: " + mUsbDataUnlocked);
pw.println(" mCurrentAccessory: " + mCurrentAccessory);
pw.println(" mHostConnected: " + mHostConnected);
pw.println(" mSourcePower: " + mSourcePower);
@@ -1275,12 +1275,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
/**
* Evaluates USB function policies and applies the change accordingly.
*/
- protected abstract void setEnabledFunctions(String functions, boolean forceRestart,
- boolean usbDataUnlocked);
-
+ protected abstract void setEnabledFunctions(long functions, boolean forceRestart);
}
- private final class UsbHandlerLegacy extends UsbHandler {
+ private static final class UsbHandlerLegacy extends UsbHandler {
/**
* The non-persistent property which stores the current USB settings.
*/
@@ -1293,46 +1291,44 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
private HashMap<String, HashMap<String, Pair<String, String>>> mOemModeMap;
private String mCurrentOemFunctions;
+ private String mCurrentFunctionsStr;
+ private boolean mUsbDataUnlocked;
- UsbHandlerLegacy(Looper looper, Context context) {
- super(looper);
+ UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager,
+ UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+ UsbSettingsManager settingsManager) {
+ super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
try {
readOemUsbOverrideConfig(context);
// Restore default functions.
- mCurrentOemFunctions = SystemProperties.get(getPersistProp(false),
+ mCurrentOemFunctions = getSystemProperty(getPersistProp(false),
UsbManager.USB_FUNCTION_NONE);
if (isNormalBoot()) {
- mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
+ mCurrentFunctionsStr = getSystemProperty(USB_CONFIG_PROPERTY,
UsbManager.USB_FUNCTION_NONE);
- mCurrentFunctionsApplied = mCurrentFunctions.equals(
- SystemProperties.get(USB_STATE_PROPERTY));
+ mCurrentFunctionsApplied = mCurrentFunctionsStr.equals(
+ getSystemProperty(USB_STATE_PROPERTY, UsbManager.USB_FUNCTION_NONE));
} else {
- mCurrentFunctions = SystemProperties.get(getPersistProp(true),
+ mCurrentFunctionsStr = getSystemProperty(getPersistProp(true),
UsbManager.USB_FUNCTION_NONE);
- mCurrentFunctionsApplied = SystemProperties.get(USB_CONFIG_PROPERTY,
+ mCurrentFunctionsApplied = getSystemProperty(USB_CONFIG_PROPERTY,
UsbManager.USB_FUNCTION_NONE).equals(
- SystemProperties.get(USB_STATE_PROPERTY));
+ getSystemProperty(USB_STATE_PROPERTY, UsbManager.USB_FUNCTION_NONE));
}
+ // Mask out adb, since it is stored in mAdbEnabled
+ mCurrentFunctions = UsbManager.usbFunctionsFromString(mCurrentFunctionsStr)
+ & ~UsbManager.FUNCTION_ADB;
mCurrentUsbFunctionsReceived = true;
String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
updateState(state);
-
- // register observer to listen for settings changes
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
- false, new AdbSettingsObserver());
-
- // Watch for USB configuration changes
- mUEventObserver.startObserving(USB_STATE_MATCH);
- mUEventObserver.startObserving(ACCESSORY_START_MATCH);
} catch (Exception e) {
Slog.e(TAG, "Error initializing UsbHandler", e);
}
}
private void readOemUsbOverrideConfig(Context context) {
- String[] configList = mContext.getResources().getStringArray(
+ String[] configList = context.getResources().getStringArray(
com.android.internal.R.array.config_oemUsbModeOverride);
if (configList != null) {
@@ -1367,7 +1363,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
return usbFunctions;
}
- String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+ String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown");
Slog.d(TAG, "applyOemOverride usbfunctions=" + usbFunctions + " bootmode=" + bootMode);
Map<String, Pair<String, String>> overridesMap =
@@ -1386,25 +1382,22 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
if (!overrideFunctions.second.equals("")) {
String newFunction;
if (mAdbEnabled) {
- newFunction = UsbManager.addFunction(overrideFunctions.second,
+ newFunction = addFunction(overrideFunctions.second,
UsbManager.USB_FUNCTION_ADB);
} else {
newFunction = overrideFunctions.second;
}
Slog.d(TAG, "OEM USB override persisting: " + newFunction + "in prop: "
+ getPersistProp(false));
- SystemProperties.set(getPersistProp(false),
- newFunction);
+ setSystemProperty(getPersistProp(false), newFunction);
}
return overrideFunctions.first;
} else if (mAdbEnabled) {
- String newFunction = UsbManager.addFunction(UsbManager.USB_FUNCTION_NONE,
+ String newFunction = addFunction(UsbManager.USB_FUNCTION_NONE,
UsbManager.USB_FUNCTION_ADB);
- SystemProperties.set(getPersistProp(false),
- newFunction);
+ setSystemProperty(getPersistProp(false), newFunction);
} else {
- SystemProperties.set(getPersistProp(false),
- UsbManager.USB_FUNCTION_NONE);
+ setSystemProperty(getPersistProp(false), UsbManager.USB_FUNCTION_NONE);
}
}
// return passed in functions as is.
@@ -1417,7 +1410,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
String value = null;
for (int i = 0; i < 20; i++) {
// State transition is done when sys.usb.state is set to the new configuration
- value = SystemProperties.get(USB_STATE_PROPERTY);
+ value = getSystemProperty(USB_STATE_PROPERTY, "");
if (state.equals(value)) return true;
SystemClock.sleep(50);
}
@@ -1432,14 +1425,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
* we always set it due to b/23631400, where adbd was getting killed
* and not restarted due to property timeouts on some devices
*/
- SystemProperties.set(USB_CONFIG_PROPERTY, config);
+ setSystemProperty(USB_CONFIG_PROPERTY, config);
}
@Override
- protected void setEnabledFunctions(String functions, boolean forceRestart,
- boolean usbDataUnlocked) {
+ protected void setEnabledFunctions(long usbFunctions, boolean forceRestart) {
+ boolean usbDataUnlocked = isUsbDataTransferActive(usbFunctions);
if (DEBUG) {
- Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+ Slog.d(TAG, "setEnabledFunctions functions=" + usbFunctions + ", "
+ "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
}
@@ -1452,9 +1445,9 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
/**
* Try to set the enabled functions.
*/
- final String oldFunctions = mCurrentFunctions;
+ final long oldFunctions = mCurrentFunctions;
final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
- if (trySetEnabledFunctions(functions, forceRestart)) {
+ if (trySetEnabledFunctions(usbFunctions, forceRestart)) {
return;
}
@@ -1464,7 +1457,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
* user restrictions independently of any other new functions we were
* trying to activate.
*/
- if (oldFunctionsApplied && !oldFunctions.equals(functions)) {
+ if (oldFunctionsApplied && oldFunctions != usbFunctions) {
Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
if (trySetEnabledFunctions(oldFunctions, false)) {
return;
@@ -1475,7 +1468,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
* Still didn't work. Try to restore the default functions.
*/
Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
- if (trySetEnabledFunctions(null, false)) {
+ if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
return;
}
@@ -1484,7 +1477,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
* Try to get ADB working if enabled.
*/
Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
- if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) {
+ if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
return;
}
@@ -1495,30 +1488,49 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
private boolean isNormalBoot() {
- String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+ String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown");
return bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown");
}
- private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
+ protected String applyAdbFunction(String functions) {
+ // Do not pass null pointer to the UsbManager.
+ // There isn't a check there.
+ if (functions == null) {
+ functions = "";
+ }
+ if (mAdbEnabled) {
+ functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
+ } else {
+ functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
+ }
+ return functions;
+ }
+
+ private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) {
+ String functions = null;
+ if (usbFunctions != UsbManager.FUNCTION_NONE) {
+ functions = UsbManager.usbFunctionsToString(usbFunctions);
+ }
+ mCurrentFunctions = usbFunctions;
if (functions == null || applyAdbFunction(functions)
.equals(UsbManager.USB_FUNCTION_NONE)) {
- functions = getChargingFunctions();
+ functions = UsbManager.usbFunctionsToString(getChargingFunctions());
}
functions = applyAdbFunction(functions);
String oemFunctions = applyOemOverrideFunction(functions);
- if (!isNormalBoot() && !mCurrentFunctions.equals(functions)) {
- SystemProperties.set(getPersistProp(true), functions);
+ if (!isNormalBoot() && !mCurrentFunctionsStr.equals(functions)) {
+ setSystemProperty(getPersistProp(true), functions);
}
if ((!functions.equals(oemFunctions)
&& !mCurrentOemFunctions.equals(oemFunctions))
- || !mCurrentFunctions.equals(functions)
+ || !mCurrentFunctionsStr.equals(functions)
|| !mCurrentFunctionsApplied
|| forceRestart) {
Slog.i(TAG, "Setting USB config to " + functions);
- mCurrentFunctions = functions;
+ mCurrentFunctionsStr = functions;
mCurrentOemFunctions = oemFunctions;
mCurrentFunctionsApplied = false;
@@ -1538,12 +1550,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
setUsbConfig(oemFunctions);
if (mBootCompleted
- && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+ && (containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+ || containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
/**
* Start up dependent services.
*/
- updateUsbStateBroadcastIfNeeded(true);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), true);
}
if (!waitForState(oemFunctions)) {
@@ -1557,7 +1569,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
private String getPersistProp(boolean functions) {
- String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+ String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown");
String persistProp = USB_PERSISTENT_CONFIG_PROPERTY;
if (!(bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"))) {
if (functions) {
@@ -1568,9 +1580,54 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
return persistProp;
}
+
+ private static String addFunction(String functions, String function) {
+ if (UsbManager.USB_FUNCTION_NONE.equals(functions)) {
+ return function;
+ }
+ if (!containsFunction(functions, function)) {
+ if (functions.length() > 0) {
+ functions += ",";
+ }
+ functions += function;
+ }
+ return functions;
+ }
+
+ private static String removeFunction(String functions, String function) {
+ String[] split = functions.split(",");
+ for (int i = 0; i < split.length; i++) {
+ if (function.equals(split[i])) {
+ split[i] = null;
+ }
+ }
+ if (split.length == 1 && split[0] == null) {
+ return UsbManager.USB_FUNCTION_NONE;
+ }
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < split.length; i++) {
+ String s = split[i];
+ if (s != null) {
+ if (builder.length() > 0) {
+ builder.append(",");
+ }
+ builder.append(s);
+ }
+ }
+ return builder.toString();
+ }
+
+ static boolean containsFunction(String functions, String function) {
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
+ }
}
- private final class UsbHandlerHal extends UsbHandler {
+ private static final class UsbHandlerHal extends UsbHandler {
/**
* Proxy object for the usb gadget hal daemon.
@@ -1627,9 +1684,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
*/
protected static final String ADBD = "adbd";
+ protected boolean mCurrentUsbFunctionsRequested;
- UsbHandlerHal(Looper looper) {
- super(looper);
+ UsbHandlerHal(Looper looper, Context context, UsbDeviceManager deviceManager,
+ UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+ UsbSettingsManager settingsManager) {
+ super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
try {
ServiceNotification serviceNotification = new ServiceNotification();
@@ -1645,25 +1705,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
mGadgetProxy = IUsbGadget.getService(true);
mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
USB_GADGET_HAL_DEATH_COOKIE);
- mCurrentFunctions = UsbManager.USB_FUNCTION_NONE;
+ mCurrentFunctions = UsbManager.FUNCTION_NONE;
mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
mCurrentUsbFunctionsRequested = true;
}
String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
updateState(state);
-
- /**
- * Register observer to listen for settings changes.
- */
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
- false, new AdbSettingsObserver());
-
- /**
- * Watch for USB configuration changes.
- */
- mUEventObserver.startObserving(USB_STATE_MATCH);
- mUEventObserver.startObserving(ACCESSORY_START_MATCH);
} catch (NoSuchElementException e) {
Slog.e(TAG, "Usb gadget hal not found", e);
} catch (RemoteException e) {
@@ -1696,7 +1743,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
USB_GADGET_HAL_DEATH_COOKIE);
if (!mCurrentFunctionsApplied) {
- setCurrentFunctions(mCurrentFunctions, mUsbDataUnlocked);
+ setEnabledFunctions(mCurrentFunctions, false);
}
} catch (NoSuchElementException e) {
Slog.e(TAG, "Usb gadget hal not found", e);
@@ -1711,12 +1758,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_CHARGING_FUNCTIONS:
- setEnabledFunctions(null, false, mUsbDataUnlocked);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
break;
case MSG_SET_FUNCTIONS_TIMEOUT:
Slog.e(TAG, "Set functions timed out! no reply from usb hal");
if (msg.arg1 != 1) {
- setEnabledFunctions(null, false, mUsbDataUnlocked);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
break;
case MSG_GET_CURRENT_USB_FUNCTIONS:
@@ -1725,7 +1772,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
if (mCurrentUsbFunctionsRequested) {
Slog.e(TAG, "updating mCurrentFunctions");
- mCurrentFunctions = functionListToString((Long) msg.obj);
+ // Mask out adb, since it is stored in mAdbEnabled
+ mCurrentFunctions = ((Long) msg.obj) & ~UsbManager.FUNCTION_ADB;
Slog.e(TAG,
"mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
mCurrentFunctionsApplied = msg.arg1 == 1;
@@ -1737,7 +1785,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
* Dont force to default when the configuration is already set to default.
*/
if (msg.arg1 != 1) {
- setEnabledFunctions(null, !mAdbEnabled, false);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
}
break;
default:
@@ -1789,70 +1837,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
}
- private long stringToFunctionList(String config) {
- long functionsMask = 0;
- String[] functions = config.split(",");
- for (int i = 0; i < functions.length; i++) {
- switch (functions[i]) {
- case "none":
- functionsMask |= GadgetFunction.NONE;
- break;
- case "adb":
- functionsMask |= GadgetFunction.ADB;
- break;
- case "mtp":
- functionsMask |= GadgetFunction.MTP;
- break;
- case "ptp":
- functionsMask |= GadgetFunction.PTP;
- break;
- case "midi":
- functionsMask |= GadgetFunction.MIDI;
- break;
- case "accessory":
- functionsMask |= GadgetFunction.ACCESSORY;
- break;
- case "rndis":
- functionsMask |= GadgetFunction.RNDIS;
- break;
- }
- }
- return functionsMask;
- }
-
- private String functionListToString(Long functionList) {
- StringJoiner functions = new StringJoiner(",");
- if (functionList == GadgetFunction.NONE) {
- functions.add("none");
- return functions.toString();
- }
- if ((functionList & GadgetFunction.ADB) != 0) {
- functions.add("adb");
- }
- if ((functionList & GadgetFunction.MTP) != 0) {
- functions.add("mtp");
- }
- if ((functionList & GadgetFunction.PTP) != 0) {
- functions.add("ptp");
- }
- if ((functionList & GadgetFunction.MIDI) != 0) {
- functions.add("midi");
- }
- if ((functionList & GadgetFunction.ACCESSORY) != 0) {
- functions.add("accessory");
- }
- if ((functionList & GadgetFunction.RNDIS) != 0) {
- functions.add("rndis");
- }
- /**
- * Remove the trailing comma.
- */
- return functions.toString();
- }
-
-
- private void setUsbConfig(String config, boolean chargingFunctions) {
- Long functions = stringToFunctionList(config);
+ private void setUsbConfig(long config, boolean chargingFunctions) {
if (true) Slog.d(TAG, "setUsbConfig(" + config + ") request:" + ++mCurrentRequest);
/**
* Cancel any ongoing requests, if present.
@@ -1867,20 +1852,20 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
return;
}
try {
- if ((functions & GadgetFunction.ADB) != 0) {
+ if ((config & UsbManager.FUNCTION_ADB) != 0) {
/**
* Start adbd if ADB function is included in the configuration.
*/
- SystemProperties.set(CTL_START, ADBD);
+ setSystemProperty(CTL_START, ADBD);
} else {
/**
* Stop adbd otherwise.
*/
- SystemProperties.set(CTL_STOP, ADBD);
+ setSystemProperty(CTL_STOP, ADBD);
}
UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
- functions, chargingFunctions);
- mGadgetProxy.setCurrentUsbFunctions(functions, usbGadgetCallback,
+ config, chargingFunctions);
+ mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback,
SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
SET_FUNCTIONS_TIMEOUT_MS);
@@ -1894,49 +1879,29 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
@Override
- protected void setEnabledFunctions(String functions, boolean forceRestart,
- boolean usbDataUnlocked) {
+ protected void setEnabledFunctions(long functions, boolean forceRestart) {
if (DEBUG) {
Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
- + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
+ + "forceRestart=" + forceRestart);
}
-
- if (usbDataUnlocked != mUsbDataUnlocked) {
- mUsbDataUnlocked = usbDataUnlocked;
- updateUsbNotification(false);
- forceRestart = true;
- }
-
- trySetEnabledFunctions(functions, forceRestart);
- }
-
- private void trySetEnabledFunctions(String functions, boolean forceRestart) {
- boolean chargingFunctions = false;
-
- if (functions == null || applyAdbFunction(functions)
- .equals(UsbManager.USB_FUNCTION_NONE)) {
- functions = getChargingFunctions();
- chargingFunctions = true;
- }
- functions = applyAdbFunction(functions);
-
- if (!mCurrentFunctions.equals(functions)
+ if (mCurrentFunctions != functions
|| !mCurrentFunctionsApplied
|| forceRestart) {
- Slog.i(TAG, "Setting USB config to " + functions);
+ Slog.i(TAG, "Setting USB config to " + UsbManager.usbFunctionsToString(functions));
mCurrentFunctions = functions;
mCurrentFunctionsApplied = false;
// set the flag to false as that would be stale value
mCurrentUsbFunctionsRequested = false;
+ boolean chargingFunctions = functions == UsbManager.FUNCTION_NONE;
+ functions = getAppliedFunctions(functions);
+
// Set the new USB configuration.
- setUsbConfig(mCurrentFunctions, chargingFunctions);
+ setUsbConfig(functions, chargingFunctions);
- if (mBootCompleted
- && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+ if (mBootCompleted && isUsbDataTransferActive(functions)) {
// Start up dependent services.
- updateUsbStateBroadcastIfNeeded(true);
+ updateUsbStateBroadcastIfNeeded(functions, true);
}
}
}
@@ -1968,27 +1933,37 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
return nativeOpenAccessory();
}
- /**
- * Checks whether the function is present in the USB configuration.
- *
- * @param function function to be checked.
- */
- public boolean isFunctionEnabled(String function) {
- return mHandler.isFunctionEnabled(function);
+ public long getCurrentFunctions() {
+ return mHandler.getEnabledFunctions();
+ }
+
+ public long getScreenUnlockedFunctions() {
+ return mHandler.getScreenUnlockedFunctions();
}
/**
* Adds function to the current USB configuration.
*
- * @param functions name of the USB function, or null to restore the default function.
- * @param usbDataUnlocked whether user data is accessible.
+ * @param functions The functions to set, or empty to set the charging function.
*/
- public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
+ public void setCurrentFunctions(long functions) {
if (DEBUG) {
- Slog.d(TAG, "setCurrentFunctions(" + functions + ", "
- + usbDataUnlocked + ")");
- }
- mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
+ Slog.d(TAG, "setCurrentFunctions(" + UsbManager.usbFunctionsToString(functions) + ")");
+ }
+ if (functions == UsbManager.FUNCTION_NONE) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_CHARGING);
+ } else if (functions == UsbManager.FUNCTION_MTP) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP);
+ } else if (functions == UsbManager.FUNCTION_PTP) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_PTP);
+ } else if (functions == UsbManager.FUNCTION_MIDI) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MIDI);
+ } else if (functions == UsbManager.FUNCTION_RNDIS) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_RNDIS);
+ } else if (functions == UsbManager.FUNCTION_ACCESSORY) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_ACCESSORY);
+ }
+ mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
}
/**
@@ -1996,9 +1971,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
*
* @param functions Functions to set.
*/
- public void setScreenUnlockedFunctions(String functions) {
+ public void setScreenUnlockedFunctions(long functions) {
if (DEBUG) {
- Slog.d(TAG, "setScreenUnlockedFunctions(" + functions + ")");
+ Slog.d(TAG, "setScreenUnlockedFunctions("
+ + UsbManager.usbFunctionsToString(functions) + ")");
}
mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 7a352a4dc69d..58f914773071 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -44,7 +44,7 @@ import java.util.LinkedList;
*/
public class UsbHostManager {
private static final String TAG = UsbHostManager.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private final Context mContext;
@@ -230,6 +230,7 @@ public class UsbHostManager {
}
private boolean isBlackListed(String deviceAddress) {
+ Slog.i(TAG, "isBlackListed(" + deviceAddress + ")");
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
if (deviceAddress.startsWith(mHostBlacklist[i])) {
@@ -241,6 +242,7 @@ public class UsbHostManager {
/* returns true if the USB device should not be accessible by applications */
private boolean isBlackListed(int clazz, int subClass) {
+ Slog.i(TAG, "isBlackListed(" + clazz + ", " + subClass + ")");
// blacklist hubs
if (clazz == UsbConstants.USB_CLASS_HUB) return true;
@@ -312,13 +314,7 @@ public class UsbHostManager {
usbDeviceConnectionHandler);
}
- // Headset?
- boolean isInputHeadset = parser.isInputHeadset();
- boolean isOutputHeadset = parser.isOutputHeadset();
- Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
- + " , out: " + isOutputHeadset + "]");
-
- mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
+ mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
@@ -343,10 +339,13 @@ public class UsbHostManager {
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
private void usbDeviceRemoved(String deviceAddress) {
+ if (DEBUG) {
+ Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") - start");
+ }
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
- mUsbAlsaManager.usbDeviceRemoved(device);
+ mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 1a20819b9a80..2f6e53143312 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -382,59 +382,44 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
- public boolean isFunctionEnabled(String function) {
+ public void setCurrentFunctions(long functions) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- return mDeviceManager != null && mDeviceManager.isFunctionEnabled(function);
+ Preconditions.checkArgument(UsbManager.areSettableFunctions(functions));
+ Preconditions.checkState(mDeviceManager != null);
+ mDeviceManager.setCurrentFunctions(functions);
}
@Override
- public void setCurrentFunction(String function, boolean usbDataUnlocked) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-
- if (!isSupportedCurrentFunction(function)) {
- Slog.w(TAG, "Caller of setCurrentFunction() requested unsupported USB function: "
- + function);
- function = UsbManager.USB_FUNCTION_NONE;
- }
+ public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
+ setCurrentFunctions(UsbManager.usbFunctionsFromString(functions));
+ }
- if (mDeviceManager != null) {
- mDeviceManager.setCurrentFunctions(function, usbDataUnlocked);
- } else {
- throw new IllegalStateException("USB device mode not supported");
- }
+ @Override
+ public boolean isFunctionEnabled(String function) {
+ return (getCurrentFunctions() & UsbManager.usbFunctionsFromString(function)) != 0;
}
@Override
- public void setScreenUnlockedFunctions(String function) {
+ public long getCurrentFunctions() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-
- if (!isSupportedCurrentFunction(function)) {
- Slog.w(TAG, "Caller of setScreenUnlockedFunctions() requested unsupported USB function:"
- + function);
- function = UsbManager.USB_FUNCTION_NONE;
- }
-
- if (mDeviceManager != null) {
- mDeviceManager.setScreenUnlockedFunctions(function);
- } else {
- throw new IllegalStateException("USB device mode not supported");
- }
+ Preconditions.checkState(mDeviceManager != null);
+ return mDeviceManager.getCurrentFunctions();
}
- private static boolean isSupportedCurrentFunction(String function) {
- if (function == null) return true;
+ @Override
+ public void setScreenUnlockedFunctions(long functions) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ Preconditions.checkArgument(UsbManager.areSettableFunctions(functions));
+ Preconditions.checkState(mDeviceManager != null);
- switch (function) {
- case UsbManager.USB_FUNCTION_NONE:
- case UsbManager.USB_FUNCTION_AUDIO_SOURCE:
- case UsbManager.USB_FUNCTION_MIDI:
- case UsbManager.USB_FUNCTION_MTP:
- case UsbManager.USB_FUNCTION_PTP:
- case UsbManager.USB_FUNCTION_RNDIS:
- return true;
- }
+ mDeviceManager.setScreenUnlockedFunctions(functions);
+ }
- return false;
+ @Override
+ public long getScreenUnlockedFunctions() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ Preconditions.checkState(mDeviceManager != null);
+ return mDeviceManager.getScreenUnlockedFunctions();
}
@Override
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 7a1e9e2f9896..297a6eab4a78 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@ import java.util.ArrayList;
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private final String mDeviceAddr;
@@ -358,50 +358,93 @@ public final class UsbDescriptorParser {
return list;
}
+ /*
+ * Attribute predicates
+ */
/**
* @hide
*/
- public boolean hasHIDDescriptor() {
- ArrayList<UsbDescriptor> descriptors =
- getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
- return !descriptors.isEmpty();
+ public boolean hasInput() {
+ if (DEBUG) {
+ Log.d(TAG, "---- hasInput()");
+ }
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ boolean hasInput = false;
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACTerminal) {
+ UsbACTerminal inDescr = (UsbACTerminal) descriptor;
+ // Check for input and bi-directional terminal types
+ int type = inDescr.getTerminalType();
+ if (DEBUG) {
+ Log.d(TAG, " type:0x" + Integer.toHexString(type));
+ }
+ if ((type >= UsbTerminalTypes.TERMINAL_IN_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY)
+ || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)
+ || (type == UsbTerminalTypes.TERMINAL_USB_STREAMING)) {
+ hasInput = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "hasInput() = " + hasInput);
+ }
+ return hasInput;
}
/**
* @hide
*/
- public boolean hasMIDIInterface() {
- ArrayList<UsbDescriptor> descriptors =
- getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
- for (UsbDescriptor descriptor : descriptors) {
- // enusure that this isn't an unrecognized interface descriptor
- if (descriptor instanceof UsbInterfaceDescriptor) {
- UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
- if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
- return true;
+ public boolean hasOutput() {
+ if (DEBUG) {
+ Log.d(TAG, "---- hasOutput()");
+ }
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ boolean hasOutput = false;
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACTerminal) {
+ UsbACTerminal outDescr = (UsbACTerminal) descriptor;
+ // Check for output and bi-directional terminal types
+ int type = outDescr.getTerminalType();
+ if (DEBUG) {
+ Log.d(TAG, " type:0x" + Integer.toHexString(type));
+ }
+ if ((type >= UsbTerminalTypes.TERMINAL_OUT_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER)
+ || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)) {
+ hasOutput = true;
+ break;
}
} else {
- Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength()
+ Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
- return false;
+ if (DEBUG) {
+ Log.d(TAG, "hasOutput() = " + hasOutput);
+ }
+ return hasOutput;
}
/**
* @hide
*/
- public float getInputHeadsetProbability() {
- if (hasMIDIInterface()) {
- return 0.0f;
- }
-
- float probability = 0.0f;
- ArrayList<UsbDescriptor> acDescriptors;
-
- // Look for a microphone
+ public boolean hasMic() {
boolean hasMic = false;
- acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
+
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
@@ -418,18 +461,23 @@ public final class UsbDescriptorParser {
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
+ return hasMic;
+ }
- // Look for a "speaker"
+ /**
+ * @hide
+ */
+ public boolean hasSpeaker() {
boolean hasSpeaker = false;
- acDescriptors =
+
+ ArrayList<UsbDescriptor> acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal outDescr = (UsbACTerminal) descriptor;
if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
- || outDescr.getTerminalType()
- == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
+ || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
|| outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
hasSpeaker = true;
break;
@@ -440,6 +488,55 @@ public final class UsbDescriptorParser {
}
}
+ return hasSpeaker;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasHIDDescriptor() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
+ return !descriptors.isEmpty();
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasMIDIInterface() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
+ for (UsbDescriptor descriptor : descriptors) {
+ // enusure that this isn't an unrecognized interface descriptor
+ if (descriptor instanceof UsbInterfaceDescriptor) {
+ UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
+ if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+ return true;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public float getInputHeadsetProbability() {
+ if (hasMIDIInterface()) {
+ return 0.0f;
+ }
+
+ float probability = 0.0f;
+
+ // Look for a microphone
+ boolean hasMic = hasMic();
+
+ // Look for a "speaker"
+ boolean hasSpeaker = hasSpeaker();
+
if (hasMic && hasSpeaker) {
probability += 0.75f;
}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index fcfc5931ac7b..95eb14ada354 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -134,6 +134,25 @@ public final class PhoneAccount implements Parcelable {
"android.telecom.extra.LOG_SELF_MANAGED_CALLS";
/**
+ * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+ * indicates whether calls for a {@link PhoneAccount} should generate a "call recording tone"
+ * when the user is recording audio on the device.
+ * <p>
+ * The call recording tone is played over the telephony audio stream so that the remote party
+ * has an audible indication that it is possible their call is being recorded using a call
+ * recording app on the device.
+ * <p>
+ * This extra only has an effect for calls placed via Telephony (e.g.
+ * {@link #CAPABILITY_SIM_SUBSCRIPTION}).
+ * <p>
+ * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is
+ * in progress.
+ * @hide
+ */
+ public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
+ "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a34e9f9481fa..63ab76677dc7 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -77,6 +77,14 @@ public class CarrierConfigManager {
public static final String
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ /**
+ * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+ * true means visible. false means gone.
+ * @hide
+ */
+ public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
+ "call_barring_visibility_bool";
+
/**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
@@ -146,6 +154,15 @@ public class CarrierConfigManager {
public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
/**
+ * Determines if the carrier requires that a tone be played to the remote party when an app is
+ * recording audio during a call (e.g. using a call recording app).
+ * <p>
+ * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true
+ * in order to work.
+ * @hide
+ */
+ public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
+ /**
* Determines if the carrier requires converting the destination number before sending out an
* SMS. Certain networks and numbering plans require different formats.
*/
@@ -1381,6 +1398,14 @@ public class CarrierConfigManager {
public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
/**
+ * When true, indicates that the HD audio icon in the in-call screen should be shown for
+ * GSM/CDMA calls.
+ * @hide
+ */
+ public static final String KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO =
+ "gsm_cdma_calls_can_be_hd_audio";
+
+ /**
* Whether system apps are allowed to use fallback if carrier video call is not available.
* Defaults to {@code true}.
*
@@ -1793,6 +1818,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
+ sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false);
sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
@@ -1833,6 +1859,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+ sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
@@ -2033,6 +2060,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+ sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true);
sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index dfaaab918012..ece1ee378170 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -36,48 +36,14 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements
private int mEvdoEcio; // This value is the EVDO Ec/Io
private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
int evdoSnr) {
- initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param cdmaDbm
- * @param cdmaEcio
- * @param evdoDbm
- * @param evdoEcio
- * @param evdoSnr
- *
- * @hide
- */
- public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) {
mCdmaDbm = cdmaDbm;
mCdmaEcio = cdmaEcio;
mEvdoDbm = evdoDbm;
@@ -85,9 +51,12 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements
mEvdoSnr = evdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthCdma s) {
mCdmaDbm = s.mCdmaDbm;
mCdmaEcio = s.mCdmaEcio;
@@ -96,9 +65,7 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements
mEvdoSnr = s.mEvdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthCdma copy() {
return new CellSignalStrengthCdma(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index f68d2cad1226..8687cd1c454b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -34,80 +34,40 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P
private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- private int mTimingAdvance;
+ private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm(int ss, int ber) {
- initialize(ss, ber);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
- copyFrom(s);
+ this(ss, ber, Integer.MAX_VALUE);
}
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
+ /** @hide */
+ public CellSignalStrengthGsm(int ss, int ber, int ta) {
mSignalStrength = ss;
mBitErrorRate = ber;
- mTimingAdvance = Integer.MAX_VALUE;
+ mTimingAdvance = ta;
}
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- * @param ta timing advance
- *
- * @hide
- */
- public void initialize(int ss, int ber, int ta) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- mTimingAdvance = ta;
+ /** @hide */
+ public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+ copyFrom(s);
}
- /**
- * @hide
- */
+ /** @hide */
protected void copyFrom(CellSignalStrengthGsm s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthGsm copy() {
return new CellSignalStrengthGsm(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 6ffc8b6e497c..f009fb145fc2 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -37,50 +37,15 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
private int mCqi;
private int mTimingAdvance;
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
int timingAdvance) {
- initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthLte(CellSignalStrengthLte s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param lteSignalStrength
- * @param rsrp
- * @param rsrq
- * @param rssnr
- * @param cqi
- *
- * @hide
- */
- public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi,
- int timingAdvance) {
- mSignalStrength = lteSignalStrength;
+ mSignalStrength = signalStrength;
mRsrp = rsrp;
mRsrq = rsrq;
mRssnr = rssnr;
@@ -88,25 +53,12 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
mTimingAdvance = timingAdvance;
}
- /**
- * Initialize from the SignalStrength structure.
- *
- * @param ss
- *
- * @hide
- */
- public void initialize(SignalStrength ss, int timingAdvance) {
- mSignalStrength = ss.getLteSignalStrength();
- mRsrp = ss.getLteRsrp();
- mRsrq = ss.getLteRsrq();
- mRssnr = ss.getLteRssnr();
- mCqi = ss.getLteCqi();
- mTimingAdvance = timingAdvance;
+ /** @hide */
+ public CellSignalStrengthLte(CellSignalStrengthLte s) {
+ copyFrom(s);
}
- /**
- * @hide
- */
+ /** @hide */
protected void copyFrom(CellSignalStrengthLte s) {
mSignalStrength = s.mSignalStrength;
mRsrp = s.mRsrp;
@@ -116,9 +68,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthLte copy() {
return new CellSignalStrengthLte(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 2cd56b8500a6..dd32a960db91 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -34,62 +34,32 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements
private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
- private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma(int ss, int ber) {
- initialize(ss, ber);
+ mSignalStrength = ss;
+ mBitErrorRate = ber;
}
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
copyFrom(s);
}
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- }
-
- /**
- * @hide
- */
+ /** @hide */
protected void copyFrom(CellSignalStrengthWcdma s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthWcdma copy() {
return new CellSignalStrengthWcdma(this);
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 000000000000..b362df9ff677
--- /dev/null
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.SparseBooleanArray;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+ /**
+ * API to determine if the caller has permissions to get cell location.
+ *
+ * @param pkgName Package name of the application requesting access
+ * @param uid The uid of the package
+ * @param pid The pid of the package
+ * @return boolean true or false if permissions is granted
+ */
+ public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
+ int uid, int pid) throws SecurityException {
+ Trace.beginSection("TelephonyLocationCheck");
+ try {
+ // Always allow the phone process to access location. This avoid breaking legacy code
+ // that rely on public-facing APIs to access cell location, and it doesn't create a
+ // info leak risk because the cell location is stored in the phone process anyway.
+ if (uid == Process.PHONE_UID) {
+ return true;
+ }
+
+ // We always require the location permission and also require the
+ // location mode to be on for non-legacy apps. Legacy apps are
+ // required to be in the foreground to at least mitigate the case
+ // where a legacy app the user is not using tracks their location.
+ // Granting ACCESS_FINE_LOCATION to an app automatically grants it
+ // ACCESS_COARSE_LOCATION.
+
+ if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) ==
+ PackageManager.PERMISSION_DENIED) {
+ return false;
+ }
+ final int opCode = AppOpsManager.permissionToOpCode(
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+ if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
+ && !isLegacyForeground(context, pkgName, uid)) {
+ return false;
+ }
+ // If the user or profile is current, permission is granted.
+ // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+ return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+ int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+ return locationMode != Settings.Secure.LOCATION_MODE_OFF
+ && locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
+ }
+
+ private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName,
+ int uid) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
+ try {
+ if (context.getPackageManager().getApplicationInfo(pkgName, 0)
+ .targetSdkVersion <= Build.VERSION_CODES.O) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume known app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify validity before checking app's version.
+ }
+ return false;
+ }
+
+ private static boolean isForegroundApp(@NonNull Context context, int uid) {
+ final ActivityManager am = context.getSystemService(ActivityManager.class);
+ return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+ }
+
+ private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
+ return context.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int callingUserId = UserHandle.getUserId(uid);
+ if (callingUserId == currentUser) {
+ return true;
+ } else {
+ List<UserInfo> userProfiles = context.getSystemService(
+ UserManager.class).getProfiles(currentUser);
+ for (UserInfo user : userProfiles) {
+ if (user.id == callingUserId) {
+ return true;
+ }
+ }
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 90a3677d1796..1176491907ce 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -22,13 +22,13 @@ import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -1340,6 +1340,39 @@ public class ServiceState implements Parcelable {
}
/** @hide */
+ public static int rilRadioTechnologyToAccessNetworkType(@RilRadioTechnology int rt) {
+ switch(rt) {
+ case RIL_RADIO_TECHNOLOGY_GPRS:
+ case RIL_RADIO_TECHNOLOGY_EDGE:
+ case RIL_RADIO_TECHNOLOGY_GSM:
+ return AccessNetworkType.GERAN;
+ case RIL_RADIO_TECHNOLOGY_UMTS:
+ case RIL_RADIO_TECHNOLOGY_HSDPA:
+ case RIL_RADIO_TECHNOLOGY_HSPAP:
+ case RIL_RADIO_TECHNOLOGY_HSUPA:
+ case RIL_RADIO_TECHNOLOGY_HSPA:
+ case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ return AccessNetworkType.UTRAN;
+ case RIL_RADIO_TECHNOLOGY_IS95A:
+ case RIL_RADIO_TECHNOLOGY_IS95B:
+ case RIL_RADIO_TECHNOLOGY_1xRTT:
+ case RIL_RADIO_TECHNOLOGY_EVDO_0:
+ case RIL_RADIO_TECHNOLOGY_EVDO_A:
+ case RIL_RADIO_TECHNOLOGY_EVDO_B:
+ case RIL_RADIO_TECHNOLOGY_EHRPD:
+ return AccessNetworkType.CDMA2000;
+ case RIL_RADIO_TECHNOLOGY_LTE:
+ case RIL_RADIO_TECHNOLOGY_LTE_CA:
+ return AccessNetworkType.EUTRAN;
+ case RIL_RADIO_TECHNOLOGY_IWLAN:
+ return AccessNetworkType.IWLAN;
+ case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+ default:
+ return AccessNetworkType.UNKNOWN;
+ }
+ }
+
+ /** @hide */
public int getDataNetworkType() {
return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index debf43da79b4..34f2dac028c7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -612,9 +612,9 @@ public class SubscriptionManager {
* onSubscriptionsChanged overridden.
*/
public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
- String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (DBG) {
- logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+ logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
+ " listener=" + listener);
}
try {
@@ -623,7 +623,7 @@ public class SubscriptionManager {
ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
if (tr != null) {
- tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+ tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
}
} catch (RemoteException ex) {
// Should not happen
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cdee9e6f2d73..a3a30807986e 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -220,11 +220,6 @@ public interface RILConstants {
String SETUP_DATA_PROTOCOL_IPV6 = "IPV6";
String SETUP_DATA_PROTOCOL_IPV4V6 = "IPV4V6";
- /* Deactivate data call reasons */
- int DEACTIVATE_REASON_NONE = 0;
- int DEACTIVATE_REASON_RADIO_OFF = 1;
- int DEACTIVATE_REASON_PDP_RESET = 2;
-
/* NV config radio reset types. */
int NV_CONFIG_RELOAD_RESET = 1;
int NV_CONFIG_ERASE_RESET = 2;
diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
index 71451101ac59..021e3861d882 100644
--- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
@@ -14,12 +14,16 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.perftests.amteststestapp">
+ package="com.android.frameworks.perftests.amteststestapp">
<application android:name=".TestApplication">
<activity android:name=".TestActivity" android:exported="true"/>
+ <provider
+ android:authorities="com.android.frameworks.perftests.amteststestapp"
+ android:name=".TestContentProvider"
+ android:exported="true" />
<receiver
- android:name=".TestBroadcastReceiver"
- android:exported="true">
+ android:name=".TestBroadcastReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java
new file mode 100644
index 000000000000..0940578e4b28
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.amteststestapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class TestContentProvider extends ContentProvider {
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
new file mode 100644
index 000000000000..3bf56ce8b085
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.tests;
+
+import android.content.ContentProviderClient;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.perftests.am.util.TargetPackageUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ContentProviderPerfTest extends BasePerfTest {
+ /**
+ * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is
+ * running.
+ */
+ @Test
+ public void contentProviderRunning() {
+ runPerfFunction(() -> {
+ startTargetPackage();
+
+ long startTimeNs = System.nanoTime();
+ final ContentProviderClient contentProviderClient =
+ mContext.getContentResolver()
+ .acquireContentProviderClient(TargetPackageUtils.PACKAGE_NAME);
+ final long endTimeNs = System.nanoTime();
+
+ contentProviderClient.close();
+
+ return endTimeNs - startTimeNs;
+ });
+ }
+
+ /**
+ * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is
+ * not running.
+ */
+ @Test
+ public void contentProviderNotRunning() {
+ runPerfFunction(() -> {
+ final long startTimeNs = System.nanoTime();
+ final ContentProviderClient contentProviderClient =
+ mContext.getContentResolver().acquireContentProviderClient(
+ TargetPackageUtils.PACKAGE_NAME);
+ final long endTimeNs = System.nanoTime();
+
+ contentProviderClient.close();
+
+ return endTimeNs - startTimeNs;
+ });
+ }
+}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 1443fc1ae3ee..063060f166dc 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -83,7 +83,7 @@ public class AppLaunch extends InstrumentationTestCase {
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String WEARABLE_ACTION_GOOGLE =
"com.google.android.wearable.action.GOOGLE";
- private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle
+ private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; //5s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps
private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index ebf5f6854c6f..c8f96c9f0670 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -285,7 +285,8 @@
<activity
android:name="ColoredShadowsActivity"
- android:label="View/ColoredShadows">
+ android:label="View/ColoredShadows"
+ android:theme="@style/ThemeColoredShadows">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml
index 108709bd76b2..fa5437f38ace 100644
--- a/tests/HwAccelerationTest/res/values/styles.xml
+++ b/tests/HwAccelerationTest/res/values/styles.xml
@@ -34,4 +34,11 @@
<item name="android:translationZ">400dp</item>
<item name="android:layout_alignParentBottom">true</item>
</style>
+
+ <style name="ThemeColoredShadows" parent="@android:style/Theme.Material.Light">
+ <!--
+ <item name="android:ambientShadowAlpha">0</item>
+ <item name="android:spotShadowAlpha">1</item>
+ -->
+ </style>
</resources>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
index 135c93c97af2..901d90eed70a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
@@ -17,6 +17,9 @@
package com.android.test.hwui;
import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
@@ -36,7 +39,9 @@ public class ColoredShadowsActivity extends Activity {
private void setShadowColors(ViewGroup row, int rowIndex) {
for (int i = 0; i < row.getChildCount(); i++) {
View view = row.getChildAt(i);
- view.setShadowColor(shadowColorFor(view));
+ //view.setBackground(new MyHackyBackground());
+ view.setOutlineSpotShadowColor(shadowColorFor(view));
+ view.setOutlineAmbientShadowColor(shadowColorFor(view));
view.setElevation(6.0f * (rowIndex + 1));
}
}
@@ -44,12 +49,27 @@ public class ColoredShadowsActivity extends Activity {
private int shadowColorFor(View view) {
switch (view.getId()) {
case R.id.grey: return 0xFF3C4043;
- case R.id.blue: return 0xFF185ABC;
- case R.id.red: return 0xFFB31412;
- case R.id.yellow: return 0xFFEA8600;
- case R.id.green: return 0xFF137333;
+ case R.id.blue: return Color.BLUE;
+ case R.id.red: return 0xFFEA4335;
+ case R.id.yellow: return 0xFFFBBC04;
+ case R.id.green: return 0xFF34A853;
default: return 0xFF000000;
}
}
+ private static class MyHackyBackground extends ColorDrawable {
+ MyHackyBackground() {
+ super(0);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public int getAlpha() {
+ return 254;
+ }
+ }
}
diff --git a/tests/UsbTests/Android.mk b/tests/UsbTests/Android.mk
new file mode 100644
index 000000000000..a04f32a6d714
--- /dev/null
+++ b/tests/UsbTests/Android.mk
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ frameworks-base-testutils \
+ android-support-test \
+ mockito-target-inline-minus-junit4 \
+ platform-test-annotations \
+ services.core \
+ services.net \
+ services.usb \
+ truth-prebuilt \
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libdexmakerjvmtiagent \
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := UsbTests
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsbTests/AndroidManifest.xml b/tests/UsbTests/AndroidManifest.xml
new file mode 100644
index 000000000000..5d606951bb5e
--- /dev/null
+++ b/tests/UsbTests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.usb" >
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.usb"
+ android:label="UsbTests"/>
+</manifest>
diff --git a/tests/UsbTests/AndroidTest.xml b/tests/UsbTests/AndroidTest.xml
new file mode 100644
index 000000000000..0b623fbf2015
--- /dev/null
+++ b/tests/UsbTests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs sample instrumentation test.">
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="UsbTests.apk"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="UsbTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.usb"/>
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration> \ No newline at end of file
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
new file mode 100644
index 000000000000..c491b4658999
--- /dev/null
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.hardware.usb.UsbManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.FgThread;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Tests for UsbHandler state changes.
+ */
+@RunWith(AndroidJUnit4.class)
+public class UsbHandlerTest {
+ private static final String TAG = UsbHandlerTest.class.getSimpleName();
+
+ @Mock
+ private UsbDeviceManager mUsbDeviceManager;
+ @Mock
+ private UsbDebuggingManager mUsbDebuggingManager;
+ @Mock
+ private UsbAlsaManager mUsbAlsaManager;
+ @Mock
+ private UsbSettingsManager mUsbSettingsManager;
+ @Mock
+ private SharedPreferences mSharedPreferences;
+ @Mock
+ private SharedPreferences.Editor mEditor;
+
+ private MockUsbHandler mUsbHandler;
+
+ private static final int MSG_UPDATE_STATE = 0;
+ private static final int MSG_ENABLE_ADB = 1;
+ private static final int MSG_SET_CURRENT_FUNCTIONS = 2;
+ private static final int MSG_SYSTEM_READY = 3;
+ private static final int MSG_BOOT_COMPLETED = 4;
+ private static final int MSG_USER_SWITCHED = 5;
+ private static final int MSG_UPDATE_USER_RESTRICTIONS = 6;
+ private static final int MSG_SET_SCREEN_UNLOCKED_FUNCTIONS = 12;
+ private static final int MSG_UPDATE_SCREEN_LOCK = 13;
+
+ private Map<String, String> mMockProperties;
+ private Map<String, Integer> mMockGlobalSettings;
+
+ private class MockUsbHandler extends UsbDeviceManager.UsbHandler {
+ boolean mIsUsbTransferAllowed;
+ Intent mBroadcastedIntent;
+
+ MockUsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
+ UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
+ UsbSettingsManager settingsManager) {
+ super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
+ mUseUsbNotification = false;
+ mIsUsbTransferAllowed = true;
+ mCurrentUsbFunctionsReceived = true;
+ }
+
+ @Override
+ protected void setEnabledFunctions(long functions, boolean force) {
+ mCurrentFunctions = functions;
+ }
+
+ @Override
+ protected void setSystemProperty(String property, String value) {
+ mMockProperties.put(property, value);
+ }
+
+ @Override
+ protected void putGlobalSettings(ContentResolver resolver, String setting, int val) {
+ mMockGlobalSettings.put(setting, val);
+ }
+
+ @Override
+ protected String getSystemProperty(String property, String def) {
+ if (mMockProperties.containsKey(property)) {
+ return mMockProperties.get(property);
+ }
+ return def;
+ }
+
+ @Override
+ protected boolean isUsbTransferAllowed() {
+ return mIsUsbTransferAllowed;
+ }
+
+ @Override
+ protected SharedPreferences getPinnedSharedPrefs(Context context) {
+ return mSharedPreferences;
+ }
+
+ @Override
+ protected void sendStickyBroadcast(Intent intent) {
+ mBroadcastedIntent = intent;
+ }
+ }
+
+ @Before
+ public void before() {
+ MockitoAnnotations.initMocks(this);
+ mMockProperties = new HashMap<>();
+ mMockGlobalSettings = new HashMap<>();
+ when(mSharedPreferences.edit()).thenReturn(mEditor);
+
+ mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
+ InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
+ mUsbAlsaManager, mUsbSettingsManager);
+ }
+
+ @SmallTest
+ public void setFunctionsMtp() {
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_MTP));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+ }
+
+ @SmallTest
+ public void setFunctionsPtp() {
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_PTP));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_PTP, 0);
+ }
+
+ @SmallTest
+ public void setFunctionsMidi() {
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_MIDI));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MIDI, 0);
+ }
+
+ @SmallTest
+ public void setFunctionsRndis() {
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_RNDIS));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_RNDIS, 0);
+ }
+
+ @SmallTest
+ public void enableAdb() {
+ sendBootCompleteMessages(mUsbHandler);
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 1));
+ assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+ assertTrue(mUsbHandler.mAdbEnabled);
+ assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler
+ .USB_PERSISTENT_CONFIG_PROPERTY), UsbManager.USB_FUNCTION_ADB);
+ verify(mUsbDebuggingManager).setAdbEnabled(true);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1));
+
+ assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false));
+ assertTrue(mUsbHandler.mBroadcastedIntent
+ .getBooleanExtra(UsbManager.USB_CONFIGURED, false));
+ assertTrue(mUsbHandler.mBroadcastedIntent
+ .getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false));
+ }
+
+ @SmallTest
+ public void disableAdb() {
+ mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY,
+ UsbManager.USB_FUNCTION_ADB);
+ mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
+ InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
+ mUsbAlsaManager, mUsbSettingsManager);
+
+ sendBootCompleteMessages(mUsbHandler);
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 0));
+ assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+ assertFalse(mUsbHandler.mAdbEnabled);
+ assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler
+ .USB_PERSISTENT_CONFIG_PROPERTY), "");
+ verify(mUsbDebuggingManager).setAdbEnabled(false);
+ }
+
+ @SmallTest
+ public void bootCompletedCharging() {
+ sendBootCompleteMessages(mUsbHandler);
+ assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+ }
+
+ @Test
+ @SmallTest
+ public void bootCompletedAdbEnabled() {
+ mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, "adb");
+ mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
+ InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
+ mUsbAlsaManager, mUsbSettingsManager);
+
+ sendBootCompleteMessages(mUsbHandler);
+ assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+ assertEquals(mMockGlobalSettings.get(Settings.Global.ADB_ENABLED).intValue(), 1);
+ assertTrue(mUsbHandler.mAdbEnabled);
+ verify(mUsbDebuggingManager).setAdbEnabled(true);
+ }
+
+ @SmallTest
+ public void userSwitchedDisablesMtp() {
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_MTP));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_USER_SWITCHED,
+ UserHandle.getCallingUserId() + 1));
+ assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+ }
+
+ @SmallTest
+ public void changedRestrictionsDisablesMtp() {
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_MTP));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+ mUsbHandler.mIsUsbTransferAllowed = false;
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_USER_RESTRICTIONS));
+ assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+ }
+
+ @SmallTest
+ public void disconnectResetsCharging() {
+ sendBootCompleteMessages(mUsbHandler);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_MTP));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 0, 0));
+
+ assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
+ }
+
+ @SmallTest
+ public void configuredSendsBroadcast() {
+ sendBootCompleteMessages(mUsbHandler);
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_MTP));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1));
+
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+ assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false));
+ assertTrue(mUsbHandler.mBroadcastedIntent
+ .getBooleanExtra(UsbManager.USB_CONFIGURED, false));
+ assertTrue(mUsbHandler.mBroadcastedIntent
+ .getBooleanExtra(UsbManager.USB_FUNCTION_MTP, false));
+ }
+
+ @SmallTest
+ public void setScreenUnlockedFunctions() {
+ sendBootCompleteMessages(mUsbHandler);
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0));
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS,
+ UsbManager.FUNCTION_MTP));
+ assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0);
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+ verify(mEditor).putString(String.format(Locale.ENGLISH,
+ UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser),
+ UsbManager.USB_FUNCTION_MTP);
+ }
+
+ @SmallTest
+ public void unlockScreen() {
+ when(mSharedPreferences.getString(String.format(Locale.ENGLISH,
+ UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser), ""))
+ .thenReturn(UsbManager.USB_FUNCTION_MTP);
+ sendBootCompleteMessages(mUsbHandler);
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0));
+
+ assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0);
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0);
+ }
+
+ private static void sendBootCompleteMessages(Handler handler) {
+ handler.handleMessage(handler.obtainMessage(MSG_BOOT_COMPLETED));
+ handler.handleMessage(handler.obtainMessage(MSG_SYSTEM_READY));
+ }
+} \ No newline at end of file
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 099cfd457160..e692652c7ea4 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -311,7 +311,7 @@ public class TetheringTest {
// Emulate pressing the USB tethering button in Settings UI.
mTethering.startTethering(TETHERING_USB, null, false);
mLooper.dispatchAll();
- verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
+ verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
// Pretend we receive a USB connected broadcast. Here we also pretend
// that the RNDIS function is somehow enabled, so that we see if we
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index dc5ba0cc83b5..fe63aa1bf79b 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -7,22 +7,21 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.util.List;
+
/** @hide */
@SystemApi
@SystemService(Context.WIFI_RTT_SERVICE)
@@ -175,7 +174,8 @@ public class RttManager {
@Deprecated
@SuppressLint("Doclava125")
public Capabilities getCapabilities() {
- return new Capabilities();
+ throw new UnsupportedOperationException(
+ "getCapabilities is not supported in the adaptation layer");
}
/**
@@ -316,16 +316,7 @@ public class RttManager {
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public RttCapabilities getRttCapabilities() {
- synchronized (mCapabilitiesLock) {
- if (mRttCapabilities == null) {
- try {
- mRttCapabilities = mService.getRttCapabilities();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return mRttCapabilities;
- }
+ return mRttCapabilities;
}
/** specifies parameters for RTT request */
@@ -972,69 +963,6 @@ public class RttManager {
}
}
- private boolean rttParamSanity(RttParams params, int index) {
- if (mRttCapabilities == null) {
- if(getRttCapabilities() == null) {
- Log.e(TAG, "Can not get RTT capabilities");
- throw new IllegalStateException("RTT chip is not working");
- }
- }
-
- if (params.deviceType != RTT_PEER_TYPE_AP) {
- return false;
- } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
- RTT_TYPE_TWO_SIDED) {
- Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
- return false;
- } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
- !mRttCapabilities.oneSidedRttSupported) {
- Log.e(TAG, "Request " + index + ": One side RTT is not supported");
- return false;
- } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
- !mRttCapabilities.twoSided11McRttSupported) {
- Log.e(TAG, "Request " + index + ": two side RTT is not supported");
- return false;
- } else if(params.bssid == null || params.bssid.isEmpty()) {
- Log.e(TAG,"No BSSID in params");
- return false;
- } else if ( params.numberBurst != 0 ) {
- Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
- return false;
- } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
- Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
- params.numSamplesPerBurst);
- return false;
- } else if (params.numRetriesPerMeasurementFrame < 0 ||
- params.numRetriesPerMeasurementFrame > 3) {
- Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
- params.numRetriesPerMeasurementFrame);
- return false;
- } else if(params.numRetriesPerFTMR < 0 ||
- params.numRetriesPerFTMR > 3) {
- Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
- params.numRetriesPerFTMR);
- return false;
- } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
- Log.e(TAG, "Request " + index + ": LCI is not supported");
- return false;
- } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
- Log.e(TAG, "Request " + index + ": LCR is not supported");
- return false;
- } else if (params.burstTimeout < 1 ||
- (params.burstTimeout > 11 && params.burstTimeout != 15)){
- Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
- return false;
- } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
- return false;
- } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
- return false;
- }
-
- return true;
- }
-
/**
* Request to start an RTT ranging
*
@@ -1045,24 +973,72 @@ public class RttManager {
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startRanging(RttParams[] params, RttListener listener) {
- int index = 0;
- for(RttParams rttParam : params) {
- if (!rttParamSanity(rttParam, index)) {
- throw new IllegalArgumentException("RTT Request Parameter Illegal");
+ Log.i(TAG, "Send RTT request to RTT Service");
+
+ if (!mNewService.isAvailable()) {
+ listener.onFailure(REASON_NOT_AVAILABLE, "");
+ return;
+ }
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ for (RttParams rttParams : params) {
+ if (rttParams.deviceType != RTT_PEER_TYPE_AP) {
+ listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported");
+ return;
+ }
+
+ ScanResult reconstructed = new ScanResult();
+ reconstructed.BSSID = rttParams.bssid;
+ if (rttParams.requestType == RTT_TYPE_TWO_SIDED) {
+ reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
}
- index++;
+ reconstructed.channelWidth = rttParams.channelWidth;
+ reconstructed.frequency = rttParams.frequency;
+ reconstructed.centerFreq0 = rttParams.centerFreq0;
+ reconstructed.centerFreq1 = rttParams.centerFreq1;
+ builder.addResponder(
+ android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed));
+ }
+ try {
+ mNewService.startRanging(builder.build(), new RangingResultCallback() {
+ @Override
+ public void onRangingFailure(int code) {
+ int localCode = REASON_UNSPECIFIED;
+ if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) {
+ localCode = REASON_NOT_AVAILABLE;
+ }
+ listener.onFailure(localCode, "");
+ }
+
+ @Override
+ public void onRangingResults(List<RangingResult> results) {
+ RttResult[] legacyResults = new RttResult[results.size()];
+ int i = 0;
+ for (RangingResult result : results) {
+ legacyResults[i] = new RttResult();
+ legacyResults[i].status = result.getStatus();
+ legacyResults[i].bssid = result.getMacAddress().toString();
+ legacyResults[i].distance = result.getDistanceMm() / 10;
+ legacyResults[i].distanceStandardDeviation =
+ result.getDistanceStdDevMm() / 10;
+ legacyResults[i].rssi = result.getRssi();
+ legacyResults[i].ts = result.getRangingTimestampUs();
+ }
+ listener.onSuccess(legacyResults);
+ }
+ }, null);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "startRanging: invalid arguments - " + e);
+ listener.onFailure(REASON_INVALID_REQUEST, e.getMessage());
+ } catch (SecurityException e) {
+ Log.e(TAG, "startRanging: security exception - " + e);
+ listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage());
}
- validateChannel();
- ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
- Log.i(TAG, "Send RTT request to RTT Service");
- mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
- 0, putListener(listener), parcelableParams);
}
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void stopRanging(RttListener listener) {
- validateChannel();
- mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+ Log.e(TAG, "stopRanging: unsupported operation - nop");
}
/**
@@ -1095,12 +1071,8 @@ public class RttManager {
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void enableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = putListenerIfAbsent(callback);
- mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "enableResponder is not supported in the adaptation layer");
}
/**
@@ -1115,16 +1087,8 @@ public class RttManager {
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void disableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = removeListener(callback);
- if (key == INVALID_KEY) {
- Log.e(TAG, "responder not enabled yet");
- return;
- }
- mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "disableResponder is not supported in the adaptation layer");
}
/**
@@ -1238,17 +1202,9 @@ public class RttManager {
/** @hide */
public static final int CMD_OP_REG_BINDER = BASE + 9;
- private static final int INVALID_KEY = 0;
-
private final Context mContext;
- private final IRttManager mService;
- private final SparseArray mListenerMap = new SparseArray();
- private final Object mListenerMapLock = new Object();
- private final Object mCapabilitiesLock = new Object();
-
+ private final WifiRttManager mNewService;
private RttCapabilities mRttCapabilities;
- private int mListenerKey = 1;
- private AsyncChannel mAsyncChannel;
/**
* Create a new WifiScanner instance.
@@ -1263,170 +1219,20 @@ public class RttManager {
*/
public RttManager(Context context, IRttManager service, Looper looper) {
mContext = context;
- mService = service;
- Messenger messenger = null;
- int[] key = new int[1];
- try {
- Log.d(TAG, "Get the messenger from " + mService);
- messenger = mService.getMessenger(new Binder(), key);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- if (messenger == null) {
- throw new IllegalStateException("getMessenger() returned null! This is invalid.");
- }
-
- mAsyncChannel = new AsyncChannel();
-
- Handler handler = new ServiceHandler(looper);
- mAsyncChannel.connectSync(mContext, handler, messenger);
- // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
- // synchronously, which causes RttService to receive the wrong replyTo value.
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
- new RttClient(context.getPackageName()));
- mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]);
+ mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
+
+ boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT);
+
+ mRttCapabilities = new RttCapabilities();
+ mRttCapabilities.oneSidedRttSupported = rttSupported;
+ mRttCapabilities.twoSided11McRttSupported = rttSupported;
+ mRttCapabilities.lciSupported = false;
+ mRttCapabilities.lcrSupported = false;
+ mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT;
+ mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT;
+ mRttCapabilities.responderSupported = false;
+ mRttCapabilities.secureRttSupported = false;
}
-
- private void validateChannel() {
- if (mAsyncChannel == null) throw new IllegalStateException(
- "No permission to access and change wifi or a bad initialization");
- }
-
- private int putListener(Object listener) {
- if (listener == null) return INVALID_KEY;
- int key;
- synchronized (mListenerMapLock) {
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- }
- return key;
- }
-
- // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
- private int putListenerIfAbsent(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int key = getListenerKey(listener);
- if (key != INVALID_KEY) {
- return key;
- }
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- return key;
- }
-
- }
-
- private Object getListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- return listener;
- }
- }
-
- private int getListenerKey(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int index = mListenerMap.indexOfValue(listener);
- if (index == -1) {
- return INVALID_KEY;
- } else {
- return mListenerMap.keyAt(index);
- }
- }
- }
-
- private Object removeListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- mListenerMap.remove(key);
- return listener;
- }
- }
-
- private int removeListener(Object listener) {
- int key = getListenerKey(listener);
- if (key == INVALID_KEY) return key;
- synchronized (mListenerMapLock) {
- mListenerMap.remove(key);
- return key;
- }
- }
-
- private class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- Log.i(TAG, "RTT manager get message: " + msg.what);
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection lost");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- mAsyncChannel = null;
- getLooper().quit();
- return;
- }
-
- Object listener = getListener(msg.arg2);
- if (listener == null) {
- Log.e(TAG, "invalid listener key = " + msg.arg2 );
- return;
- } else {
- Log.i(TAG, "listener key = " + msg.arg2);
- }
-
- switch (msg.what) {
- /* ActionListeners grouped together */
- case CMD_OP_SUCCEEDED :
- reportSuccess(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_FAILED :
- reportFailure(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_ABORTED :
- ((RttListener) listener).onAborted();
- removeListener(msg.arg2);
- break;
- case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
- ResponderConfig config = (ResponderConfig) msg.obj;
- ((ResponderCallback) (listener)).onResponderEnabled(config);
- break;
- case CMD_OP_ENALBE_RESPONDER_FAILED:
- ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
- removeListener(msg.arg2);
- break;
- default:
- if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
- return;
- }
- }
-
- void reportSuccess(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
- ((RttListener) listener).onSuccess(parcelableResults.mResults);
- }
-
- void reportFailure(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- Bundle bundle = (Bundle) msg.obj;
- ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
- }
- }
-
}