summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp6
-rw-r--r--Android.mk19
-rw-r--r--apct-tests/perftests/core/AndroidManifest.xml6
-rw-r--r--apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java114
-rw-r--r--apct-tests/perftests/core/src/android/os/PermissionTest.java66
-rw-r--r--apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java156
-rw-r--r--api/current.txt129
-rw-r--r--api/system-current.txt85
-rw-r--r--cmds/incident_helper/src/TextParserBase.h2
-rw-r--r--cmds/incidentd/Android.mk1
-rw-r--r--cmds/incidentd/README.md4
-rw-r--r--cmds/incidentd/incidentd.rc2
-rw-r--r--cmds/incidentd/src/FdBuffer.cpp4
-rw-r--r--cmds/incidentd/src/IncidentService.cpp36
-rw-r--r--cmds/incidentd/src/Reporter.cpp2
-rw-r--r--cmds/incidentd/src/Section.cpp52
-rw-r--r--cmds/incidentd/src/io_util.cpp2
-rw-r--r--cmds/incidentd/src/report_directory.cpp26
-rw-r--r--cmds/incidentd/tests/Reporter_test.cpp16
-rw-r--r--cmds/incidentd/tests/Section_test.cpp18
-rw-r--r--cmds/statsd/Android.mk3
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp4
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp12
-rw-r--r--cmds/statsd/src/atoms.proto99
-rw-r--r--cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp2
-rw-r--r--cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp89
-rw-r--r--cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h34
-rw-r--r--cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp88
-rw-r--r--cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h42
-rw-r--r--cmds/statsd/src/external/Perfetto.cpp111
-rw-r--r--cmds/statsd/src/external/Perfetto.h35
-rw-r--r--cmds/statsd/src/external/StatsPullerManagerImpl.cpp2
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp8
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp9
-rw-r--r--cmds/statsd/src/perfetto/perfetto_config.proto61
-rw-r--r--cmds/statsd/src/statsd_config.proto4
-rw-r--r--cmds/statsd/tools/dogfood/Android.mk7
-rw-r--r--cmds/statsd/tools/loadtest/Android.mk1
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java5
-rw-r--r--core/java/android/app/ActivityManagerInternal.java5
-rw-r--r--core/java/android/app/AppOpsManager.java19
-rw-r--r--core/java/android/app/Instrumentation.java4
-rw-r--r--core/java/android/app/SystemServiceRegistry.java13
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java26
-rw-r--r--core/java/android/app/slice/ISliceManager.aidl2
-rw-r--r--core/java/android/app/slice/Slice.java7
-rw-r--r--core/java/android/app/slice/SliceManager.java65
-rw-r--r--core/java/android/app/slice/SliceProvider.java153
-rw-r--r--core/java/android/app/usage/NetworkStats.java52
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java35
-rw-r--r--core/java/android/app/usage/UsageStatsManagerInternal.java6
-rw-r--r--core/java/android/content/Context.java14
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java6
-rw-r--r--core/java/android/hardware/HardwareBuffer.java56
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java174
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java42
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java7
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java115
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java207
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java70
-rw-r--r--core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java7
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java108
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java66
-rw-r--r--core/java/android/hardware/radio/ProgramSelector.java125
-rw-r--r--core/java/android/net/IIpSecService.aidl4
-rw-r--r--core/java/android/net/IpSecManager.java128
-rw-r--r--core/java/android/net/IpSecTransform.java21
-rw-r--r--core/java/android/net/NetworkIdentity.java1
-rw-r--r--core/java/android/net/NetworkStats.java7
-rw-r--r--core/java/android/net/NetworkTemplate.java69
-rw-r--r--core/java/android/os/BatteryStats.java125
-rw-r--r--core/java/android/os/Debug.java22
-rw-r--r--core/java/android/os/IPermissionController.aidl1
-rw-r--r--core/java/android/os/ISystemUpdateManager.aidl27
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/RecoverySystem.java30
-rw-r--r--core/java/android/os/SystemUpdateManager.java152
-rw-r--r--core/java/android/os/connectivity/GpsBatteryStats.aidl (renamed from packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java)14
-rw-r--r--core/java/android/os/connectivity/GpsBatteryStats.java108
-rw-r--r--core/java/android/os/connectivity/WifiBatteryStats.aidl20
-rw-r--r--core/java/android/os/connectivity/WifiBatteryStats.java279
-rw-r--r--core/java/android/privacy/internal/rappor/RapporEncoder.java5
-rw-r--r--core/java/android/provider/AlarmClock.java7
-rw-r--r--core/java/android/provider/CallLog.java9
-rw-r--r--core/java/android/provider/Settings.java529
-rw-r--r--core/java/android/provider/SettingsValidators.java82
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java1
-rw-r--r--core/java/android/service/autofill/AutofillFieldClassificationService.java130
-rw-r--r--core/java/android/service/autofill/FieldClassification.java27
-rw-r--r--core/java/android/service/autofill/IAutofillFieldClassificationService.aidl4
-rw-r--r--core/java/android/service/autofill/UserData.java9
-rw-r--r--core/java/android/text/style/AbsoluteSizeSpan.java58
-rw-r--r--core/java/android/text/style/BackgroundColorSpan.java52
-rw-r--r--core/java/android/text/style/ForegroundColorSpan.java47
-rw-r--r--core/java/android/text/style/RelativeSizeSpan.java45
-rw-r--r--core/java/android/text/style/ScaleXSpan.java46
-rw-r--r--core/java/android/text/style/StrikethroughSpan.java37
-rw-r--r--core/java/android/text/style/SubscriptSpan.java44
-rw-r--r--core/java/android/text/style/SuperscriptSpan.java45
-rw-r--r--core/java/android/text/style/UnderlineSpan.java37
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java57
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java26
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java49
-rw-r--r--core/java/android/util/apk/ApkSigningBlockUtils.java20
-rw-r--r--core/java/android/util/apk/ApkVerityBuilder.java6
-rw-r--r--core/java/android/view/Surface.java1
-rw-r--r--core/java/android/view/autofill/AutofillManager.java77
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl4
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl9
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHelper.java8
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java380
-rw-r--r--core/java/com/android/internal/os/CpuPowerCalculator.java24
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java146
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java178
-rw-r--r--core/java/com/android/internal/os/PowerProfile.java207
-rw-r--r--core/java/com/android/internal/os/WakelockPowerCalculator.java2
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl2
-rw-r--r--core/java/com/android/internal/util/ConcurrentUtils.java25
-rw-r--r--core/java/com/android/internal/util/DumpUtils.java1
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java13
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h4
-rw-r--r--core/jni/android/opengl/util.cpp26
-rw-r--r--core/jni/android_database_SQLiteCommon.cpp103
-rw-r--r--core/jni/android_media_AudioFormat.h35
-rw-r--r--core/jni/android_media_AudioSystem.cpp19
-rw-r--r--core/jni/android_media_AudioTrack.cpp72
-rw-r--r--core/proto/android/os/incident.proto2
-rw-r--r--core/proto/android/server/forceappstandbytracker.proto9
-rw-r--r--core/proto/android/service/netstats.proto2
-rw-r--r--core/res/AndroidManifest.xml34
-rw-r--r--core/res/res/drawable/ic_screenshot.xml6
-rw-r--r--core/res/res/values/config.xml18
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml11
-rw-r--r--core/res/res/xml/power_profile.xml22
-rw-r--r--core/res/res/xml/power_profile_test.xml116
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java5
-rw-r--r--core/tests/coretests/src/android/provider/SettingsValidatorsTest.java28
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java186
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java240
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java245
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java10
-rw-r--r--core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java58
-rw-r--r--core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java8
-rw-r--r--core/tests/privacytests/src/android/privacy/RapporEncoderTest.java2
-rw-r--r--data/etc/platform.xml4
-rw-r--r--docs/html/reference/images/text/style/absolutesizespan.pngbin0 -> 12985 bytes
-rw-r--r--docs/html/reference/images/text/style/backgroundcolorspan.pngbin0 -> 16410 bytes
-rw-r--r--docs/html/reference/images/text/style/foregroundcolorspan.pngbin0 -> 18636 bytes
-rw-r--r--docs/html/reference/images/text/style/relativesizespan.pngbin0 -> 10288 bytes
-rw-r--r--docs/html/reference/images/text/style/scalexspan.pngbin0 -> 9236 bytes
-rw-r--r--docs/html/reference/images/text/style/strikethroughspan.pngbin0 -> 8458 bytes
-rw-r--r--docs/html/reference/images/text/style/subscriptspan.pngbin0 -> 10906 bytes
-rw-r--r--docs/html/reference/images/text/style/superscriptspan.pngbin0 -> 4163 bytes
-rw-r--r--docs/html/reference/images/text/style/underlinespan.pngbin0 -> 6473 bytes
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java140
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedImageDrawable.java21
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java85
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java43
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp10
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp34
-rw-r--r--libs/incident/Android.mk1
-rw-r--r--libs/incident/include/android/os/IncidentReportArgs.h4
-rw-r--r--libs/incident/proto/android/os/header.proto (renamed from core/proto/android/os/incidentheader.proto)31
-rw-r--r--libs/incident/src/IncidentReportArgs.cpp8
-rw-r--r--location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java86
-rw-r--r--media/java/android/media/AudioFormat.java73
-rw-r--r--media/java/android/media/AudioManager.java40
-rw-r--r--media/java/android/media/AudioSystem.java12
-rw-r--r--media/java/android/media/AudioTrack.java156
-rw-r--r--media/java/android/media/IAudioService.aidl3
-rw-r--r--media/java/android/media/MediaRecorder.java33
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java2
-rw-r--r--packages/ExtServices/AndroidManifest.xml6
-rw-r--r--packages/ExtServices/res/values/strings.xml5
-rw-r--r--packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java22
-rw-r--r--packages/SettingsLib/res/values/strings.xml6
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java32
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java1
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java41
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java83
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java10
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java110
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java84
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java159
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java259
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java96
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java132
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java181
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java122
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java28
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/AndroidManifest.xml13
-rw-r--r--packages/SystemUI/res/color/qs_background_dark.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_screenshot_edit.xml27
-rw-r--r--packages/SystemUI/res/drawable/qs_bg_gradient.xml24
-rw-r--r--packages/SystemUI/res/layout/battery_percentage_view.xml2
-rw-r--r--packages/SystemUI/res/layout/output_chooser.xml14
-rw-r--r--packages/SystemUI/res/layout/output_chooser_item.xml1
-rw-r--r--packages/SystemUI/res/layout/qs_customize_panel.xml1
-rw-r--r--packages/SystemUI/res/layout/qs_detail.xml3
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml12
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml39
-rw-r--r--packages/SystemUI/res/layout/quick_qs_status_icons.xml25
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml10
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml12
-rw-r--r--packages/SystemUI/res/layout/rotate_suggestion.xml24
-rw-r--r--packages/SystemUI/res/layout/slice_permission_request.xml50
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml51
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml2
-rw-r--r--packages/SystemUI/res/layout/system_icons.xml7
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/values/ids.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml20
-rw-r--r--packages/SystemUI/res/values/styles.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java133
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java169
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java4
-rw-r--r--packages/SystemUI/tests/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java196
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java158
-rw-r--r--proto/src/metrics_constants.proto5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java27
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java17
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java98
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java9
-rw-r--r--services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java59
-rw-r--r--services/backup/java/com/android/server/backup/TransportManager.java5
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformBackupTask.java60
-rw-r--r--services/backup/java/com/android/server/backup/transport/TransportClient.java55
-rw-r--r--services/backup/java/com/android/server/backup/transport/TransportClientManager.java32
-rw-r--r--services/backup/java/com/android/server/backup/transport/TransportUtils.java23
-rw-r--r--services/core/java/com/android/server/AppOpsService.java65
-rw-r--r--services/core/java/com/android/server/ForceAppStandbyTracker.java179
-rw-r--r--services/core/java/com/android/server/IpSecService.java98
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java8
-rw-r--r--services/core/java/com/android/server/SystemUpdateManagerService.java255
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java55
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java9
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java2
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java8
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java28
-rw-r--r--services/core/java/com/android/server/am/UserController.java12
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java76
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java2
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java3
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java25
-rw-r--r--services/core/java/com/android/server/net/watchlist/WatchlistConfig.java20
-rw-r--r--services/core/java/com/android/server/oemlock/OemLockService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java45
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java30
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java44
-rw-r--r--services/core/java/com/android/server/slice/PinnedSliceState.java92
-rw-r--r--services/core/java/com/android/server/slice/SliceManagerService.java139
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java15
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java12
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java83
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java22
-rw-r--r--services/core/jni/Android.bp24
-rw-r--r--services/core/jni/com_android_server_ArcVideoService.cpp149
-rw-r--r--services/core/jni/onload.cpp7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java32
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java57
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java165
-rw-r--r--services/tests/uiservicestests/AndroidManifest.xml1
-rw-r--r--services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java43
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java24
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java5
-rw-r--r--telecomm/java/android/telecom/Call.java1
-rw-r--r--telecomm/java/android/telecom/Connection.java15
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java7
-rwxr-xr-xtelecomm/java/android/telecom/TransformationInfo.java127
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java8
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java42
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java8
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java26
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java247
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java14
-rw-r--r--tests/net/Android.mk24
-rw-r--r--tests/net/java/com/android/server/IpSecServiceParameterizedTest.java18
-rw-r--r--tests/net/java/com/android/server/IpSecServiceTest.java10
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java3
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java5
-rw-r--r--wifi/java/android/net/wifi/WifiActivityEnergyInfo.java25
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java9
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java65
-rw-r--r--wifi/java/android/net/wifi/rtt/LocationCivic.java118
-rw-r--r--wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java272
-rw-r--r--wifi/java/android/net/wifi/rtt/RangingResult.java77
-rw-r--r--wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java101
342 files changed, 11716 insertions, 3272 deletions
diff --git a/Android.bp b/Android.bp
index 19c0580e21d2..d1332bb4fe87 100644
--- a/Android.bp
+++ b/Android.bp
@@ -230,6 +230,7 @@ java_library {
"core/java/android/os/ISchedulingPolicyService.aidl",
"core/java/android/os/IStatsCompanionService.aidl",
"core/java/android/os/IStatsManager.aidl",
+ "core/java/android/os/ISystemUpdateManager.aidl",
"core/java/android/os/IThermalEventListener.aidl",
"core/java/android/os/IThermalService.aidl",
"core/java/android/os/IUpdateLock.aidl",
@@ -689,7 +690,10 @@ gensrcs {
" $(in) " +
"&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
- srcs: ["core/proto/**/*.proto"],
+ srcs: [
+ "core/proto/**/*.proto",
+ "libs/incident/**/*.proto",
+ ],
output_extension: "srcjar",
}
diff --git a/Android.mk b/Android.mk
index 298b85ddf084..2254008422e0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -331,6 +331,8 @@ LOCAL_DROIDDOC_OPTIONS:=\
$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-referenceonly \
-api $(INTERNAL_PLATFORM_API_FILE) \
+ -privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+ -privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
-removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \
-nodocs
@@ -340,7 +342,9 @@ LOCAL_UNINSTALLABLE_MODULE := true
include $(BUILD_DROIDDOC)
-$(INTERNAL_PLATFORM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \
+ $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+ $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
# ==== the system api stubs ===================================
@@ -365,6 +369,8 @@ LOCAL_DROIDDOC_OPTIONS:=\
-referenceonly \
-showAnnotation android.annotation.SystemApi \
-api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+ -privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+ -privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \
-removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \
-exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \
-nodocs
@@ -375,7 +381,9 @@ LOCAL_UNINSTALLABLE_MODULE := true
include $(BUILD_DROIDDOC)
-$(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+ $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+ $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
# ==== the test api stubs ===================================
@@ -781,6 +789,7 @@ LOCAL_PROTOC_FLAGS := \
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
cmds/am/proto/instrumentation_data.proto \
+ cmds/statsd/src/perfetto/perfetto_config.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto) \
$(call all-proto-files-under, cmds/statsd/src)
@@ -797,7 +806,8 @@ LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \
store_unknown_fields = true
LOCAL_JAVA_LIBRARIES := core-oj core-libart
LOCAL_SRC_FILES := \
- $(call all-proto-files-under, core/proto)
+ $(call all-proto-files-under, core/proto) \
+ $(call all-proto-files-under, libs/incident/proto/android/os)
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -809,7 +819,8 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_PROTOC_FLAGS := \
-Iexternal/protobuf/src
LOCAL_SRC_FILES := \
- $(call all-proto-files-under, core/proto)
+ $(call all-proto-files-under, core/proto) \
+ $(call all-proto-files-under, libs/incident/proto/android/os)
include $(BUILD_STATIC_JAVA_LIBRARY)
# Include subdirectory makefiles
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index bd0b944e1233..132a2f9b2d0c 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -10,7 +10,11 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.StubActivity" />
+ <activity android:name="android.perftests.utils.StubActivity">
+ <intent-filter>
+ <action android:name="com.android.perftests.core.PERFTEST" />
+ </intent-filter>
+ </activity>
<service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
</application>
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
new file mode 100644
index 000000000000..145fbcd2d5d7
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PackageManagerPerfTest {
+ private static final String PERMISSION_NAME_EXISTS =
+ "com.android.perftests.core.TestPermission";
+ private static final String PERMISSION_NAME_DOESNT_EXIST =
+ "com.android.perftests.core.TestBadPermission";
+ private static final ComponentName TEST_ACTIVITY =
+ new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity");
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testCheckPermissionExists() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
+ }
+ }
+
+ @Test
+ public void testCheckPermissionDoesntExist() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
+ }
+ }
+
+ @Test
+ public void testQueryIntentActivities() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
+
+ while (state.keepRunning()) {
+ pm.queryIntentActivities(intent, 0);
+ }
+ }
+
+ @Test
+ public void testGetPackageInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ pm.getPackageInfo(packageName, 0);
+ }
+ }
+
+ @Test
+ public void testGetApplicationInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+ final String packageName = TEST_ACTIVITY.getPackageName();
+
+ while (state.keepRunning()) {
+ pm.getApplicationInfo(packageName, 0);
+ }
+ }
+
+ @Test
+ public void testGetActivityInfo() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+
+ while (state.keepRunning()) {
+ pm.getActivityInfo(TEST_ACTIVITY, 0);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/PermissionTest.java b/apct-tests/perftests/core/src/android/os/PermissionTest.java
deleted file mode 100644
index d292e7dc3b96..000000000000
--- a/apct-tests/perftests/core/src/android/os/PermissionTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package android.os;
-
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.content.Context;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class PermissionTest {
- private static final String PERMISSION_HAS_NAME = "com.android.perftests.core.TestPermission";
- private static final String PERMISSION_DOESNT_HAVE_NAME =
- "com.android.perftests.core.TestBadPermission";
- private static final String THIS_PACKAGE_NAME = "com.android.perftests.core";
-
- @Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- @Test
- public void testHasPermission() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Context context = InstrumentationRegistry.getTargetContext();
- while (state.keepRunning()) {
- int ret = context.getPackageManager().checkPermission(PERMISSION_HAS_NAME,
- THIS_PACKAGE_NAME);
- }
- }
-
- @Test
- public void testDoesntHavePermission() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Context context = InstrumentationRegistry.getTargetContext();
-
- while (state.keepRunning()) {
- int ret = context.getPackageManager().checkPermission(PERMISSION_DOESNT_HAVE_NAME,
- THIS_PACKAGE_NAME);
- }
- }
-
-}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 6975609d990a..682885b3120d 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -25,9 +25,12 @@ import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.content.res.ColorStateList;
+import android.graphics.Canvas;
import android.graphics.Typeface;
import android.text.Layout;
import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
import org.junit.Before;
import org.junit.Rule;
@@ -285,4 +288,157 @@ public class StaticLayoutPerfTest {
.build();
}
}
+
+ @Test
+ public void testDraw_FixedText_NoStyled() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_RandomText_Styled() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_RandomText_NoStyled() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_RandomText_Styled_WithoutCache() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_RandomText_NoStyled_WithoutCache() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_MeasuredText_Styled() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_MeasuredText_NoStyled() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_MeasuredText_Styled_WithoutCache() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
+ @Test
+ public void testDraw_MeasuredText_NoStyled_WithoutCache() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+ final StaticLayout layout =
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+ final DisplayListCanvas c = node.start(1200, 200);
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ layout.draw(c);
+ }
+ }
+
}
diff --git a/api/current.txt b/api/current.txt
index 86f1a3b52583..8d26717af988 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2796,6 +2796,7 @@ package android.accessibilityservice {
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+ field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9
field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
@@ -6473,6 +6474,7 @@ package android.app.admin {
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public boolean isPrintingEnabled();
method public boolean isProfileOwnerApp(java.lang.String);
method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isResetPasswordTokenActive(android.content.ComponentName);
@@ -6542,6 +6544,7 @@ package android.app.admin {
method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
+ method public void setPrintingEnabled(android.content.ComponentName, boolean);
method public void setProfileEnabled(android.content.ComponentName);
method public void setProfileName(android.content.ComponentName, java.lang.String);
method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -6672,6 +6675,7 @@ package android.app.admin {
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
+ field public static final java.lang.String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -7252,6 +7256,7 @@ package android.app.usage {
public static class NetworkStats.Bucket {
ctor public NetworkStats.Bucket();
+ method public int getDefaultNetwork();
method public long getEndTimeStamp();
method public int getMetered();
method public int getRoaming();
@@ -7263,6 +7268,9 @@ package android.app.usage {
method public long getTxBytes();
method public long getTxPackets();
method public int getUid();
+ field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
+ field public static final int DEFAULT_NETWORK_NO = 1; // 0x1
+ field public static final int DEFAULT_NETWORK_YES = 2; // 0x2
field public static final int METERED_ALL = -1; // 0xffffffff
field public static final int METERED_NO = 1; // 0x1
field public static final int METERED_YES = 2; // 0x2
@@ -15233,18 +15241,26 @@ package android.hardware {
method public void writeToParcel(android.os.Parcel, int);
field public static final int BLOB = 33; // 0x21
field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR;
+ field public static final int DS_24UI8 = 50; // 0x32
+ field public static final int DS_FP32UI8 = 52; // 0x34
+ field public static final int D_16 = 48; // 0x30
+ field public static final int D_24 = 49; // 0x31
+ field public static final int D_FP32 = 51; // 0x33
field public static final int RGBA_1010102 = 43; // 0x2b
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_FP16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
+ field public static final int S_UI8 = 53; // 0x35
field public static final long USAGE_CPU_READ_OFTEN = 3L; // 0x3L
field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
field public static final long USAGE_CPU_WRITE_RARELY = 32L; // 0x20L
field public static final long USAGE_GPU_COLOR_OUTPUT = 512L; // 0x200L
+ field public static final long USAGE_GPU_CUBE_MAP = 33554432L; // 0x2000000L
field public static final long USAGE_GPU_DATA_BUFFER = 16777216L; // 0x1000000L
+ field public static final long USAGE_GPU_MIPMAP_COMPLETE = 67108864L; // 0x4000000L
field public static final long USAGE_GPU_SAMPLED_IMAGE = 256L; // 0x100L
field public static final long USAGE_PROTECTED_CONTENT = 16384L; // 0x4000L
field public static final long USAGE_SENSOR_DIRECT_DATA = 8388608L; // 0x800000L
@@ -15559,8 +15575,10 @@ package android.hardware.camera2 {
method public <T> T get(android.hardware.camera2.CameraCharacteristics.Key<T>);
method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys();
method public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys();
+ method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys();
method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys();
method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys();
+ method public java.util.List<java.lang.String> getPhysicalCameraIds();
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
@@ -15599,6 +15617,7 @@ package android.hardware.camera2 {
field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
@@ -15639,6 +15658,7 @@ package android.hardware.camera2 {
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<boolean[]> STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> STATISTICS_INFO_MAX_FACE_COUNT;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SYNC_MAX_LATENCY;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES;
@@ -15659,6 +15679,7 @@ package android.hardware.camera2 {
public abstract class CameraDevice implements java.lang.AutoCloseable {
method public abstract void close();
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
+ method public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int, java.util.Set<java.lang.String>) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
@@ -15829,6 +15850,7 @@ package android.hardware.camera2 {
field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
+ field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4; // 0x4
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -15844,6 +15866,8 @@ package android.hardware.camera2 {
field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0
field public static final int LENS_STATE_MOVING = 1; // 0x1
field public static final int LENS_STATE_STATIONARY = 0; // 0x0
+ field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0; // 0x0
+ field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1; // 0x1
field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
@@ -15853,6 +15877,7 @@ package android.hardware.camera2 {
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; // 0xb
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; // 0xa
@@ -15902,6 +15927,8 @@ package android.hardware.camera2 {
field public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1; // 0x1
field public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; // 0x0
field public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; // 0x1
+ field public static final int STATISTICS_OIS_DATA_MODE_OFF = 0; // 0x0
+ field public static final int STATISTICS_OIS_DATA_MODE_ON = 1; // 0x1
field public static final int STATISTICS_SCENE_FLICKER_50HZ = 1; // 0x1
field public static final int STATISTICS_SCENE_FLICKER_60HZ = 2; // 0x2
field public static final int STATISTICS_SCENE_FLICKER_NONE = 0; // 0x0
@@ -15984,6 +16011,7 @@ package android.hardware.camera2 {
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_FACE_DETECT_MODE;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE;
field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> TONEMAP_GAMMA;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_MODE;
@@ -15994,8 +16022,10 @@ package android.hardware.camera2 {
method public void addTarget(android.view.Surface);
method public android.hardware.camera2.CaptureRequest build();
method public <T> T get(android.hardware.camera2.CaptureRequest.Key<T>);
+ method public <T> T getPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, java.lang.String);
method public void removeTarget(android.view.Surface);
method public <T> void set(android.hardware.camera2.CaptureRequest.Key<T>, T);
+ method public <T> android.hardware.camera2.CaptureRequest.Builder setPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, T, java.lang.String);
method public void setTag(java.lang.Object);
}
@@ -16083,6 +16113,10 @@ package android.hardware.camera2 {
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key<long[]> STATISTICS_OIS_TIMESTAMPS;
+ field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_X_SHIFTS;
+ field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_Y_SHIFTS;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_SCENE_FLICKER;
field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> TONEMAP_GAMMA;
@@ -16191,6 +16225,7 @@ package android.hardware.camera2.params {
method public int getSurfaceGroupId();
method public java.util.List<android.view.Surface> getSurfaces();
method public void removeSurface(android.view.Surface);
+ method public void setPhysicalCameraId(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -21737,7 +21772,13 @@ package android.media {
field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
+ field public static final int ENCODING_AAC_ELD = 15; // 0xf
+ field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
+ field public static final int ENCODING_AAC_HE_V2 = 12; // 0xc
+ field public static final int ENCODING_AAC_LC = 10; // 0xa
+ field public static final int ENCODING_AAC_XHE = 16; // 0x10
field public static final int ENCODING_AC3 = 5; // 0x5
+ field public static final int ENCODING_AC4 = 17; // 0x11
field public static final int ENCODING_DEFAULT = 1; // 0x1
field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
field public static final int ENCODING_DTS = 7; // 0x7
@@ -21745,6 +21786,7 @@ package android.media {
field public static final int ENCODING_E_AC3 = 6; // 0x6
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
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
@@ -21787,6 +21829,7 @@ package android.media {
method public boolean isBluetoothScoOn();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
+ method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
method public boolean isSpeakerphoneOn();
method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
@@ -22087,6 +22130,7 @@ package android.media {
method public int reloadStaticData();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public void removeStreamEventCallback();
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -22100,6 +22144,7 @@ package android.media {
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
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);
method public int setVolume(float);
method public void stop() throws java.lang.IllegalStateException;
method public int write(byte[], int, int);
@@ -22135,6 +22180,7 @@ package android.media {
method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+ method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
method public android.media.AudioTrack.Builder setPerformanceMode(int);
method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22150,6 +22196,12 @@ package android.media {
method public default void onRoutingChanged(android.media.AudioRouting);
}
+ public static abstract class AudioTrack.StreamEventCallback {
+ method public void onStreamDataRequest(android.media.AudioTrack);
+ method public void onStreamPresentationEnd(android.media.AudioTrack);
+ method public void onTearDown(android.media.AudioTrack);
+ }
+
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
@@ -27345,7 +27397,7 @@ package android.net.wifi {
method public int addNetwork(android.net.wifi.WifiConfiguration);
method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method public static int calculateSignalLevel(int, int);
- method public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
+ method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
@@ -27378,7 +27430,7 @@ package android.net.wifi {
method public boolean setWifiEnabled(boolean);
method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
method public deprecated boolean startScan();
- method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
+ method public deprecated void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method public int updateNetwork(android.net.wifi.WifiConfiguration);
field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
@@ -27408,11 +27460,11 @@ package android.net.wifi {
field public static final int WIFI_STATE_ENABLED = 3; // 0x3
field public static final int WIFI_STATE_ENABLING = 2; // 0x2
field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4
- field public static final int WPS_AUTH_FAILURE = 6; // 0x6
- field public static final int WPS_OVERLAP_ERROR = 3; // 0x3
- field public static final int WPS_TIMED_OUT = 7; // 0x7
- field public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
- field public static final int WPS_WEP_PROHIBITED = 4; // 0x4
+ field public static final deprecated int WPS_AUTH_FAILURE = 6; // 0x6
+ field public static final deprecated int WPS_OVERLAP_ERROR = 3; // 0x3
+ field public static final deprecated int WPS_TIMED_OUT = 7; // 0x7
+ field public static final deprecated int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
+ field public static final deprecated int WPS_WEP_PROHIBITED = 4; // 0x4
}
public static class WifiManager.LocalOnlyHotspotCallback {
@@ -27446,11 +27498,11 @@ package android.net.wifi {
method public void setWorkSource(android.os.WorkSource);
}
- public static abstract class WifiManager.WpsCallback {
+ public static abstract deprecated class WifiManager.WpsCallback {
ctor public WifiManager.WpsCallback();
- method public abstract void onFailed(int);
- method public abstract void onStarted(java.lang.String);
- method public abstract void onSucceeded();
+ method public abstract deprecated void onFailed(int);
+ method public abstract deprecated void onStarted(java.lang.String);
+ method public abstract deprecated void onSucceeded();
}
public class WpsInfo implements android.os.Parcelable {
@@ -27892,6 +27944,29 @@ package android.net.wifi.p2p.nsd {
package android.net.wifi.rtt {
+ public final class LocationCivic implements android.os.Parcelable {
+ method public int describeContents();
+ method public byte[] getData();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationCivic> CREATOR;
+ }
+
+ public final class LocationConfigurationInformation implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getAltitude();
+ method public int getAltitudeType();
+ method public double getAltitudeUncertainty();
+ method public double getLatitude();
+ method public double getLatitudeUncertainty();
+ method public double getLongitude();
+ method public double getLongitudeUncertainty();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ALTITUDE_IN_FLOORS = 2; // 0x2
+ field public static final int ALTITUDE_IN_METERS = 1; // 0x1
+ field public static final int ALTITUDE_UNKNOWN = 0; // 0x0
+ field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationConfigurationInformation> CREATOR;
+ }
+
public final class RangingRequest implements android.os.Parcelable {
method public int describeContents();
method public static int getMaxPeers();
@@ -27915,6 +27990,8 @@ package android.net.wifi.rtt {
method public android.net.MacAddress getMacAddress();
method public android.net.wifi.aware.PeerHandle getPeerHandle();
method public long getRangingTimestampUs();
+ method public android.net.wifi.rtt.LocationCivic getReportedLocationCivic();
+ method public android.net.wifi.rtt.LocationConfigurationInformation getReportedLocationConfigurationInformation();
method public int getRssi();
method public int getStatus();
method public void writeToParcel(android.os.Parcel, int);
@@ -31516,7 +31593,7 @@ package android.os {
}
public final class Debug {
- method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
+ method public static void attachJvmtiAgent(java.lang.String, java.lang.String, java.lang.ClassLoader) throws java.io.IOException;
method public static deprecated void changeDebugPort(int);
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[]);
@@ -33993,9 +34070,10 @@ package android.provider {
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
field public static final java.lang.String FEATURES = "features";
+ field public static final int FEATURES_ASSISTED_DIALING_USED = 16; // 0x10
field public static final int FEATURES_HD_CALL = 4; // 0x4
field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
- field public static final int FEATURES_RTT = 16; // 0x10
+ field public static final int FEATURES_RTT = 32; // 0x20
field public static final int FEATURES_VIDEO = 1; // 0x1
field public static final int FEATURES_WIFI = 8; // 0x8
field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
@@ -37910,7 +37988,6 @@ package android.service.autofill {
}
public static final class FieldClassification.Match {
- method public java.lang.String getAlgorithm();
method public java.lang.String getRemoteId();
method public float getScore();
}
@@ -39844,6 +39921,7 @@ package android.telecom {
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+ field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
field public static final int PROPERTY_CONFERENCE = 1; // 0x1
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -40064,6 +40142,7 @@ package android.telecom {
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int PROPERTY_IS_RTT = 256; // 0x100
@@ -40486,6 +40565,7 @@ package android.telecom {
field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
+ field public static final java.lang.String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO = "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO";
field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
@@ -40501,6 +40581,7 @@ package android.telecom {
field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
+ field public static final java.lang.String EXTRA_USE_ASSISTED_DIALING = "android.telecom.extra.USE_ASSISTED_DIALING";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
@@ -40513,6 +40594,18 @@ package android.telecom {
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
}
+ public final class TransformationInfo implements android.os.Parcelable {
+ ctor public TransformationInfo(java.lang.String, java.lang.String, java.lang.String, java.lang.String, int);
+ method public int describeContents();
+ method public java.lang.String getOriginalNumber();
+ method public java.lang.String getTransformedNumber();
+ method public int getTransformedNumberCountryCallingCode();
+ method public java.lang.String getUserHomeCountryCode();
+ method public java.lang.String getUserRoamingCountryCode();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telecom.TransformationInfo> CREATOR;
+ }
+
public class VideoProfile implements android.os.Parcelable {
ctor public VideoProfile(int);
ctor public VideoProfile(int, int);
@@ -40676,6 +40769,7 @@ package android.telephony {
field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
+ field public static final java.lang.String KEY_ASSISTED_DIALING_ENABLED_BOOL = "assisted_dialing_enabled_bool";
field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
@@ -41421,6 +41515,8 @@ package android.telephony {
method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method public int getAndroidCarrierIdForSubscription();
+ method public java.lang.CharSequence getAndroidCarrierNameForSubscription();
method public int getCallState();
method public android.os.PersistableBundle getCarrierConfig();
method public deprecated android.telephony.CellLocation getCellLocation();
@@ -41458,8 +41554,6 @@ package android.telephony {
method public int getSimState();
method public int getSimState(int);
method public java.lang.String getSubscriberId();
- method public int getSubscriptionCarrierId();
- method public java.lang.String getSubscriptionCarrierName();
method public java.lang.String getVisualVoicemailPackageName();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
@@ -41677,6 +41771,7 @@ package android.telephony.data {
method public java.net.InetAddress getMmsProxy();
method public java.net.URL getMmsc();
method public java.lang.String getMvnoType();
+ method public int getNetworkTypeBitmask();
method public java.lang.String getOperatorNumeric();
method public java.lang.String getPassword();
method public int getPort();
@@ -41720,11 +41815,11 @@ package android.telephony.data {
method public android.telephony.data.ApnSetting.Builder setAuthType(int);
method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
- method public android.telephony.data.ApnSetting.Builder setId(int);
method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
method public android.telephony.data.ApnSetting.Builder setPort(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index f35984aaf8d1..ba9f2ac0983e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -75,6 +75,7 @@ package android {
field public static final java.lang.String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
+ field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
@@ -131,6 +132,7 @@ package android {
field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
+ field public static final java.lang.String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
@@ -380,6 +382,7 @@ package android.app.admin {
method public java.lang.CharSequence getDeviceOwnerOrganizationName();
method public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
method public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
+ method public java.lang.CharSequence getPrintingDisabledReason();
method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
method public int getUserProvisioningState();
@@ -760,6 +763,7 @@ package android.content {
field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
field public static final java.lang.String STATS_MANAGER = "stats";
+ field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final java.lang.String VR_SERVICE = "vrmanager";
field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -1777,9 +1781,9 @@ package android.hardware.radio {
method public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(int);
method public long getFirstId(int);
method public android.hardware.radio.ProgramSelector.Identifier getPrimaryId();
- method public int getProgramType();
+ method public deprecated int getProgramType();
method public android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds();
- method public long[] getVendorIds();
+ method public deprecated long[] getVendorIds();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector> CREATOR;
field public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1; // 0x1
@@ -1787,27 +1791,31 @@ package android.hardware.radio {
field public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8; // 0x8
field public static final int IDENTIFIER_TYPE_DAB_SCID = 7; // 0x7
field public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5
+ field public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5
field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa
- field public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
+ field public static final deprecated int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9
field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3
- field public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
+ field public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; // 0x2714
+ field public static final deprecated int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0
field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
- field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
- field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
- field public static final int PROGRAM_TYPE_AM = 1; // 0x1
- field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3
- field public static final int PROGRAM_TYPE_DAB = 5; // 0x5
- field public static final int PROGRAM_TYPE_DRMO = 6; // 0x6
- field public static final int PROGRAM_TYPE_FM = 2; // 0x2
- field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
- field public static final int PROGRAM_TYPE_INVALID = 0; // 0x0
- field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
- field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
- field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
+ field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf
+ field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
+ field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
+ field public static final int IDENTIFIER_TYPE_VENDOR_START = 1000; // 0x3e8
+ field public static final deprecated int PROGRAM_TYPE_AM = 1; // 0x1
+ field public static final deprecated int PROGRAM_TYPE_AM_HD = 3; // 0x3
+ field public static final deprecated int PROGRAM_TYPE_DAB = 5; // 0x5
+ field public static final deprecated int PROGRAM_TYPE_DRMO = 6; // 0x6
+ field public static final deprecated int PROGRAM_TYPE_FM = 2; // 0x2
+ field public static final deprecated int PROGRAM_TYPE_FM_HD = 4; // 0x4
+ field public static final deprecated int PROGRAM_TYPE_INVALID = 0; // 0x0
+ field public static final deprecated int PROGRAM_TYPE_SXM = 7; // 0x7
+ field public static final deprecated int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
+ field public static final deprecated int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
}
public static final class ProgramSelector.Identifier implements android.os.Parcelable {
@@ -1822,7 +1830,7 @@ package android.hardware.radio {
public static abstract class ProgramSelector.IdentifierType implements java.lang.annotation.Annotation {
}
- public static abstract class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
+ public static abstract deprecated class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
}
public class RadioManager {
@@ -2860,9 +2868,18 @@ package android.net {
method public void onTetheringStarted();
}
+ public final class IpSecManager {
+ method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ }
+
+ public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method public void close();
+ method public java.lang.String getInterfaceName();
+ }
+
public static class IpSecTransform.Builder {
+ method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
method public android.net.IpSecTransform.Builder setNattKeepalive(int);
- method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
}
public class NetworkKey implements android.os.Parcelable {
@@ -3489,6 +3506,22 @@ package android.os {
method public abstract void onResult(android.os.Bundle);
}
+ public class SystemUpdateManager {
+ method public android.os.Bundle retrieveSystemUpdateInfo();
+ method public void updateSystemUpdateInfo(android.os.PersistableBundle);
+ field public static final java.lang.String KEY_IS_SECURITY_UPDATE = "is_security_update";
+ field public static final java.lang.String KEY_STATUS = "status";
+ field public static final java.lang.String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
+ field public static final java.lang.String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
+ field public static final java.lang.String KEY_TITLE = "title";
+ field public static final int STATUS_IDLE = 1; // 0x1
+ field public static final int STATUS_IN_PROGRESS = 3; // 0x3
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ field public static final int STATUS_WAITING_DOWNLOAD = 2; // 0x2
+ field public static final int STATUS_WAITING_INSTALL = 4; // 0x4
+ field public static final int STATUS_WAITING_REBOOT = 5; // 0x5
+ }
+
public class UpdateEngine {
ctor public UpdateEngine();
method public void applyPayload(java.lang.String, long, long, java.lang.String[]);
@@ -3862,20 +3895,10 @@ package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
method public android.os.IBinder onBind(android.content.Intent);
- method public java.util.List<java.lang.String> onGetAvailableAlgorithms();
- method public java.lang.String onGetDefaultAlgorithm();
- method public android.service.autofill.AutofillFieldClassificationService.Scores onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+ method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
- }
-
- public static final class AutofillFieldClassificationService.Scores implements android.os.Parcelable {
- ctor public AutofillFieldClassificationService.Scores(java.lang.String, int, int);
- ctor public AutofillFieldClassificationService.Scores(android.os.Parcel);
- method public int describeContents();
- method public java.lang.String getAlgorithm();
- method public float[][] getScores();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.AutofillFieldClassificationService.Scores> CREATOR;
+ field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
+ field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
}
}
diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h
index c41612de4eb3..166796673e25 100644
--- a/cmds/incident_helper/src/TextParserBase.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -68,4 +68,4 @@ public:
virtual status_t Parse(const int in, const int out) const;
};
-#endif // TEXT_PARSER_BASE_H \ No newline at end of file
+#endif // TEXT_PARSER_BASE_H
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 8420bc8f7ac7..368a70bc3898 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -134,6 +134,7 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
libincident \
liblog \
+ libprotobuf-cpp-lite \
libprotoutil \
libselinux \
libservices \
diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md
index ad0fa08c7326..71c6deb18aac 100644
--- a/cmds/incidentd/README.md
+++ b/cmds/incidentd/README.md
@@ -12,8 +12,8 @@ Run the test on a device manually
```
root$ mmm -j frameworks/base/cmds/incidentd && \
-adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/incidentd_test/ && \
-adb shell /data/nativetest/incidentd_test/incidentd_test 2>/dev/null
+adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/ && \
+adb shell /data/nativetest/incidentd_test 2>/dev/null
```
Run the test via AndroidTest.xml
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
index 66667dca2982..1bd146850ea9 100644
--- a/cmds/incidentd/incidentd.rc
+++ b/cmds/incidentd/incidentd.rc
@@ -19,4 +19,4 @@ service incidentd /system/bin/incidentd
on post-fs-data
# Create directory for incidentd
- mkdir /data/misc/incidents 0770 root root
+ mkdir /data/misc/incidents 0770 incidentd incidentd
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 30dd339a629b..0fff4e6dc4a0 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -63,12 +63,14 @@ FdBuffer::read(int fd, int64_t timeout)
int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
if (remainingTime <= 0) {
+ if (DEBUG) ALOGD("timed out due to long read");
mTimedOut = true;
break;
}
int count = poll(&pfds, 1, remainingTime);
if (count == 0) {
+ if (DEBUG) ALOGD("timed out due to block calling poll");
mTimedOut = true;
break;
} else if (count < 0) {
@@ -129,6 +131,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
if (remainingTime <= 0) {
+ if (DEBUG) ALOGD("timed out due to long read");
mTimedOut = true;
break;
}
@@ -136,6 +139,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
// wait for any pfds to be ready to perform IO
int count = poll(pfds, 3, remainingTime);
if (count == 0) {
+ if (DEBUG) ALOGD("timed out due to block calling poll");
mTimedOut = true;
break;
} else if (count < 0) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index c4b54bbbc022..1d5ab59f9ba8 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -43,24 +43,44 @@ String16 const DUMP_PERMISSION("android.permission.DUMP");
String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
static Status
-checkIncidentPermissions()
+checkIncidentPermissions(const IncidentReportArgs& args)
{
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (callingUid == AID_ROOT || callingUid == AID_SHELL) {
+ // root doesn't have permission.DUMP if don't do this!
+ return Status::ok();
+ }
+
+ // checking calling permission.
if (!checkCallingPermission(DUMP_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
- IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ IPCThreadState::self()->getCallingPid(), callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.DUMP");
}
if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
- IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ IPCThreadState::self()->getCallingPid(), callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.USAGE_STATS");
}
+
+ // checking calling request uid permission.
+ switch (args.dest()) {
+ case DEST_LOCAL:
+ if (callingUid != AID_SHELL || callingUid != AID_ROOT) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Calling process does not have permission to get local data.");
+ }
+ case DEST_EXPLICIT:
+ if (callingUid != AID_SHELL || callingUid != AID_ROOT ||
+ callingUid != AID_STATSD || callingUid != AID_SYSTEM) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Calling process does not have permission to get explicit data.");
+ }
+ }
return Status::ok();
}
-
-
// ================================================================================
ReportRequestQueue::ReportRequestQueue()
{
@@ -71,7 +91,7 @@ ReportRequestQueue::~ReportRequestQueue()
}
void
-ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
+ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
{
unique_lock<mutex> lock(mLock);
mQueue.push_back(request);
@@ -196,7 +216,7 @@ IncidentService::reportIncident(const IncidentReportArgs& args)
{
ALOGI("reportIncident");
- Status status = checkIncidentPermissions();
+ Status status = checkIncidentPermissions(args);
if (!status.isOk()) {
return status;
}
@@ -212,7 +232,7 @@ IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
{
ALOGI("reportIncidentToStream");
- Status status = checkIncidentPermissions();
+ Status status = checkIncidentPermissions(args);
if (!status.isOk()) {
return status;
}
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 34930aa57321..bd559d6980f1 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -251,7 +251,7 @@ Reporter::create_file(int* fd)
// Override umask. Not super critical. If it fails go on with life.
chmod(filename, 0660);
- if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
+ if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) {
ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
status_t err = -errno;
unlink(mFilename.c_str());
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 61d16f815e65..0827785811b6 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -19,6 +19,7 @@
#include "Section.h"
#include <errno.h>
+#include <sys/prctl.h>
#include <unistd.h>
#include <wait.h>
@@ -30,7 +31,6 @@
#include <log/log_event_list.h>
#include <log/logprint.h>
#include <log/log_read.h>
-#include <private/android_filesystem_config.h> // for AID_NOBODY
#include <private/android_logger.h>
#include "FdBuffer.h"
@@ -55,26 +55,20 @@ static pid_t
fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
{
const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
-
// fork used in multithreaded environment, avoid adding unnecessary code in child process
pid_t pid = fork();
if (pid == 0) {
- // child process executes incident helper as nobody
- if (setgid(AID_NOBODY) == -1) {
- ALOGW("%s can't change gid: %s", name, strerror(errno));
- _exit(EXIT_FAILURE);
- }
- if (setuid(AID_NOBODY) == -1) {
- ALOGW("%s can't change uid: %s", name, strerror(errno));
- _exit(EXIT_FAILURE);
- }
-
- if (dup2(p2cPipe.readFd(), STDIN_FILENO) != 0 || !p2cPipe.close() ||
- dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) {
+ if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0
+ || !p2cPipe.close()
+ || TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1
+ || !c2pPipe.close()) {
ALOGW("%s can't setup stdin and stdout for incident helper", name);
_exit(EXIT_FAILURE);
}
+ /* make sure the child dies when incidentd dies */
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+
execv(INCIDENT_HELPER, const_cast<char**>(ihArgs));
ALOGW("%s failed in incident helper process: %s", name, strerror(errno));
@@ -87,11 +81,23 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi
}
// ================================================================================
+static status_t statusCode(int status) {
+ if (WIFSIGNALED(status)) {
+ ALOGD("return by signal: %s", strerror(WTERMSIG(status)));
+ return -WTERMSIG(status);
+ } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
+ ALOGD("return by exit: %s", strerror(WEXITSTATUS(status)));
+ return -WEXITSTATUS(status);
+ }
+ return NO_ERROR;
+}
+
static status_t kill_child(pid_t pid) {
int status;
+ ALOGD("try to kill child process %d", pid);
kill(pid, SIGKILL);
if (waitpid(pid, &status, 0) == -1) return -1;
- return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+ return statusCode(status);
}
static status_t wait_child(pid_t pid) {
@@ -104,7 +110,7 @@ static status_t wait_child(pid_t pid) {
nanosleep(&WAIT_INTERVAL_NS, NULL);
}
if (!died) return kill_child(pid);
- return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+ return statusCode(status);
}
// ================================================================================
static const Privacy*
@@ -275,9 +281,9 @@ FileSection::Execute(ReportRequestSet* requests) const
status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
this->timeoutMs, mIsSysfs);
if (readStatus != NO_ERROR || buffer.timedOut()) {
- ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
- this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
- strerror(-kill_child(pid)));
+ ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s",
+ this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ kill_child(pid);
return readStatus;
}
@@ -543,10 +549,10 @@ CommandSection::Execute(ReportRequestSet* requests) const
close(cmdPipe.writeFd());
status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs);
if (readStatus != NO_ERROR || buffer.timedOut()) {
- ALOGW("CommandSection '%s' failed to read data from incident helper: %s, "
- "timedout: %s, kill command: %s, kill incident helper: %s",
- this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
- strerror(-kill_child(cmdPid)), strerror(-kill_child(ihPid)));
+ ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s",
+ this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ kill_child(cmdPid);
+ kill_child(ihPid);
return readStatus;
}
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
index af4a35cc0015..90f543e30ff7 100644
--- a/cmds/incidentd/src/io_util.cpp
+++ b/cmds/incidentd/src/io_util.cpp
@@ -23,7 +23,7 @@
status_t write_all(int fd, uint8_t const* buf, size_t size)
{
while (size > 0) {
- ssize_t amt = ::write(fd, buf, size);
+ ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size));
if (amt < 0) {
return -errno;
}
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index 65030b3a1799..20111d8ae89a 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -58,26 +58,9 @@ create_directory(const char* directory)
goto done;
}
} else {
- if (mkdir(dir, 0770)) {
- ALOGE("No incident reports today. "
- "Unable to create incident report dir %s: %s", dir,
- strerror(errno));
- err = -errno;
- goto done;
- }
- if (chmod(dir, 0770)) {
- ALOGE("No incident reports today. "
- "Unable to set permissions for incident report dir %s: %s", dir,
- strerror(errno));
- err = -errno;
- goto done;
- }
- if (chown(dir, AID_SYSTEM, AID_SYSTEM)) {
- ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n",
- dir, strerror(errno));
- err = -errno;
- goto done;
- }
+ ALOGE("No such directory %s, something wrong.", dir);
+ err = -1;
+ goto done;
}
if (!last) {
*d++ = '/';
@@ -97,8 +80,7 @@ create_directory(const char* directory)
err = BAD_VALUE;
goto done;
}
- if ((st.st_uid != AID_SYSTEM && st.st_uid != AID_ROOT) ||
- (st.st_gid != AID_SYSTEM && st.st_gid != AID_ROOT)) {
+ if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) {
ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
st.st_uid, st.st_gid, directory);
err = BAD_VALUE;
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index 531c9f29bf03..c494bd646b0b 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -17,6 +17,7 @@
#include "Reporter.h"
#include <android/os/BnIncidentReportStatusListener.h>
+#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
#include <android-base/file.h>
#include <android-base/test_utils.h>
@@ -29,6 +30,7 @@
using namespace android;
using namespace android::base;
using namespace android::binder;
+using namespace android::os;
using namespace std;
using ::testing::StrEq;
using ::testing::Test;
@@ -141,7 +143,8 @@ TEST_F(ReporterTest, RunReportWithHeaders) {
IncidentReportArgs args1, args2;
args1.addSection(1);
args2.addSection(2);
- std::vector<uint8_t> header {'a', 'b', 'c', 'd', 'e'};
+ IncidentHeaderProto header;
+ header.set_alert_id(12);
args2.addHeader(header);
sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd);
sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd);
@@ -153,7 +156,7 @@ TEST_F(ReporterTest, RunReportWithHeaders) {
string result;
ReadFileToString(tf.path, &result);
- EXPECT_THAT(result, StrEq("\n\x5" "abcde"));
+ EXPECT_THAT(result, StrEq("\n\x2" "\b\f"));
EXPECT_EQ(l->startInvoked, 2);
EXPECT_EQ(l->finishInvoked, 2);
@@ -164,13 +167,16 @@ TEST_F(ReporterTest, RunReportWithHeaders) {
TEST_F(ReporterTest, RunReportToGivenDirectory) {
IncidentReportArgs args;
- args.addHeader({'1', '2', '3'});
- args.addHeader({'a', 'b', 'c', 'd'});
+ IncidentHeaderProto header1, header2;
+ header1.set_alert_id(12);
+ header2.set_reason("abcd");
+ args.addHeader(header1);
+ args.addHeader(header2);
sp<ReportRequest> r = new ReportRequest(args, l, -1);
reporter->batch.add(r);
ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
vector<string> results = InspectFiles();
ASSERT_EQ((int)results.size(), 1);
- EXPECT_EQ(results[0], "\n\x3" "123\n\x4" "abcd");
+ EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd");
}
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index cbfb89685de0..2cfd7df6be84 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -18,6 +18,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
+#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string.h>
@@ -34,6 +35,7 @@ const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
using namespace android::base;
using namespace android::binder;
+using namespace android::os;
using namespace std;
using ::testing::StrEq;
using ::testing::internal::CaptureStdout;
@@ -66,15 +68,9 @@ TEST(SectionTest, HeaderSection) {
args1.addSection(2);
args2.setAll(true);
- vector<uint8_t> head1;
- head1.push_back('a');
- head1.push_back('x');
- head1.push_back('e');
-
- vector<uint8_t> head2;
- head2.push_back('p');
- head2.push_back('u');
- head2.push_back('p');
+ IncidentHeaderProto head1, head2;
+ head1.set_reason("axe");
+ head2.set_reason("pup");
args1.addHeader(head1);
args1.addHeader(head2);
@@ -87,10 +83,10 @@ TEST(SectionTest, HeaderSection) {
string content;
CaptureStdout();
ASSERT_EQ(NO_ERROR, hs.Execute(&requests));
- EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x3" "axe\n\x03pup"));
+ EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "\x12\x3" "axe\n\x05\x12\x03pup"));
EXPECT_TRUE(ReadFileToString(output2.path, &content));
- EXPECT_THAT(content, StrEq("\n\x03pup"));
+ EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
}
TEST(SectionTest, FileSection) {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5eff54887be1..01f4a84bbb93 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -34,6 +34,7 @@ statsd_common_src := \
src/config/ConfigKey.cpp \
src/config/ConfigListener.cpp \
src/config/ConfigManager.cpp \
+ src/external/Perfetto.cpp \
src/external/StatsPuller.cpp \
src/external/StatsCompanionServicePuller.cpp \
src/external/SubsystemSleepStatePuller.cpp \
@@ -57,6 +58,7 @@ statsd_common_src := \
src/metrics/MetricsManager.cpp \
src/metrics/metrics_manager_util.cpp \
src/packages/UidMap.cpp \
+ src/perfetto/perfetto_config.proto \
src/storage/StorageManager.cpp \
src/StatsLogProcessor.cpp \
src/StatsService.cpp \
@@ -209,6 +211,7 @@ LOCAL_MODULE := statsdprotolite
LOCAL_SRC_FILES := \
src/stats_log.proto \
src/statsd_config.proto \
+ src/perfetto/perfetto_config.proto \
src/atoms.proto
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 9d6d8a1f2b73..7a7a2f617e6a 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -127,6 +127,10 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) {
StatsdStats::getInstance().noteAtomLogged(
event->GetTagId(), event->GetTimestampNs() / NS_PER_SEC);
+ if (mMetricsManagers.empty()) {
+ return;
+ }
+
// Hard-coded logic to update the isolated uid's in the uid-map.
// The field numbers need to be currently updated by hand with atoms.proto
if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index f10b2cf618cd..e34aed33a3cb 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,7 +18,9 @@
#include "Log.h"
#include "AnomalyTracker.h"
+#include "external/Perfetto.h"
#include "guardrail/StatsdStats.h"
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
#include <android/os/IIncidentManager.h>
#include <android/os/IncidentReportArgs.h>
@@ -239,7 +241,7 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
}
break;
case Subscription::SubscriberInformationCase::kPerfettoDetails:
- ALOGW("Perfetto reports not implemented.");
+ CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
break;
default:
break;
@@ -253,10 +255,10 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
for (const auto section : incidentdSections) {
incidentReport.addSection(section);
}
- int64_t alertId = mAlert.id();
- std::vector<uint8_t> header;
- uint8_t* src = static_cast<uint8_t*>(static_cast<void*>(&alertId));
- header.insert(header.end(), src, src + sizeof(int64_t));
+ android::os::IncidentHeaderProto header;
+ header.set_alert_id(mAlert.id());
+ header.mutable_config_key()->set_uid(mConfigKey.GetUid());
+ header.mutable_config_key()->set_id(mConfigKey.GetId());
incidentReport.addHeader(header);
service->reportIncident(incidentReport);
} else {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7d513cd01694..a07bd2f50607 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -84,6 +84,7 @@ message Atom {
AppStartChanged app_start_changed = 48;
AppStartCancelChanged app_start_cancel_changed = 49;
AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
+ LmkEventOccurred lmk_event_occurred = 51;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -101,6 +102,11 @@ message Atom {
CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
WifiActivityEnergyInfo wifi_activity_energy_info = 10011;
ModemActivityInfo modem_activity_info = 10012;
+ MemoryStat memory_stat = 10013;
+ CpuSuspendTime cpu_suspend_time = 10014;
+ CpuIdleTime cpu_idle_time = 10015;
+ CpuActiveTime cpu_active_time = 10016;
+ CpuClusterTime cpu_cluster_time = 10017;
}
}
@@ -321,8 +327,9 @@ message ScheduledJobStateChanged {
optional string name = 2;
enum State {
- OFF = 0;
- ON = 1;
+ FINISHED = 0;
+ STARTED = 1;
+ SCHEDULED = 2;
}
optional State state = 3;
@@ -1177,3 +1184,91 @@ message ModemActivityInfo {
// product of current(mA), voltage(V) and time(ms)
optional uint64 energy_used = 10;
}
+
+/*
+ * Logs the memory stats for a process
+ */
+message MemoryStat {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1;
+
+ // The app package name.
+ optional string pkg_name = 2;
+
+ // # of page-faults
+ optional int64 pgfault = 3;
+
+ // # of major page-faults
+ optional int64 pgmajfault = 4;
+
+ // RSS+CACHE(+SWAP)
+ optional int64 usage_in_bytes = 5;
+}
+
+/*
+ * Logs the event when LMKD kills a process to reduce memory pressure
+ * Logged from:
+ * system/core/lmkd/lmkd.c
+ */
+message LmkEventOccurred {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1;
+
+ // The app package name.
+ optional string pkg_name = 2;
+
+ // oom adj score.
+ optional int32 oom_score = 3;
+
+ // Used as start/stop boundaries for the event
+ enum State {
+ UNKNOWN = 0;
+ START = 1;
+ END = 2;
+ }
+ optional State state = 4;
+}
+
+/*
+ * Cpu syspend time for cpu power calculation.
+ */
+message CpuSuspendTime {
+ optional uint64 time = 1;
+}
+
+/*
+ * Cpu idle time for cpu power calculation.
+ */
+message CpuIdleTime {
+ optional uint64 time = 1;
+}
+
+/*
+ * Reads from /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+message CpuActiveTime {
+ optional uint64 uid = 1;
+ optional uint64 idx = 2;
+ optional uint64 time_ms = 3;
+}
+
+/**
+ * Reads from /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+message CpuClusterTime {
+ optional uint64 uid = 1;
+ optional uint64 idx = 2;
+ optional uint64 time_ms = 3;
+} \ No newline at end of file
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index a61afb429f54..a75127324745 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -34,7 +34,7 @@ namespace android {
namespace os {
namespace statsd {
-static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const string sProcFile = "/proc/uid_time_in_state";
static const int kLineBufferSize = 1024;
/**
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
new file mode 100644
index 000000000000..7a2d1991a538
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+
+#include "KernelUidCpuActiveTimeReader.h"
+#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_concurrent_active_time";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+KernelUidCpuActiveTimeReader::KernelUidCpuActiveTimeReader() : StatsPuller(android::util::CPU_ACTIVE_TIME) {
+}
+
+bool KernelUidCpuActiveTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ char* pch;
+ while (!fin.eof()) {
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok(buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(NULL, " ");
+ uint64_t timeMs;
+ int idx = 0;
+ do {
+ timeMs = std::stoull(pch);
+ auto ptr = make_shared<LogEvent>(mTagId, timestamp);
+ ptr->write(uid);
+ ptr->write(idx);
+ ptr->write(timeMs);
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, freq idx %d, active time %lld", (long long)uid, idx, (long long)timeMs);
+ idx++;
+ pch = strtok(NULL, " ");
+ } while (pch != NULL);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h
new file mode 100644
index 000000000000..fcae35fa6eb5
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h
@@ -0,0 +1,34 @@
+/*
+ * 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 {
+
+class KernelUidCpuActiveTimeReader : public StatsPuller {
+ public:
+ KernelUidCpuActiveTimeReader();
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
new file mode 100644
index 000000000000..7426e743c40f
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "KernelUidCpuClusterTimeReader.h"
+#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_concurrent_policy_time";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+KernelUidCpuClusterTimeReader::KernelUidCpuClusterTimeReader() : StatsPuller(android::util::CPU_CLUSTER_TIME) {
+}
+
+bool KernelUidCpuClusterTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ char* pch;
+ while (!fin.eof()) {
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok(buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(NULL, " ");
+ uint64_t timeMs;
+ int idx = 0;
+ do {
+ timeMs = std::stoull(pch);
+ auto ptr = make_shared<LogEvent>(mTagId, timestamp);
+ ptr->write(uid);
+ ptr->write(idx);
+ ptr->write(timeMs);
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, freq idx %d, cluster time %lld", (long long)uid, idx, (long long)timeMs);
+ idx++;
+ pch = strtok(NULL, " ");
+ } while (pch != NULL);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h
new file mode 100644
index 000000000000..90236ae00762
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h
@@ -0,0 +1,42 @@
+/*
+ * 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 /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class KernelUidCpuClusterTimeReader : public StatsPuller {
+ public:
+ KernelUidCpuClusterTimeReader();
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
new file mode 100644
index 000000000000..f7b33e77c557
--- /dev/null
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 "Log.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+
+#include <android-base/unique_fd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace {
+const char kDropboxTag[] = "perfetto";
+}
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) {
+ ALOGD("Starting trace collection through perfetto");
+
+ if (!config.has_trace_config()) {
+ ALOGE("The perfetto trace config is empty, aborting");
+ return false;
+ }
+
+ android::base::unique_fd readPipe;
+ android::base::unique_fd writePipe;
+ if (!android::base::Pipe(&readPipe, &writePipe)) {
+ ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno));
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno));
+ return false;
+ }
+
+ if (pid == 0) {
+ // Child process.
+
+ // No malloc calls or library calls after this point. Remember that even
+ // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf().
+
+ writePipe.reset(); // Close the write end (owned by the main process).
+
+ // Replace stdin with |readPipe| so the main process can write into it.
+ if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1);
+ execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
+ kDropboxTag, nullptr);
+
+ // execl() doesn't return in case of success, if we get here something failed.
+ _exit(1);
+ }
+
+ // Main process.
+
+ readPipe.reset(); // Close the read end (owned by the child process).
+
+ // Using fopen() because fwrite() has the right logic to chunking write()
+ // over a pipe (see __sfvwrite()).
+ FILE* writePipeStream = fdopen(writePipe.get(), "wb");
+ if (!writePipeStream) {
+ ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
+ return false;
+ }
+
+ std::string cfgProto = config.trace_config().SerializeAsString();
+ size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
+ fclose(writePipeStream);
+ if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
+ ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten,
+ strerror(errno));
+ return false;
+ }
+
+ // This does NOT wait for the full duration of the trace. It just waits until the process
+ // has read the config from stdin and detached.
+ int childStatus = 0;
+ waitpid(pid, &childStatus, 0);
+ if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) {
+ ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus);
+ return false;
+ }
+
+ ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded");
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
new file mode 100644
index 000000000000..e2e02533f17f
--- /dev/null
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+using android::os::StatsLogEventWrapper;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PerfettoDetails; // Declared in statsd_config.pb.h
+
+// Starts the collection of a Perfetto trace with the given |config|.
+// The trace is uploaded to Dropbox by the perfetto cmdline util once done.
+// This method returns immediately after passing the config and does NOT wait
+// for the full duration of the trace.
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index bb2e8c08dfe6..79f1a5d84174 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -64,6 +64,8 @@ StatsPullerManagerImpl::StatsPullerManagerImpl()
mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
+ mPullers.insert({android::util::CPU_SUSPEND_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_SUSPEND_TIME)});
+ mPullers.insert({android::util::CPU_IDLE_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_IDLE_TIME)});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 6782f3fd6fcf..34fa3c404d10 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -36,13 +36,13 @@ using std::string;
using android::util::ProtoOutputStream;
LogEvent::LogEvent(log_msg& msg) {
- android_log_context context =
+ mContext =
create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
mLogUid = msg.entry_v4.uid;
- init(context);
- if (context) {
- android_log_destroy(&context);
+ init(mContext);
+ if (mContext) {
+ android_log_destroy(&mContext);
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index c7550f7d4711..e98587356857 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -285,8 +285,15 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(
interval.start = value;
interval.startUpdated = true;
} else {
+ // Generally we expect value to be monotonically increasing.
+ // If not, there was a reset event. We take the absolute value as
+ // diff in this case.
if (interval.startUpdated) {
- interval.sum += (value - interval.start);
+ if (value > interval.start) {
+ interval.sum += (value - interval.start);
+ } else {
+ interval.sum += value;
+ }
interval.startUpdated = false;
} else {
VLOG("No start for matching end %ld", value);
diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto
new file mode 100644
index 000000000000..dc868f997a63
--- /dev/null
+++ b/cmds/statsd/src/perfetto/perfetto_config.proto
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+message DataSourceConfig {
+ message FtraceConfig {
+ repeated string event_names = 1;
+ }
+
+ optional string name = 1;
+
+ optional uint32 target_buffer = 2;
+
+ optional FtraceConfig ftrace_config = 100;
+}
+
+message TraceConfig {
+ message BufferConfig {
+ optional uint32 size_kb = 1;
+
+ enum OptimizeFor {
+ DEFAULT = 0;
+ ONE_SHOT_READ = 1;
+
+ }
+ optional OptimizeFor optimize_for = 3;
+
+ enum FillPolicy {
+ UNSPECIFIED = 0;
+ RING_BUFFER = 1;
+ }
+ optional FillPolicy fill_policy = 4;
+ }
+ repeated BufferConfig buffers = 1;
+
+ message DataSource {
+ optional protos.DataSourceConfig config = 1;
+
+ repeated string producer_name_filter = 2;
+ }
+ repeated DataSource data_sources = 2;
+
+ optional uint32 duration_ms = 3;
+}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 624785486043..cd60ee74e3ad 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -22,6 +22,8 @@ package android.os.statsd;
option java_package = "com.android.internal.os";
option java_outer_classname = "StatsdConfigProto";
+import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
+
enum Position {
POSITION_UNKNOWN = 0;
FIRST = 1;
@@ -272,7 +274,7 @@ message IncidentdDetails {
}
message PerfettoDetails {
- optional int32 perfetto_stuff = 1;
+ optional perfetto.protos.TraceConfig trace_config = 1;
}
message Subscription {
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index a4c080063284..32a85b136023 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -19,13 +19,10 @@ include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := StatsdDogfood
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += ../../src/stats_log.proto \
- ../../src/atoms.proto \
- ../../src/statsd_config.proto
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite
+LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
+ statsdprotolite
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_PRIVILEGED_MODULE := true
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
index 0a0fd6647fbb..f5722c2c3d19 100644
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ b/cmds/statsd/tools/loadtest/Android.mk
@@ -21,6 +21,7 @@ LOCAL_PACKAGE_NAME := StatsdLoadtest
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += ../../src/stats_log.proto \
../../src/atoms.proto \
+ ../../src/perfetto/perfetto_config.proto \
../../src/statsd_config.proto
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 97dcb90bbb99..0a4541ba4777 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -363,6 +363,11 @@ public abstract class AccessibilityService extends Service {
*/
public static final int GLOBAL_ACTION_LOCK_SCREEN = 8;
+ /**
+ * Action to take a screenshot
+ */
+ public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ccb54f93ba33..da9f72855e09 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -339,4 +339,9 @@ public abstract class ActivityManagerInternal {
* Returns maximum number of users that can run simultaneously.
*/
public abstract int getMaxRunningUsers();
+
+ /**
+ * Returns is the caller has the same uid as the Recents component
+ */
+ public abstract boolean isCallerRecents(int callingUid);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 57f9f67b4f9c..7ca680239eef 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1326,6 +1326,25 @@ public class AppOpsManager {
}
/**
+ * Retrieve the human readable mode.
+ * @hide
+ */
+ public static String modeToString(int mode) {
+ switch (mode) {
+ case MODE_ALLOWED:
+ return "allow";
+ case MODE_IGNORED:
+ return "ignore";
+ case MODE_ERRORED:
+ return "deny";
+ case MODE_DEFAULT:
+ return "default";
+ default:
+ return "mode=" + mode;
+ }
+ }
+
+ /**
* Retrieve whether the op allows itself to be reset.
* @hide
*/
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b469de56d857..c5a58f2eef30 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -458,7 +459,8 @@ public class Instrumentation {
*
* @see Context#startActivity(Intent, Bundle)
*/
- public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
+ @NonNull
+ public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
validateNotAppThread();
synchronized (mSync) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 33277eae0520..fb8d1017e205 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -112,6 +112,7 @@ import android.os.IBinder;
import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
+import android.os.ISystemUpdateManager;
import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
@@ -119,6 +120,7 @@ import android.os.Process;
import android.os.RecoverySystem;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemUpdateManager;
import android.os.SystemVibrator;
import android.os.UserHandle;
import android.os.UserManager;
@@ -485,6 +487,17 @@ final class SystemServiceRegistry {
return new StorageStatsManager(ctx, service);
}});
+ registerService(Context.SYSTEM_UPDATE_SERVICE, SystemUpdateManager.class,
+ new CachedServiceFetcher<SystemUpdateManager>() {
+ @Override
+ public SystemUpdateManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.SYSTEM_UPDATE_SERVICE);
+ ISystemUpdateManager service = ISystemUpdateManager.Stub.asInterface(b);
+ return new SystemUpdateManager(service);
+ }});
+
registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,
new CachedServiceFetcher<TelephonyManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0be55642d4cf..7fccda8c04e3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1157,9 +1157,17 @@ public class DevicePolicyManager {
public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
/**
+ * Constant to indicate the feature of mandatory backups. Used as argument to
+ * {@link #createAdminSupportIntent(String)}.
+ * @see #setMandatoryBackupTransport(ComponentName, ComponentName)
+ */
+ public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
+
+ /**
* A String indicating a specific restricted feature. Can be a user restriction from the
* {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values
- * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+ * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
+ * {@link #POLICY_MANDATORY_BACKUPS}.
* @see #createAdminSupportIntent(String)
* @hide
*/
@@ -6806,7 +6814,8 @@ public class DevicePolicyManager {
* @param restriction Indicates for which feature the dialog should be displayed. Can be a
* user restriction from {@link UserManager}, e.g.
* {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
- * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+ * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
+ * {@link #POLICY_MANDATORY_BACKUPS}.
* @return Intent An intent to be used to start the dialog-activity if the restriction is
* set by an admin, or null if the restriction does not exist or no admin set it.
*/
@@ -9201,10 +9210,13 @@ public class DevicePolicyManager {
/**
* Allows/disallows printing.
*
+ * Called by a device owner or a profile owner.
+ * Device owner changes policy for all users. Profile owner can override it if present.
+ * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored.
+ *
* @param admin which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled whether printing should be allowed or not.
* @throws SecurityException if {@code admin} is neither device, nor profile owner.
- * @hide
*/
public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) {
try {
@@ -9215,10 +9227,12 @@ public class DevicePolicyManager {
}
/**
- * Returns whether printing is enabled for current user.
+ * Returns whether printing is enabled for this user.
+ *
+ * Always {@code false} if {@code FEATURE_PRINTING} is absent.
+ * Otherwise, {@code true} by default.
*
* @return {@code true} iff printing is enabled.
- * @hide
*/
public boolean isPrintingEnabled() {
try {
@@ -9233,9 +9247,9 @@ public class DevicePolicyManager {
*
* Used only by PrintService.
* @return Localized error message.
- * @throws SecurityException if caller is not system.
* @hide
*/
+ @SystemApi
public CharSequence getPrintingDisabledReason() {
try {
return mService.getPrintingDisabledReason();
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 5f0e542f4b12..4461b16fe15c 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -29,4 +29,6 @@ interface ISliceManager {
void unpinSlice(String pkg, in Uri uri);
boolean hasSliceAccess(String pkg);
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);
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 6093a4a02fbe..5bd3440d09f8 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -156,6 +156,13 @@ public final class Slice implements Parcelable {
*/
public static final String HINT_SEE_MORE = "see_more";
/**
+ * A hint to tell the system that this slice cares about the return value of
+ * {@link SliceProvider#getBindingPackage} and should not cache the result
+ * for multiple apps.
+ * @hide
+ */
+ public static final String HINT_CALLER_NEEDED = "caller_needed";
+ /**
* Key to retrieve an extra added to an intent when a control is changed.
*/
public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 74864cb1a371..09c420c3e66a 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -53,12 +53,34 @@ public class SliceManager {
private static final String TAG = "SliceManager";
+ /**
+ * @hide
+ */
+ public static final String ACTION_REQUEST_SLICE_PERMISSION =
+ "android.intent.action.REQUEST_SLICE_PERMISSION";
+
private final ISliceManager mService;
private final Context mContext;
private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
new ArrayMap<>();
/**
+ * Permission denied.
+ * @hide
+ */
+ public static final int PERMISSION_DENIED = -1;
+ /**
+ * Permission granted.
+ * @hide
+ */
+ public static final int PERMISSION_GRANTED = 0;
+ /**
+ * Permission just granted by the user, and should be granted uri permission as well.
+ * @hide
+ */
+ public static final int PERMISSION_USER_GRANTED = 1;
+
+ /**
* @hide
*/
public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
@@ -284,7 +306,7 @@ public class SliceManager {
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+ final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
null, extras);
Bundle.setDefusable(res, true);
if (res == null) {
@@ -342,7 +364,7 @@ public class SliceManager {
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(),
+ final Bundle res = provider.call(mContext.getPackageName(),
SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
@@ -358,6 +380,45 @@ public class SliceManager {
}
/**
+ * Does the permission check to see if a caller has access to a specific slice.
+ * @hide
+ */
+ public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+ try {
+ if (pkg == null) {
+ throw new SecurityException("No pkg specified");
+ }
+ int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+ if (result == PERMISSION_DENIED) {
+ throw new SecurityException("User " + uid + " does not have slice permission for "
+ + uri + ".");
+ }
+ if (result == PERMISSION_USER_GRANTED) {
+ // We just had a user grant of this permission and need to grant this to the app
+ // permanently.
+ mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by SystemUI to grant a slice permission after a dialog is shown.
+ * @hide
+ */
+ public void grantPermissionFromUser(Uri uri, String pkg, boolean allSlices) {
+ try {
+ mService.grantPermissionFromUser(uri, pkg, mContext.getPackageName(), allSlices);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Class that listens to changes in {@link Slice}s.
*/
public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index aa41f14d8cb5..c4316a0ba086 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -15,13 +15,19 @@
*/
package android.app.slice;
-import android.Manifest.permission;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProviderInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
@@ -129,9 +135,41 @@ public abstract class SliceProvider extends ContentProvider {
* @hide
*/
public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_PKG = "pkg";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_OVERRIDE_PKG = "override_pkg";
private static final boolean DEBUG = false;
+ private String mBindingPkg;
+ private SliceManager mSliceManager;
+
+ /**
+ * Return the package name of the caller that initiated the binding request
+ * currently happening. The returned package will have been
+ * verified to belong to the calling UID. Returns {@code null} if not
+ * currently performing an {@link #onBindSlice(Uri, List)}.
+ * @hide
+ */
+ public final @Nullable String getBindingPackage() {
+ return mBindingPkg;
+ }
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ super.attachInfo(context, info);
+ mSliceManager = context.getSystemService(SliceManager.class);
+ }
+
/**
* Implemented to create a slice. Will be called on the main thread.
* <p>
@@ -261,54 +299,47 @@ public abstract class SliceProvider extends ContentProvider {
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (method.equals(METHOD_SLICE)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
- }
+ Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
- Slice s = handleBindSlice(uri, supportedSpecs);
+ String callingPackage = getCallingPackage();
+ if (extras.containsKey(EXTRA_OVERRIDE_PKG)) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can override calling pkg");
+ }
+ callingPackage = extras.getString(EXTRA_OVERRIDE_PKG);
+ }
+ Slice s = handleBindSlice(uri, supportedSpecs, callingPackage);
Bundle b = new Bundle();
b.putParcelable(EXTRA_SLICE, s);
return b;
} else if (method.equals(METHOD_MAP_INTENT)) {
- getContext().enforceCallingPermission(permission.BIND_SLICE,
- "Slice binding requires the permission BIND_SLICE");
Intent intent = extras.getParcelable(EXTRA_INTENT);
if (intent == null) return null;
Uri uri = onMapIntentToUri(intent);
List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Bundle b = new Bundle();
if (uri != null) {
- Slice s = handleBindSlice(uri, supportedSpecs);
+ Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage());
b.putParcelable(EXTRA_SLICE, s);
} else {
b.putParcelable(EXTRA_SLICE, null);
}
return b;
} else if (method.equals(METHOD_PIN)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
+ Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can pin/unpin slices");
}
handlePinSlice(uri);
} else if (method.equals(METHOD_UNPIN)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
+ Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can pin/unpin slices");
}
handleUnpinSlice(uri);
} else if (method.equals(METHOD_GET_DESCENDANTS)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
Bundle b = new Bundle();
b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
new ArrayList<>(handleGetDescendants(uri)));
@@ -370,14 +401,27 @@ public abstract class SliceProvider extends ContentProvider {
}
}
- private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+ private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs,
+ String callingPkg) {
+ // This can be removed once Slice#bindSlice is removed and everyone is using
+ // SliceManager#bindSlice.
+ String pkg = callingPkg != null ? callingPkg
+ : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+ try {
+ mSliceManager.enforceSlicePermission(sliceUri, pkg,
+ Binder.getCallingPid(), Binder.getCallingUid());
+ } catch (SecurityException e) {
+ return createPermissionSlice(getContext(), sliceUri, pkg);
+ }
+ }
if (Looper.myLooper() == Looper.getMainLooper()) {
- return onBindSliceStrict(sliceUri, supportedSpecs);
+ return onBindSliceStrict(sliceUri, supportedSpecs, pkg);
} else {
CountDownLatch latch = new CountDownLatch(1);
Slice[] output = new Slice[1];
Handler.getMain().post(() -> {
- output[0] = onBindSliceStrict(sliceUri, supportedSpecs);
+ output[0] = onBindSliceStrict(sliceUri, supportedSpecs, pkg);
latch.countDown();
});
try {
@@ -389,15 +433,66 @@ public abstract class SliceProvider extends ContentProvider {
}
}
- private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+ /**
+ * @hide
+ */
+ public static Slice createPermissionSlice(Context context, Uri sliceUri,
+ String callingPackage) {
+ return new Slice.Builder(sliceUri)
+ .addAction(createPermissionIntent(context, sliceUri, callingPackage),
+ new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+ .addText(getPermissionString(context, callingPackage), null)
+ .build())
+ .addHints(Slice.HINT_LIST_ITEM)
+ .build();
+ }
+
+ /**
+ * @hide
+ */
+ public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
+ String callingPackage) {
+ Intent intent = new Intent(SliceManager.ACTION_REQUEST_SLICE_PERMISSION);
+ intent.setComponent(new ComponentName("com.android.systemui",
+ "com.android.systemui.SlicePermissionActivity"));
+ intent.putExtra(EXTRA_BIND_URI, sliceUri);
+ intent.putExtra(EXTRA_PKG, callingPackage);
+ intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
+ // Unique pending intent.
+ intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
+ .build());
+
+ return PendingIntent.getActivity(context, 0, intent, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public static CharSequence getPermissionString(Context context, String callingPackage) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ return context.getString(
+ com.android.internal.R.string.slices_permission_request,
+ pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
+ context.getApplicationInfo().loadLabel(pm));
+ } catch (NameNotFoundException e) {
+ // This shouldn't be possible since the caller is verified.
+ throw new RuntimeException("Unknown calling app", e);
+ }
+ }
+
+ private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs,
+ String callingPackage) {
ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDeath()
.build());
+ mBindingPkg = callingPackage;
return onBindSlice(sliceUri, supportedSpecs);
} finally {
+ mBindingPkg = null;
StrictMode.setThreadPolicy(oldPolicy);
}
}
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 2e44a630b056..da36157d85f8 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -227,6 +227,30 @@ public final class NetworkStats implements AutoCloseable {
*/
public static final int ROAMING_YES = 0x2;
+ /** @hide */
+ @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+ DEFAULT_NETWORK_ALL,
+ DEFAULT_NETWORK_NO,
+ DEFAULT_NETWORK_YES
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DefaultNetwork {}
+
+ /**
+ * Combined usage for this network regardless of whether it was the active default network.
+ */
+ public static final int DEFAULT_NETWORK_ALL = -1;
+
+ /**
+ * Usage that occurs while this network is not the active default network.
+ */
+ public static final int DEFAULT_NETWORK_NO = 0x1;
+
+ /**
+ * Usage that occurs while this network is the active default network.
+ */
+ public static final int DEFAULT_NETWORK_YES = 0x2;
+
/**
* Special TAG value for total data across all tags
*/
@@ -235,6 +259,7 @@ public final class NetworkStats implements AutoCloseable {
private int mUid;
private int mTag;
private int mState;
+ private int mDefaultNetwork;
private int mMetered;
private int mRoaming;
private long mBeginTimeStamp;
@@ -286,6 +311,15 @@ public final class NetworkStats implements AutoCloseable {
return 0;
}
+ private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
+ switch (defaultNetwork) {
+ case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
+ case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
+ case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
+ }
+ return 0;
+ }
+
public Bucket() {
}
@@ -351,6 +385,21 @@ public final class NetworkStats implements AutoCloseable {
}
/**
+ * Default network state. One of the following values:<p/>
+ * <ul>
+ * <li>{@link #DEFAULT_NETWORK_ALL}</li>
+ * <li>{@link #DEFAULT_NETWORK_NO}</li>
+ * <li>{@link #DEFAULT_NETWORK_YES}</li>
+ * </ul>
+ * <p>Indicates whether the network usage occurred on the system default network for this
+ * type of traffic, or whether the application chose to send this traffic on a network that
+ * was not the one selected by the system.
+ */
+ public @DefaultNetwork int getDefaultNetwork() {
+ return mDefaultNetwork;
+ }
+
+ /**
* Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Start of interval.
@@ -551,6 +600,8 @@ public final class NetworkStats implements AutoCloseable {
bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+ bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+ mRecycledSummaryEntry.defaultNetwork);
bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
bucketOut.mBeginTimeStamp = mStartTimeStamp;
@@ -600,6 +651,7 @@ public final class NetworkStats implements AutoCloseable {
bucketOut.mUid = Bucket.convertUid(getUid());
bucketOut.mTag = Bucket.convertTag(mTag);
bucketOut.mState = Bucket.STATE_ALL;
+ bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
bucketOut.mMetered = Bucket.METERED_ALL;
bucketOut.mRoaming = Bucket.ROAMING_ALL;
bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 853b00331a4d..90b514e272f4 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -60,10 +60,11 @@ import android.util.Log;
* {@link #queryDetailsForUid} <p />
* {@link #queryDetails} <p />
* These queries do not aggregate over time but do aggregate over state, metered and roaming.
- * Therefore there can be multiple buckets for a particular key but all Bucket's state is going to
- * be {@link NetworkStats.Bucket#STATE_ALL}, all Bucket's metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and all Bucket's roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * Therefore there can be multiple buckets for a particular key. However, all Buckets will have
+ * {@code state} {@link NetworkStats.Bucket#STATE_ALL},
+ * {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * {@code metered } {@link NetworkStats.Bucket#METERED_ALL},
+ * {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p />
* <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
* calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
@@ -136,7 +137,9 @@ public class NetworkStatsManager {
* roaming. This means the bucket's start and end timestamp are going to be the same as the
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
- * tag {@link NetworkStats.Bucket#TAG_NONE}, metered {@link NetworkStats.Bucket#METERED_ALL},
+ * tag {@link NetworkStats.Bucket#TAG_NONE},
+ * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered {@link NetworkStats.Bucket#METERED_ALL},
* and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
@@ -209,10 +212,10 @@ public class NetworkStatsManager {
/**
* Query network usage statistics summaries. Result filtered to include only uids belonging to
* calling user. Result is aggregated over time, hence all buckets will have the same start and
- * end timestamps. Not aggregated over state, uid, metered, or roaming. This means buckets'
- * start and end timestamps are going to be the same as the 'startTime' and 'endTime'
- * parameters. State, uid, metered, and roaming are going to vary, and tag is going to be the
- * same.
+ * end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This
+ * means buckets' start and end timestamps are going to be the same as the 'startTime' and
+ * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
+ * be the same.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,9 +261,10 @@ public class NetworkStatsManager {
* belonging to calling user. Result is aggregated over state but not aggregated over time.
* This means buckets' start and end timestamps are going to be between 'startTime' and
* 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the
- * same as the 'uid' parameter and tag the same as 'tag' parameter. metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * same as the 'uid' parameter and tag the same as 'tag' parameter.
+ * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+ * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
@@ -301,9 +305,10 @@ public class NetworkStatsManager {
* metered, nor roaming. This means buckets' start and end timestamps are going to be between
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
- * tag {@link NetworkStats.Bucket#TAG_NONE}, metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * tag {@link NetworkStats.Bucket#TAG_NONE},
+ * default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL},
+ * and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 5cd0981f72e3..bd978e3df863 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -176,6 +176,12 @@ public abstract class UsageStatsManagerInternal {
public abstract void setActiveAdminApps(Set<String> adminApps, int userId);
/**
+ * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and
+ * pushed to UsageStatsService.
+ */
+ public abstract void onAdminDataAvailable();
+
+ /**
* Return usage stats.
*
* @param obfuscateInstantApps whether instant app package names need to be obfuscated in the
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 265f7c7425db..f69aab01d821 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3024,7 +3024,8 @@ public abstract class Context {
//@hide: INCIDENT_SERVICE,
//@hide: STATS_COMPANION_SERVICE,
COMPANION_DEVICE_SERVICE,
- CROSS_PROFILE_APPS_SERVICE
+ CROSS_PROFILE_APPS_SERVICE,
+ //@hide: SYSTEM_UPDATE_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3242,6 +3243,17 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.os.SystemUpdateManager} for accessing the system update
+ * manager service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @SystemApi
+ public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.WindowManager} for accessing the system's window
* manager.
*
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 15e119b20a2e..84cbdb472c13 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -26,7 +26,6 @@ import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1593,11 +1592,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
}
- /** @hide */
- public boolean isTargetingDeprecatedSdkVersion() {
- return targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT;
- }
-
/**
* Returns whether or not this application was installed as a virtual preload.
*/
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 7866b52cc68c..9aa3f40a8f0a 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -25,11 +25,11 @@ import android.os.Parcelable;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
+import libcore.util.NativeAllocationRegistry;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import libcore.util.NativeAllocationRegistry;
-
/**
* HardwareBuffer wraps a native <code>AHardwareBuffer</code> object, which is a low-level object
* representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing
@@ -42,18 +42,25 @@ import libcore.util.NativeAllocationRegistry;
public final class HardwareBuffer implements Parcelable, AutoCloseable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "RGB", "BLOB" }, value = {
+ @IntDef(prefix = { "RGB", "BLOB", "D_", "DS_", "S_" }, value = {
RGBA_8888,
RGBA_FP16,
RGBA_1010102,
RGBX_8888,
RGB_888,
RGB_565,
- BLOB
+ BLOB,
+ D_16,
+ D_24,
+ DS_24UI8,
+ D_FP32,
+ DS_FP32UI8,
+ S_UI8,
})
public @interface Format {
}
+ @Format
/** Format: 8 bits each red, green, blue, alpha */
public static final int RGBA_8888 = 1;
/** Format: 8 bits each red, green, blue, alpha, alpha is always 0xFF */
@@ -68,6 +75,18 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
public static final int RGBA_1010102 = 0x2b;
/** Format: opaque format used for raw data transfer; must have a height of 1 */
public static final int BLOB = 0x21;
+ /** Format: 16 bits depth */
+ public static final int D_16 = 0x30;
+ /** Format: 24 bits depth */
+ public static final int D_24 = 0x31;
+ /** Format: 24 bits depth, 8 bits stencil */
+ public static final int DS_24UI8 = 0x32;
+ /** Format: 32 bits depth */
+ public static final int D_FP32 = 0x33;
+ /** Format: 32 bits depth, 8 bits stencil */
+ public static final int DS_FP32UI8 = 0x34;
+ /** Format: 8 bits stencil */
+ public static final int S_UI8 = 0x35;
// Note: do not rename, this field is used by native code
private long mNativeObject;
@@ -82,9 +101,11 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
@LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE,
- USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA})
+ USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP,
+ USAGE_GPU_MIPMAP_COMPLETE})
public @interface Usage {};
+ @Usage
/** Usage: The buffer will sometimes be read by the CPU */
public static final long USAGE_CPU_READ_RARELY = 2;
/** Usage: The buffer will often be read by the CPU */
@@ -107,6 +128,10 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
public static final long USAGE_SENSOR_DIRECT_DATA = 1 << 23;
/** Usage: The buffer will be used as a shader storage or uniform buffer object */
public static final long USAGE_GPU_DATA_BUFFER = 1 << 24;
+ /** Usage: The buffer will be used as a cube map texture */
+ public static final long USAGE_GPU_CUBE_MAP = 1 << 25;
+ /** Usage: The buffer contains a complete mipmap hierarchy */
+ public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26;
// The approximate size of a native AHardwareBuffer object.
private static final long NATIVE_HARDWARE_BUFFER_SIZE = 232;
@@ -118,15 +143,9 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
*
* @param width The width in pixels of the buffer
* @param height The height in pixels of the buffer
- * @param format The format of each pixel, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
- * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB}
+ * @param format The @Format of each pixel
* @param layers The number of layers in the buffer
- * @param usage Flags describing how the buffer will be used, one of
- * {@link #USAGE_CPU_READ_RARELY}, {@link #USAGE_CPU_READ_OFTEN},
- * {@link #USAGE_CPU_WRITE_RARELY}, {@link #USAGE_CPU_WRITE_OFTEN},
- * {@link #USAGE_GPU_SAMPLED_IMAGE}, {@link #USAGE_GPU_COLOR_OUTPUT},
- * {@link #USAGE_GPU_DATA_BUFFER}, {@link #USAGE_PROTECTED_CONTENT},
- * {@link #USAGE_SENSOR_DIRECT_DATA}, {@link #USAGE_VIDEO_ENCODE}
+ * @param usage The @Usage flags describing how the buffer will be used
* @return A <code>HardwareBuffer</code> instance if successful, or throws an
* IllegalArgumentException if the dimensions passed are invalid (either zero, negative, or
* too large to allocate), if the format is not supported, if the requested number of layers
@@ -154,7 +173,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
if (nativeObject == 0) {
throw new IllegalArgumentException("Unable to create a HardwareBuffer, either the " +
"dimensions passed were too large, too many image layers were requested, " +
- "or an invalid set of usage flags was passed");
+ "or an invalid set of usage flags or invalid format was passed");
}
return new HardwareBuffer(nativeObject);
}
@@ -206,8 +225,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
}
/**
- * Returns the format of this buffer, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
- * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB}.
+ * Returns the @Format of this buffer.
*/
@Format
public int getFormat() {
@@ -338,6 +356,12 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
case RGB_565:
case RGB_888:
case BLOB:
+ case D_16:
+ case D_24:
+ case DS_24UI8:
+ case D_FP32:
+ case DS_FP32UI8:
+ case S_UI8:
return true;
}
return false;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1201ef48220a..96d043c2a894 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -22,9 +22,11 @@ import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.TypeReference;
import android.util.Rational;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -171,6 +173,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
private List<CameraCharacteristics.Key<?>> mKeys;
private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
private List<CaptureRequest.Key<?>> mAvailableSessionKeys;
+ private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys;
private List<CaptureResult.Key<?>> mAvailableResultKeys;
/**
@@ -314,6 +317,45 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
}
/**
+ * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can
+ * be overriden for physical devices backing a logical multi-camera.</p>
+ *
+ * <p>This is a subset of android.request.availableRequestKeys which contains a list
+ * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+ * The respective value of such request key can be obtained by calling
+ * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
+ * individual physical device requests must be built via
+ * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
+ * Such extended capture requests can be passed only to
+ * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
+ * not to {@link CameraCaptureSession#setRepeatingRequest } or
+ * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+ *
+ * <p>The list returned is not modifiable, so any attempts to modify it will throw
+ * a {@code UnsupportedOperationException}.</p>
+ *
+ * <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
+ *
+ * @return List of keys that can be overriden in individual physical device requests.
+ * In case the camera device doesn't support such keys the list can be null.
+ */
+ @SuppressWarnings({"unchecked"})
+ public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() {
+ if (mAvailableSessionKeys == null) {
+ Object crKey = CaptureRequest.Key.class;
+ Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+ int[] filterTags = get(REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS);
+ if (filterTags == null) {
+ return null;
+ }
+ mAvailablePhysicalRequestKeys =
+ getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
+ }
+ return mAvailablePhysicalRequestKeys;
+ }
+
+ /**
* Returns the list of keys supported by this {@link CameraDevice} for querying
* with a {@link CaptureRequest}.
*
@@ -407,6 +449,47 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
return Collections.unmodifiableList(staticKeyList);
}
+ /**
+ * Returns the list of physical camera ids that this logical {@link CameraDevice} is
+ * made up of.
+ *
+ * <p>A camera device is a logical camera if it has
+ * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device
+ * doesn't have the capability, the return value will be an empty list. </p>
+ *
+ * <p>The list returned is not modifiable, so any attempts to modify it will throw
+ * a {@code UnsupportedOperationException}.</p>
+ *
+ * <p>Each physical camera id is only listed once in the list. The order of the keys
+ * is undefined.</p>
+ *
+ * @return List of physical camera ids for this logical camera device.
+ */
+ @NonNull
+ public List<String> getPhysicalCameraIds() {
+ int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
+ if (availableCapabilities == null) {
+ throw new AssertionError("android.request.availableCapabilities must be non-null "
+ + "in the characteristics");
+ }
+
+ if (!ArrayUtils.contains(availableCapabilities,
+ REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
+ return Collections.emptyList();
+ }
+ byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+
+ String physicalCamIdString = null;
+ try {
+ physicalCamIdString = new String(physicalCamIds, "UTF-8");
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
+ }
+ String[] physicalCameraIdList = physicalCamIdString.split("\0");
+
+ return Collections.unmodifiableList(Arrays.asList(physicalCameraIdList));
+ }
+
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* The key entries below this point are generated from metadata
* definitions in /system/media/camera/docs. Do not modify by hand or
@@ -1579,6 +1662,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -1594,6 +1678,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
* @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
* @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
*/
@PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1701,6 +1786,30 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<int[]>("android.request.availableSessionKeys", int[].class);
/**
+ * <p>A subset of the available request keys that can be overriden for
+ * physical devices backing a logical multi-camera.</p>
+ * <p>This is a subset of android.request.availableRequestKeys which contains a list
+ * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+ * The respective value of such request key can be obtained by calling
+ * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
+ * individual physical device requests must be built via
+ * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
+ * Such extended capture requests can be passed only to
+ * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
+ * not to {@link CameraCaptureSession#setRepeatingRequest } or
+ * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ public static final Key<int[]> REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS =
+ new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class);
+
+ /**
* <p>The list of image formats that are supported by this
* camera device for output streams.</p>
* <p>All camera devices will support JPEG and YUV_420_888 formats.</p>
@@ -2870,6 +2979,21 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<int[]>("android.statistics.info.availableLensShadingMapModes", int[].class);
/**
+ * <p>List of OIS data output modes for {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode} that
+ * are supported by this camera device.</p>
+ * <p>If no OIS data output is available for this camera device, this key will
+ * contain only OFF.</p>
+ * <p><b>Range of valid values:</b><br>
+ * Any value listed in {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode}</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+ */
+ @PublicKey
+ public static final Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES =
+ new Key<int[]>("android.statistics.info.availableOisDataModes", int[].class);
+
+ /**
* <p>Maximum number of supported points in the
* tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
* <p>If the actual number of points provided by the application (in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}*) is
@@ -2978,6 +3102,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li>
* <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li>
* <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_3 3}</li>
+ * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -2991,6 +3116,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_3
+ * @see #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
*/
@PublicKey
public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
@@ -3167,6 +3293,54 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
public static final Key<Boolean> DEPTH_DEPTH_IS_EXCLUSIVE =
new Key<Boolean>("android.depth.depthIsExclusive", boolean.class);
+ /**
+ * <p>String containing the ids of the underlying physical cameras.</p>
+ * <p>For a logical camera, this is concatenation of all underlying physical camera ids.
+ * The null terminator for physical camera id must be preserved so that the whole string
+ * can be tokenized using '\0' to generate list of physical camera ids.</p>
+ * <p>For example, if the physical camera ids of the logical camera are "2" and "3", the
+ * value of this tag will be ['2', '\0', '3', '\0'].</p>
+ * <p>The number of physical camera ids must be no less than 2.</p>
+ * <p><b>Units</b>: UTF-8 null-terminated string</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ public static final Key<byte[]> LOGICAL_MULTI_CAMERA_PHYSICAL_IDS =
+ new Key<byte[]>("android.logicalMultiCamera.physicalIds", byte[].class);
+
+ /**
+ * <p>The accuracy of frame timestamp synchronization between physical cameras</p>
+ * <p>The accuracy of the frame timestamp synchronization determines the physical cameras'
+ * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED,
+ * the physical camera sensors usually run in master-slave mode so that their shutter
+ * time is synchronized. For APPROXIMATE sensorSyncType, the camera sensors usually run in
+ * master-master mode, and there could be offset between their start of exposure.</p>
+ * <p>In both cases, all images generated for a particular capture request still carry the same
+ * timestamps, so that they can be used to look up the matching frame number and
+ * onCaptureStarted callback.</p>
+ * <p><b>Possible values:</b>
+ * <ul>
+ * <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE APPROXIMATE}</li>
+ * <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED CALIBRATED}</li>
+ * </ul></p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE
+ * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED
+ */
+ @PublicKey
+ public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
+ new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 639795ab8080..40ee8348ac4c 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -31,6 +31,7 @@ import android.os.Handler;
import android.view.Surface;
import java.util.List;
+import java.util.Set;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -910,6 +911,47 @@ public abstract class CameraDevice implements AutoCloseable {
throws CameraAccessException;
/**
+ * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
+ * initialized with template for a target use case. This methods allows
+ * clients to pass physical camera ids which can be used to customize the
+ * request for a specific physical camera. The settings are chosen
+ * to be the best options for the specific logical camera device. If
+ * additional physical camera ids are passed, then they will also use the
+ * same settings template. Requests containing individual physical camera
+ * settings can be passed only to {@link CameraCaptureSession#capture} or
+ * {@link CameraCaptureSession#captureBurst} and not to
+ * {@link CameraCaptureSession#setRepeatingRequest} or
+ * {@link CameraCaptureSession#setRepeatingBurst}</p>
+ *
+ * @param templateType An enumeration selecting the use case for this request. Not all template
+ * types are supported on every device. See the documentation for each template type for
+ * details.
+ * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+ * the request for a specific physical camera.
+ * @return a builder for a capture request, initialized with default
+ * settings for that template, and no output streams
+ *
+ * @throws IllegalArgumentException if the templateType is not supported by
+ * this device, or one of the physical id arguments matches with logical camera id.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #TEMPLATE_PREVIEW
+ * @see #TEMPLATE_RECORD
+ * @see #TEMPLATE_STILL_CAPTURE
+ * @see #TEMPLATE_VIDEO_SNAPSHOT
+ * @see #TEMPLATE_MANUAL
+ * @see CaptureRequest.Builder#setKey
+ * @see CaptureRequest.Builder#getKey
+ */
+ @NonNull
+ public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
+ Set<String> physicalCameraIdSet) throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* <p>Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a
* {@link TotalCaptureResult}.
*
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 90bf896c2225..a2bc91e0cda6 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -996,7 +996,12 @@ public final class CameraManager {
return;
}
- Integer oldStatus = mDeviceStatus.put(id, status);
+ Integer oldStatus;
+ if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
+ oldStatus = mDeviceStatus.remove(id);
+ } else {
+ oldStatus = mDeviceStatus.put(id, status);
+ }
if (oldStatus != null && oldStatus == status) {
if (DEBUG) {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 2294ec56525f..e7c8961186ec 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -845,6 +845,53 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
+ /**
+ * <p>The camera device is a logical camera backed by two or more physical cameras that are
+ * also exposed to the application.</p>
+ * <p>This capability requires the camera device to support the following:</p>
+ * <ul>
+ * <li>This camera device must list the following static metadata entries in {@link android.hardware.camera2.CameraCharacteristics }:<ul>
+ * <li>android.logicalMultiCamera.physicalIds</li>
+ * <li>{@link CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE android.logicalMultiCamera.sensorSyncType}</li>
+ * </ul>
+ * </li>
+ * <li>The underlying physical cameras' static metadata must list the following entries,
+ * so that the application can correlate pixels from the physical streams:<ul>
+ * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+ * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
+ * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+ * </ul>
+ * </li>
+ * <li>The logical camera device must be LIMITED or higher device.</li>
+ * </ul>
+ * <p>Both the logical camera device and its underlying physical devices support the
+ * mandatory stream combinations required for their device levels.</p>
+ * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
+ * <ul>
+ * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
+ * or raw stream with two physical streams of the same size and format, each from a
+ * separate physical camera, given that the size and format are supported by both
+ * physical cameras.</li>
+ * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
+ * advertise RAW capability, but the underlying physical cameras do. This is usually
+ * the case when the physical cameras have different sensor sizes.</li>
+ * </ul>
+ * <p>Using physical streams in place of a logical stream of the same size and format will
+ * not slow down the frame rate of the capture, as long as the minimum frame duration
+ * of the physical and logical streams are the same.</p>
+ *
+ * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+ * @see CameraCharacteristics#LENS_POSE_REFERENCE
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+ * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
@@ -1134,6 +1181,38 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3;
+ /**
+ * <p>This camera device is backed by an external camera connected to this Android device.</p>
+ * <p>The device has capability identical to a LIMITED level device, with the following
+ * exceptions:</p>
+ * <ul>
+ * <li>The device may not report lens/sensor related information such as<ul>
+ * <li>{@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}</li>
+ * <li>{@link CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE android.lens.info.hyperfocalDistance}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern}</li>
+ * <li>{@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}</li>
+ * <li>{@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew}</li>
+ * </ul>
+ * </li>
+ * <li>The device will report 0 for {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}</li>
+ * <li>The device has less guarantee on stable framerate, as the framerate partly depends
+ * on the external camera being used.</li>
+ * </ul>
+ *
+ * @see CaptureRequest#LENS_FOCAL_LENGTH
+ * @see CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE
+ * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
+ * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+ * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
+ * @see CameraCharacteristics#SENSOR_ORIENTATION
+ * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ */
+ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4;
+
//
// Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
//
@@ -1160,6 +1239,26 @@ public abstract class CameraMetadata<TKey> {
public static final int SYNC_MAX_LATENCY_UNKNOWN = -1;
//
+ // Enumeration values for CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+ //
+
+ /**
+ * <p>A software mechanism is used to synchronize between the physical cameras. As a result,
+ * the timestamp of an image from a physical stream is only an approximation of the
+ * image sensor start-of-exposure time.</p>
+ * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+ */
+ public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0;
+
+ /**
+ * <p>The camera device supports frame timestamp synchronization at the hardware level,
+ * and the timestamp of a physical stream image accurately reflects its
+ * start-of-exposure time.</p>
+ * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+ */
+ public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1;
+
+ //
// Enumeration values for CaptureRequest#COLOR_CORRECTION_MODE
//
@@ -2566,6 +2665,22 @@ public abstract class CameraMetadata<TKey> {
public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1;
//
+ // Enumeration values for CaptureRequest#STATISTICS_OIS_DATA_MODE
+ //
+
+ /**
+ * <p>Do not include OIS data in the capture result.</p>
+ * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+ */
+ public static final int STATISTICS_OIS_DATA_MODE_OFF = 0;
+
+ /**
+ * <p>Include OIS data in the capture result.</p>
+ * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+ */
+ public static final int STATISTICS_OIS_DATA_MODE_ON = 1;
+
+ //
// Enumeration values for CaptureRequest#TONEMAP_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index ce75fa52eff7..481b7649610a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -33,9 +33,11 @@ import android.view.Surface;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
-
+import java.util.Set;
/**
* <p>An immutable package of settings and outputs needed to capture a single
@@ -219,7 +221,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
private static final ArraySet<Surface> mEmptySurfaceSet = new ArraySet<Surface>();
- private final CameraMetadataNative mSettings;
+ private String mLogicalCameraId;
+ private CameraMetadataNative mLogicalCameraSettings;
+ private final HashMap<String, CameraMetadataNative> mPhysicalCameraSettings =
+ new HashMap<String, CameraMetadataNative>();
+
private boolean mIsReprocess;
// If this request is part of constrained high speed request list that was created by
// {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
@@ -236,8 +242,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* Used by Binder to unparcel this object only.
*/
private CaptureRequest() {
- mSettings = new CameraMetadataNative();
- setNativeInstance(mSettings);
mIsReprocess = false;
mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
@@ -249,8 +253,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*/
@SuppressWarnings("unchecked")
private CaptureRequest(CaptureRequest source) {
- mSettings = new CameraMetadataNative(source.mSettings);
- setNativeInstance(mSettings);
+ mLogicalCameraId = new String(source.mLogicalCameraId);
+ for (Map.Entry<String, CameraMetadataNative> entry :
+ source.mPhysicalCameraSettings.entrySet()) {
+ mPhysicalCameraSettings.put(new String(entry.getKey()),
+ new CameraMetadataNative(entry.getValue()));
+ }
+ mLogicalCameraSettings = mPhysicalCameraSettings.get(mLogicalCameraId);
+ setNativeInstance(mLogicalCameraSettings);
mSurfaceSet.addAll(source.mSurfaceSet);
mIsReprocess = source.mIsReprocess;
mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
@@ -272,16 +282,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* reprocess capture request to the same session where
* the {@link TotalCaptureResult}, used to create the reprocess
* capture, came from.
+ * @param logicalCameraId Camera Id of the actively open camera that instantiates the
+ * Builder.
+ *
+ * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+ * the request for a specific physical camera.
*
* @throws IllegalArgumentException If creating a reprocess capture request with an invalid
- * reprocessableSessionId.
+ * reprocessableSessionId, or multiple physical cameras.
*
* @see CameraDevice#createReprocessCaptureRequest
*/
private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
- int reprocessableSessionId) {
- mSettings = CameraMetadataNative.move(settings);
- setNativeInstance(mSettings);
+ int reprocessableSessionId, String logicalCameraId, Set<String> physicalCameraIdSet) {
+ if ((physicalCameraIdSet != null) && isReprocess) {
+ throw new IllegalArgumentException("Create a reprocess capture request with " +
+ "with more than one physical camera is not supported!");
+ }
+
+ mLogicalCameraId = logicalCameraId;
+ mLogicalCameraSettings = CameraMetadataNative.move(settings);
+ mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
+ if (physicalCameraIdSet != null) {
+ for (String physicalId : physicalCameraIdSet) {
+ mPhysicalCameraSettings.put(physicalId, new CameraMetadataNative(
+ mLogicalCameraSettings));
+ }
+ }
+
+ setNativeInstance(mLogicalCameraSettings);
mIsReprocess = isReprocess;
if (isReprocess) {
if (reprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) {
@@ -309,7 +338,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*/
@Nullable
public <T> T get(Key<T> key) {
- return mSettings.get(key);
+ return mLogicalCameraSettings.get(key);
}
/**
@@ -319,7 +348,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
@SuppressWarnings("unchecked")
@Override
protected <T> T getProtected(Key<?> key) {
- return (T) mSettings.get(key);
+ return (T) mLogicalCameraSettings.get(key);
}
/**
@@ -403,7 +432,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @hide
*/
public CameraMetadataNative getNativeCopy() {
- return new CameraMetadataNative(mSettings);
+ return new CameraMetadataNative(mLogicalCameraSettings);
}
/**
@@ -444,14 +473,16 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
return other != null
&& Objects.equals(mUserTag, other.mUserTag)
&& mSurfaceSet.equals(other.mSurfaceSet)
- && mSettings.equals(other.mSettings)
+ && mPhysicalCameraSettings.equals(other.mPhysicalCameraSettings)
+ && mLogicalCameraId.equals(other.mLogicalCameraId)
+ && mLogicalCameraSettings.equals(other.mLogicalCameraSettings)
&& mIsReprocess == other.mIsReprocess
&& mReprocessableSessionId == other.mReprocessableSessionId;
}
@Override
public int hashCode() {
- return HashCodeHelpers.hashCodeGeneric(mSettings, mSurfaceSet, mUserTag);
+ return HashCodeHelpers.hashCodeGeneric(mPhysicalCameraSettings, mSurfaceSet, mUserTag);
}
public static final Parcelable.Creator<CaptureRequest> CREATOR =
@@ -479,8 +510,25 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @hide
*/
private void readFromParcel(Parcel in) {
- mSettings.readFromParcel(in);
- setNativeInstance(mSettings);
+ int physicalCameraCount = in.readInt();
+ if (physicalCameraCount <= 0) {
+ throw new RuntimeException("Physical camera count" + physicalCameraCount +
+ " should always be positive");
+ }
+
+ //Always start with the logical camera id
+ mLogicalCameraId = in.readString();
+ mLogicalCameraSettings = new CameraMetadataNative();
+ mLogicalCameraSettings.readFromParcel(in);
+ setNativeInstance(mLogicalCameraSettings);
+ mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
+ for (int i = 1; i < physicalCameraCount; i++) {
+ String physicalId = in.readString();
+ CameraMetadataNative physicalCameraSettings = new CameraMetadataNative();
+ physicalCameraSettings.readFromParcel(in);
+ mPhysicalCameraSettings.put(physicalId, physicalCameraSettings);
+ }
+
mIsReprocess = (in.readInt() == 0) ? false : true;
mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
@@ -509,7 +557,19 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
@Override
public void writeToParcel(Parcel dest, int flags) {
- mSettings.writeToParcel(dest, flags);
+ int physicalCameraCount = mPhysicalCameraSettings.size();
+ dest.writeInt(physicalCameraCount);
+ //Logical camera id and settings always come first.
+ dest.writeString(mLogicalCameraId);
+ mLogicalCameraSettings.writeToParcel(dest, flags);
+ for (Map.Entry<String, CameraMetadataNative> entry : mPhysicalCameraSettings.entrySet()) {
+ if (entry.getKey().equals(mLogicalCameraId)) {
+ continue;
+ }
+ dest.writeString(entry.getKey());
+ entry.getValue().writeToParcel(dest, flags);
+ }
+
dest.writeInt(mIsReprocess ? 1 : 0);
synchronized (mSurfacesLock) {
@@ -542,6 +602,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
}
/**
+ * Retrieves the logical camera id.
+ * @hide
+ */
+ public String getLogicalCameraId() {
+ return mLogicalCameraId;
+ }
+
+ /**
* @hide
*/
public void convertSurfaceToStreamId(
@@ -633,14 +701,20 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* submits a reprocess capture request to the same session
* where the {@link TotalCaptureResult}, used to create the
* reprocess capture, came from.
+ * @param logicalCameraId Camera Id of the actively open camera that instantiates the
+ * Builder.
+ * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+ * the request for a specific physical camera.
*
* @throws IllegalArgumentException If creating a reprocess capture request with an invalid
* reprocessableSessionId.
* @hide
*/
public Builder(CameraMetadataNative template, boolean reprocess,
- int reprocessableSessionId) {
- mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId);
+ int reprocessableSessionId, String logicalCameraId,
+ Set<String> physicalCameraIdSet) {
+ mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId,
+ logicalCameraId, physicalCameraIdSet);
}
/**
@@ -682,7 +756,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* type to the key.
*/
public <T> void set(@NonNull Key<T> key, T value) {
- mRequest.mSettings.set(key, value);
+ mRequest.mLogicalCameraSettings.set(key, value);
}
/**
@@ -696,7 +770,71 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*/
@Nullable
public <T> T get(Key<T> key) {
- return mRequest.mSettings.get(key);
+ return mRequest.mLogicalCameraSettings.get(key);
+ }
+
+ /**
+ * Set a capture request field to a value. The field definitions can be
+ * found in {@link CaptureRequest}.
+ *
+ * <p>Setting a field to {@code null} will remove that field from the capture request.
+ * Unless the field is optional, removing it will likely produce an error from the camera
+ * device when the request is submitted.</p>
+ *
+ *<p>This method can be called for logical camera devices, which are devices that have
+ * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
+ * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+ * physical devices that are backing the logical camera. The camera Id included in the
+ * 'physicalCameraId' argument selects an individual physical device that will receive
+ * the customized capture request field.</p>
+ *
+ * @throws IllegalArgumentException if the physical camera id is not valid
+ *
+ * @param key The metadata field to write.
+ * @param value The value to set the field to, which must be of a matching
+ * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
+ * via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
+ * @return The builder object.
+ * type to the key.
+ */
+ public <T> Builder setPhysicalCameraKey(@NonNull Key<T> key, T value,
+ @NonNull String physicalCameraId) {
+ if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
+ throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
+ " is not valid!");
+ }
+
+ mRequest.mPhysicalCameraSettings.get(physicalCameraId).set(key, value);
+
+ return this;
+ }
+
+ /**
+ * Get a capture request field value for a specific physical camera Id. The field
+ * definitions can be found in {@link CaptureRequest}.
+ *
+ *<p>This method can be called for logical camera devices, which are devices that have
+ * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
+ * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+ * physical devices that are backing the logical camera. The camera Id included in the
+ * 'physicalCameraId' argument selects an individual physical device and returns
+ * its specific capture request field.</p>
+ *
+ * @throws IllegalArgumentException if the key or physical camera id were not valid
+ *
+ * @param key The metadata field to read.
+ * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
+ * via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
+ * @return The value of that key, or {@code null} if the field is not set.
+ */
+ @Nullable
+ public <T> T getPhysicalCameraKey(Key<T> key,@NonNull String physicalCameraId) {
+ if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
+ throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
+ " is not valid!");
+ }
+
+ return mRequest.mPhysicalCameraSettings.get(physicalCameraId).get(key);
}
/**
@@ -748,7 +886,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @hide
*/
public boolean isEmpty() {
- return mRequest.mSettings.isEmpty();
+ return mRequest.mLogicalCameraSettings.isEmpty();
}
}
@@ -2619,6 +2757,29 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
/**
+ * <p>Whether the camera device outputs the OIS data in output
+ * result metadata.</p>
+ * <p>When set to ON,
+ * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
+ * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+ * <p><b>Possible values:</b>
+ * <ul>
+ * <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
+ * <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
+ * </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * android.Statistics.info.availableOisDataModes</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ * @see #STATISTICS_OIS_DATA_MODE_OFF
+ * @see #STATISTICS_OIS_DATA_MODE_ON
+ */
+ @PublicKey
+ public static final Key<Integer> STATISTICS_OIS_DATA_MODE =
+ new Key<Integer>("android.statistics.oisDataMode", int.class);
+
+ /**
* <p>Tonemapping / contrast / gamma curve for the blue
* channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
* CONTRAST_CURVE.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index c6fc4b4ed56c..d730fa8a31cf 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3909,6 +3909,76 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
/**
+ * <p>Whether the camera device outputs the OIS data in output
+ * result metadata.</p>
+ * <p>When set to ON,
+ * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
+ * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+ * <p><b>Possible values:</b>
+ * <ul>
+ * <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
+ * <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
+ * </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * android.Statistics.info.availableOisDataModes</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ * @see #STATISTICS_OIS_DATA_MODE_OFF
+ * @see #STATISTICS_OIS_DATA_MODE_ON
+ */
+ @PublicKey
+ public static final Key<Integer> STATISTICS_OIS_DATA_MODE =
+ new Key<Integer>("android.statistics.oisDataMode", int.class);
+
+ /**
+ * <p>An array of timestamps of OIS samples, in nanoseconds.</p>
+ * <p>The array contains the timestamps of OIS samples. The timestamps are in the same
+ * timebase as and comparable to {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p>
+ * <p><b>Units</b>: nanoseconds</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureResult#SENSOR_TIMESTAMP
+ */
+ @PublicKey
+ public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS =
+ new Key<long[]>("android.statistics.oisTimestamps", long[].class);
+
+ /**
+ * <p>An array of shifts of OIS samples, in x direction.</p>
+ * <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
+ * A positive value is a shift from left to right in active array coordinate system. For
+ * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+ * (3, 0) puts the new optical center at (1003, 500).</p>
+ * <p>The number of shifts must match the number of timestamps in
+ * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+ * <p><b>Units</b>: Pixels in active array.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ */
+ @PublicKey
+ public static final Key<float[]> STATISTICS_OIS_X_SHIFTS =
+ new Key<float[]>("android.statistics.oisXShifts", float[].class);
+
+ /**
+ * <p>An array of shifts of OIS samples, in y direction.</p>
+ * <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
+ * A positive value is a shift from top to bottom in active array coordinate system. For
+ * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+ * (0, 5) puts the new optical center at (1000, 505).</p>
+ * <p>The number of shifts must match the number of timestamps in
+ * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+ * <p><b>Units</b>: Pixels in active array.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+ */
+ @PublicKey
+ public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS =
+ new Key<float[]>("android.statistics.oisYShifts", float[].class);
+
+ /**
* <p>Tonemapping / contrast / gamma curve for the blue
* channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
* CONTRAST_CURVE.</p>
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 8c4dbfa58d05..06c2c25ab6bb 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -94,8 +94,8 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
// Note that after this step, the requestMetadata is mutated (swapped) and can not be used
// for next request builder creation.
CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
- requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
-
+ requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+ request.getLogicalCameraId(), /*physicalCameraIdSet*/ null);
// Carry over userTag, as native metadata doesn't have this field.
singleTargetRequestBuilder.setTag(request.getTag());
@@ -120,7 +120,8 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
// CaptureRequest.Builder creation.
requestMetadata = new CameraMetadataNative(request.getNativeCopy());
doubleTargetRequestBuilder = new CaptureRequest.Builder(
- requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+ requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+ request.getLogicalCameraId(), /*physicalCameraIdSet*/null);
doubleTargetRequestBuilder.setTag(request.getTag());
doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index f1ffb890eecd..cab9d7046fcd 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -16,27 +16,25 @@
package android.hardware.camera2.impl;
-import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
-import android.graphics.ImageFormat;
+import android.hardware.ICameraService;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.ReprocessFormatsMap;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SubmitInfo;
import android.hardware.camera2.utils.SurfaceUtils;
-import android.hardware.ICameraService;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -51,16 +49,15 @@ import android.view.Surface;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -715,6 +712,38 @@ public class CameraDeviceImpl extends CameraDevice
}
@Override
+ public CaptureRequest.Builder createCaptureRequest(int templateType,
+ Set<String> physicalCameraIdSet)
+ throws CameraAccessException {
+ synchronized(mInterfaceLock) {
+ checkIfCameraClosedOrInError();
+
+ for (String physicalId : physicalCameraIdSet) {
+ if (physicalId == getId()) {
+ throw new IllegalStateException("Physical id matches the logical id!");
+ }
+ }
+
+ CameraMetadataNative templatedRequest = null;
+
+ templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
+
+ // If app target SDK is older than O, or it's not a still capture template, enableZsl
+ // must be false in the default request.
+ if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
+ templateType != TEMPLATE_STILL_CAPTURE) {
+ overrideEnableZsl(templatedRequest, false);
+ }
+
+ CaptureRequest.Builder builder = new CaptureRequest.Builder(
+ templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+ getId(), physicalCameraIdSet);
+
+ return builder;
+ }
+ }
+
+ @Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
synchronized(mInterfaceLock) {
@@ -732,7 +761,8 @@ public class CameraDeviceImpl extends CameraDevice
}
CaptureRequest.Builder builder = new CaptureRequest.Builder(
- templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+ templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+ getId(), /*physicalCameraIdSet*/ null);
return builder;
}
@@ -748,7 +778,7 @@ public class CameraDeviceImpl extends CameraDevice
CameraMetadataNative(inputResult.getNativeCopy());
return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
- inputResult.getSessionId());
+ inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null);
}
}
@@ -958,7 +988,8 @@ public class CameraDeviceImpl extends CameraDevice
// callback is valid
handler = checkHandler(handler, callback);
- // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+ // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
+ // the surface isn't a physical stream surface for reprocessing request
for (CaptureRequest request : requestList) {
if (request.getTargets().isEmpty()) {
throw new IllegalArgumentException(
@@ -969,7 +1000,20 @@ public class CameraDeviceImpl extends CameraDevice
if (surface == null) {
throw new IllegalArgumentException("Null Surface targets are not allowed");
}
+
+ if (!request.isReprocess()) {
+ continue;
+ }
+ for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+ OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
+ if (configuration.isForPhysicalCamera()
+ && configuration.getSurfaces().contains(surface)) {
+ throw new IllegalArgumentException(
+ "Reprocess request on physical stream is not allowed");
+ }
+ }
}
+
}
synchronized(mInterfaceLock) {
@@ -1798,34 +1842,36 @@ public class CameraDeviceImpl extends CameraDevice
case ERROR_CAMERA_DISCONNECTED:
CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
break;
- default:
- Log.e(TAG, "Unknown error from camera device: " + errorCode);
- // no break
- case ERROR_CAMERA_DEVICE:
- case ERROR_CAMERA_SERVICE:
- mInError = true;
- final int publicErrorCode = (errorCode == ERROR_CAMERA_DEVICE) ?
- StateCallback.ERROR_CAMERA_DEVICE :
- StateCallback.ERROR_CAMERA_SERVICE;
- Runnable r = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()) {
- mDeviceCallback.onError(CameraDeviceImpl.this, publicErrorCode);
- }
- }
- };
- CameraDeviceImpl.this.mDeviceHandler.post(r);
- break;
case ERROR_CAMERA_REQUEST:
case ERROR_CAMERA_RESULT:
case ERROR_CAMERA_BUFFER:
onCaptureErrorLocked(errorCode, resultExtras);
break;
+ case ERROR_CAMERA_DEVICE:
+ scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE);
+ break;
+ case ERROR_CAMERA_DISABLED:
+ scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED);
+ break;
+ default:
+ Log.e(TAG, "Unknown error from camera device: " + errorCode);
+ scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE);
}
}
}
+ private void scheduleNotifyError(int code) {
+ mInError = true;
+ CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
+ CameraDeviceCallbacks::notifyError, this, code));
+ }
+
+ private void notifyError(int code) {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ mDeviceCallback.onError(CameraDeviceImpl.this, code);
+ }
+ }
+
@Override
public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
if (DEBUG) {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a85b5f710696..f47cd665fd9c 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -31,13 +31,12 @@ import android.util.Log;
import android.util.Size;
import android.view.Surface;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Collections;
-import java.util.ArrayList;
-
import static com.android.internal.util.Preconditions.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* A class for describing camera output, which contains a {@link Surface} and its specific
* configuration for creating capture session.
@@ -266,6 +265,7 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredGenerationId = surface.getGenerationId();
mIsDeferredConfig = false;
mIsShared = false;
+ mPhysicalCameraId = null;
}
/**
@@ -319,6 +319,7 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredGenerationId = 0;
mIsDeferredConfig = true;
mIsShared = false;
+ mPhysicalCameraId = null;
}
/**
@@ -348,8 +349,9 @@ public final class OutputConfiguration implements Parcelable {
* </ol>
*
* <p>To enable surface sharing, this function must be called before {@link
- * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link
- * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
+ * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+ * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function after
+ * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
*
* <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
* The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
@@ -360,6 +362,44 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * Set the id of the physical camera for this OutputConfiguration
+ *
+ * <p>In the case one logical camera is made up of multiple physical cameras, it could be
+ * desirable for the camera application to request streams from individual physical cameras.
+ * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
+ *
+ * <p>The valid physical camera id can be queried by {@link
+ * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
+ * </p>
+ *
+ * <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
+ * stream.</p>
+ *
+ * <p>This function must be called before {@link
+ * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+ * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function
+ * after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+ * CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
+ *
+ * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input
+ * or output of a reprocessing request. </p>
+ */
+ public void setPhysicalCameraId(@Nullable String physicalCameraId) {
+ mPhysicalCameraId = physicalCameraId;
+ }
+
+ /**
+ * Check if this configuration is for a physical camera.
+ *
+ * <p>This returns true if the output configuration was for a physical camera making up a
+ * logical multi camera via {@link OutputConfiguration#setPhysicalCameraId}.</p>
+ * @hide
+ */
+ public boolean isForPhysicalCamera() {
+ return (mPhysicalCameraId != null);
+ }
+
+ /**
* Check if this configuration has deferred configuration.
*
* <p>This will return true if the output configuration was constructed with surface deferred by
@@ -487,6 +527,7 @@ public final class OutputConfiguration implements Parcelable {
this.mConfiguredGenerationId = other.mConfiguredGenerationId;
this.mIsDeferredConfig = other.mIsDeferredConfig;
this.mIsShared = other.mIsShared;
+ this.mPhysicalCameraId = other.mPhysicalCameraId;
}
/**
@@ -502,6 +543,7 @@ public final class OutputConfiguration implements Parcelable {
boolean isShared = source.readInt() == 1;
ArrayList<Surface> surfaces = new ArrayList<Surface>();
source.readTypedList(surfaces, Surface.CREATOR);
+ String physicalCameraId = source.readString();
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
@@ -524,6 +566,7 @@ public final class OutputConfiguration implements Parcelable {
StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
mConfiguredGenerationId = 0;
}
+ mPhysicalCameraId = physicalCameraId;
}
/**
@@ -622,6 +665,7 @@ public final class OutputConfiguration implements Parcelable {
dest.writeInt(mIsDeferredConfig ? 1 : 0);
dest.writeInt(mIsShared ? 1 : 0);
dest.writeTypedList(mSurfaces);
+ dest.writeString(mPhysicalCameraId);
}
/**
@@ -675,13 +719,15 @@ public final class OutputConfiguration implements Parcelable {
if (mIsDeferredConfig) {
return HashCodeHelpers.hashCode(
mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
- mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0);
+ mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
+ mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
}
return HashCodeHelpers.hashCode(
mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
mConfiguredSize.hashCode(), mConfiguredFormat,
- mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0);
+ mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
+ mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
}
private static final String TAG = "OutputConfiguration";
@@ -701,4 +747,6 @@ public final class OutputConfiguration implements Parcelable {
private final boolean mIsDeferredConfig;
// Flag indicating if this config has shared surfaces
private boolean mIsShared;
+ // The physical camera id that this output configuration is for.
+ private String mPhysicalCameraId;
}
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 3556751f4af4..0cf7605c98a2 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -59,24 +59,56 @@ import java.util.stream.Stream;
*/
@SystemApi
public final class ProgramSelector implements Parcelable {
+ /** Invalid program type.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_INVALID = 0;
- /** Analogue AM radio (with or without RDS). */
+ /** Analogue AM radio (with or without RDS).
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_AM = 1;
- /** analogue FM radio (with or without RDS). */
+ /** analogue FM radio (with or without RDS).
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_FM = 2;
- /** AM HD Radio. */
+ /** AM HD Radio.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_AM_HD = 3;
- /** FM HD Radio. */
+ /** FM HD Radio.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_FM_HD = 4;
- /** Digital audio broadcasting. */
+ /** Digital audio broadcasting.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_DAB = 5;
- /** Digital Radio Mondiale. */
+ /** Digital Radio Mondiale.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_DRMO = 6;
- /** SiriusXM Satellite Radio. */
+ /** SiriusXM Satellite Radio.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_SXM = 7;
- /** Vendor-specific, not synced across devices. */
+ /** Vendor-specific, not synced across devices.
+ * @deprecated use {@link ProgramIdentifier} instead
+ */
+ @Deprecated
public static final int PROGRAM_TYPE_VENDOR_START = 1000;
+ /** @deprecated use {@link ProgramIdentifier} instead */
+ @Deprecated
public static final int PROGRAM_TYPE_VENDOR_END = 1999;
+ /** @deprecated use {@link ProgramIdentifier} instead */
+ @Deprecated
@IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
PROGRAM_TYPE_INVALID,
PROGRAM_TYPE_AM,
@@ -112,18 +144,46 @@ public final class ProgramSelector implements Parcelable {
*
* The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
* as opposed to HD Radio standard (where it's 1-based).
+ *
+ * @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead
*/
+ @Deprecated
public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4;
/**
- * 24bit compound primary identifier for DAB.
+ * 64bit additional identifier for HD Radio.
+ *
+ * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
+ * globally unique. To provide a best-effort solution, a short version of
+ * station name may be carried as additional identifier and may be used
+ * by the tuner hardware to double-check tuning.
+ *
+ * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
+ * must be converted to uppercase). Encoded in little-endian ASCII:
+ * the first character of the name is the LSB.
+ *
+ * For example: "Abc" is encoded as 0x434241.
+ */
+ public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004;
+ /**
+ * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT}
+ */
+ public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
+ /**
+ * 28bit compound primary identifier for Digital Audio Broadcasting.
*
* Consists of (from the LSB):
* - 16bit: SId;
- * - 8bit: ECC code.
+ * - 8bit: ECC code;
+ * - 4bit: SCIdS.
+ *
+ * SCIdS (Service Component Identifier within the Service) value
+ * of 0 represents the main service, while 1 and above represents
+ * secondary services.
+ *
* The remaining bits should be set to zeros when writing on the chip side
* and ignored when read.
*/
- public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
+ public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC;
/** 16bit */
public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6;
/** 12bit */
@@ -134,7 +194,11 @@ public final class ProgramSelector implements Parcelable {
public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
/** kHz */
public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
- /** 1: AM, 2:FM */
+ /**
+ * 1: AM, 2:FM
+ * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead
+ */
+ @Deprecated
public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
/** 32bit */
public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
@@ -148,14 +212,29 @@ public final class ProgramSelector implements Parcelable {
* type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
* not be used in any program type other than 1015).
*/
- public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START;
- public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END;
+ public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START;
+ /**
+ * @see {@link IDENTIFIER_TYPE_VENDOR_START}
+ */
+ public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END;
+ /**
+ * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead
+ */
+ @Deprecated
+ public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START;
+ /**
+ * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead
+ */
+ @Deprecated
+ public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END;
@IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
IDENTIFIER_TYPE_INVALID,
IDENTIFIER_TYPE_AMFM_FREQUENCY,
IDENTIFIER_TYPE_RDS_PI,
IDENTIFIER_TYPE_HD_STATION_ID_EXT,
IDENTIFIER_TYPE_HD_SUBCHANNEL,
+ IDENTIFIER_TYPE_HD_STATION_NAME,
+ IDENTIFIER_TYPE_DAB_SID_EXT,
IDENTIFIER_TYPE_DAB_SIDECC,
IDENTIFIER_TYPE_DAB_ENSEMBLE,
IDENTIFIER_TYPE_DAB_SCID,
@@ -166,7 +245,7 @@ public final class ProgramSelector implements Parcelable {
IDENTIFIER_TYPE_SXM_SERVICE_ID,
IDENTIFIER_TYPE_SXM_CHANNEL,
})
- @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END)
+ @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END)
@Retention(RetentionPolicy.SOURCE)
public @interface IdentifierType {}
@@ -205,7 +284,9 @@ public final class ProgramSelector implements Parcelable {
* Type of a radio technology.
*
* @return program type.
+ * @deprecated use {@link getPrimaryId} instead
*/
+ @Deprecated
public @ProgramType int getProgramType() {
return mProgramType;
}
@@ -273,7 +354,10 @@ public final class ProgramSelector implements Parcelable {
* preserving elements order.
*
* @return an array of vendor identifiers, must not be modified.
+ * @deprecated for HAL 1.x compatibility;
+ * HAL 2.x uses standard primary/secondary lists for vendor IDs
*/
+ @Deprecated
public @NonNull long[] getVendorIds() {
return mVendorIds;
}
@@ -427,6 +511,10 @@ public final class ProgramSelector implements Parcelable {
private final long mValue;
public Identifier(@IdentifierType int type, long value) {
+ if (type == IDENTIFIER_TYPE_HD_STATION_NAME) {
+ // see getType
+ type = IDENTIFIER_TYPE_HD_SUBCHANNEL;
+ }
mType = type;
mValue = value;
}
@@ -437,6 +525,13 @@ public final class ProgramSelector implements Parcelable {
* @return type of an identifier.
*/
public @IdentifierType int getType() {
+ if (mType == IDENTIFIER_TYPE_HD_SUBCHANNEL && mValue > 10) {
+ /* HD_SUBCHANNEL and HD_STATION_NAME use the same identifier type, but they differ
+ * in possible values: sub channel is 0-7, station name is greater than ASCII space
+ * code (32).
+ */
+ return IDENTIFIER_TYPE_HD_STATION_NAME;
+ }
return mType;
}
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 790c80b1d934..eeb30e23d000 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -39,9 +39,9 @@ interface IIpSecService
void closeUdpEncapsulationSocket(int resourceId);
- IpSecTransformResponse createTransportModeTransform(in IpSecConfig c, in IBinder binder);
+ IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder);
- void deleteTransportModeTransform(int transformId);
+ void deleteTransform(int transformId);
void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 2cda58c99a61..f04f03f6b617 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
@@ -625,6 +626,133 @@ public final class IpSecManager {
}
/**
+ * This class represents an IpSecTunnelInterface
+ *
+ * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
+ * local endpoints for IPsec tunnels.
+ *
+ * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be
+ * applied to provide IPsec security to packets sent through the tunnel. While a tunnel
+ * cannot be used in standalone mode within Android, the higher layers may use the tunnel
+ * to create Network objects which are accessible to the Android system.
+ * @hide
+ */
+ @SystemApi
+ public static final class IpSecTunnelInterface implements AutoCloseable {
+ private final IIpSecService mService;
+ private final InetAddress mRemoteAddress;
+ private final InetAddress mLocalAddress;
+ private final Network mUnderlyingNetwork;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private String mInterfaceName;
+ private int mResourceId = INVALID_RESOURCE_ID;
+
+ /** Get the underlying SPI held by this object. */
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ /**
+ * Add an address to the IpSecTunnelInterface
+ *
+ * <p>Add an address which may be used as the local inner address for
+ * tunneled traffic.
+ *
+ * @param address the local address for traffic inside the tunnel
+ * @throws IOException if the address could not be added
+ * @hide
+ */
+ public void addAddress(LinkAddress address) throws IOException {
+ }
+
+ /**
+ * Remove an address from the IpSecTunnelInterface
+ *
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface
+ *
+ * @param address to be removed
+ * @throws IOException if the address could not be removed
+ * @hide
+ */
+ public void removeAddress(LinkAddress address) throws IOException {
+ }
+
+ private IpSecTunnelInterface(@NonNull IIpSecService service,
+ @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
+ @NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+ mService = service;
+ mLocalAddress = localAddress;
+ mRemoteAddress = remoteAddress;
+ mUnderlyingNetwork = underlyingNetwork;
+ // TODO: Call IpSecService
+ }
+
+ /**
+ * Delete an IpSecTunnelInterface
+ *
+ * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
+ * resources. Any packets bound for this interface either inbound or outbound will
+ * all be lost.
+ */
+ @Override
+ public void close() {
+ // try {
+ // TODO: Call IpSecService
+ mResourceId = INVALID_RESOURCE_ID;
+ // } catch (RemoteException e) {
+ // throw e.rethrowFromSystemServer();
+ // }
+ mCloseGuard.close();
+ }
+
+ /** Check that the Interface was closed properly. */
+ @Override
+ protected void finalize() throws Throwable {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+ }
+
+ /**
+ * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+ *
+ * @param localAddress The local addres of the tunnel
+ * @param remoteAddress The local addres of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+ * This network should almost certainly be a network such as WiFi with an L2 address.
+ * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the socket could not be opened or bound
+ * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
+ * @hide
+ */
+ @SystemApi
+ public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
+ @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+ return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+ }
+
+ /**
+ * Apply a transform to the IpSecTunnelInterface
+ *
+ * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+ * transform.
+ * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * the transform will be used.
+ * @param transform an {@link IpSecTransform} created in tunnel mode
+ * @throws IOException indicating that the transform could not be applied due to a lower
+ * layer failure.
+ * @hide
+ */
+ @SystemApi
+ void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
+ IpSecTransform transform) throws IOException {
+ // TODO: call IpSecService
+ }
+ /**
* Construct an instance of IpSecManager within an application context.
*
* @param context the application context for this manager
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 7b9b4830929d..37e2c4fbf04a 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -124,8 +124,7 @@ public final class IpSecTransform implements AutoCloseable {
synchronized (this) {
try {
IIpSecService svc = getIpSecService();
- IpSecTransformResponse result =
- svc.createTransportModeTransform(mConfig, new Binder());
+ IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
int status = result.status;
checkResultStatus(status);
mResourceId = result.resourceId;
@@ -170,7 +169,7 @@ public final class IpSecTransform implements AutoCloseable {
* still want to clear out the transform.
*/
IIpSecService svc = getIpSecService();
- svc.deleteTransportModeTransform(mResourceId);
+ svc.deleteTransform(mResourceId);
stopKeepalive();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -300,21 +299,6 @@ public final class IpSecTransform implements AutoCloseable {
}
/**
- * Set the {@link Network} which will carry tunneled traffic.
- *
- * <p>Restricts the transformed traffic to a particular {@link Network}. This is required
- * for tunnel mode, otherwise tunneled traffic would be sent on the default network.
- *
- * @hide
- */
- @SystemApi
- public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) {
- Preconditions.checkNotNull(net);
- mConfig.setNetwork(net);
- return this;
- }
-
- /**
* Add UDP encapsulation to an IPv4 transform.
*
* <p>This allows IPsec traffic to pass through a NAT.
@@ -415,6 +399,7 @@ public final class IpSecTransform implements AutoCloseable {
* @throws IOException indicating other errors
* @hide
*/
+ @SystemApi
public IpSecTransform buildTunnelModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index fd118f340119..ce2de8554dcc 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -130,6 +130,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
proto.write(NetworkIdentityProto.METERED, mMetered);
+ proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
proto.end(start);
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a85f80e38dfd..01b2b39213f9 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -160,13 +160,6 @@ public class NetworkStats implements Parcelable {
rxBytes, rxPackets, txBytes, txPackets, operations);
}
- // TODO: fix the the telephony code to pass DEFAULT_NETWORK_YES and remove this constructor.
- public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
- long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
- this(iface, uid, set, tag, metered, roaming, DEFAULT_NETWORK_YES, rxBytes, rxPackets,
- txBytes, txPackets, operations);
- }
-
public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
long operations) {
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index b307c5d6fc53..8efd39a7a785 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -24,6 +24,15 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
import static android.net.wifi.WifiInfo.removeDoubleQuotes;
import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
@@ -191,16 +200,30 @@ public class NetworkTemplate implements Parcelable {
private final String mNetworkId;
+ // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
+ private final int mMetered;
+ private final int mRoaming;
+ private final int mDefaultNetwork;
+
public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
}
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId) {
+ this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL);
+ }
+
+ public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
+ String networkId, int metered, int roaming, int defaultNetwork) {
mMatchRule = matchRule;
mSubscriberId = subscriberId;
mMatchSubscriberIds = matchSubscriberIds;
mNetworkId = networkId;
+ mMetered = metered;
+ mRoaming = roaming;
+ mDefaultNetwork = defaultNetwork;
if (!isKnownMatchRule(matchRule)) {
Log.e(TAG, "Unknown network template rule " + matchRule
@@ -213,6 +236,9 @@ public class NetworkTemplate implements Parcelable {
mSubscriberId = in.readString();
mMatchSubscriberIds = in.createStringArray();
mNetworkId = in.readString();
+ mMetered = in.readInt();
+ mRoaming = in.readInt();
+ mDefaultNetwork = in.readInt();
}
@Override
@@ -221,6 +247,9 @@ public class NetworkTemplate implements Parcelable {
dest.writeString(mSubscriberId);
dest.writeStringArray(mMatchSubscriberIds);
dest.writeString(mNetworkId);
+ dest.writeInt(mMetered);
+ dest.writeInt(mRoaming);
+ dest.writeInt(mDefaultNetwork);
}
@Override
@@ -243,12 +272,23 @@ public class NetworkTemplate implements Parcelable {
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
}
+ if (mMetered != METERED_ALL) {
+ builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
+ }
+ if (mRoaming != ROAMING_ALL) {
+ builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming));
+ }
+ if (mDefaultNetwork != DEFAULT_NETWORK_ALL) {
+ builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
+ mDefaultNetwork));
+ }
return builder.toString();
}
@Override
public int hashCode() {
- return Objects.hash(mMatchRule, mSubscriberId, mNetworkId);
+ return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
+ mDefaultNetwork);
}
@Override
@@ -257,7 +297,10 @@ public class NetworkTemplate implements Parcelable {
final NetworkTemplate other = (NetworkTemplate) obj;
return mMatchRule == other.mMatchRule
&& Objects.equals(mSubscriberId, other.mSubscriberId)
- && Objects.equals(mNetworkId, other.mNetworkId);
+ && Objects.equals(mNetworkId, other.mNetworkId)
+ && mMetered == other.mMetered
+ && mRoaming == other.mRoaming
+ && mDefaultNetwork == other.mDefaultNetwork;
}
return false;
}
@@ -300,6 +343,10 @@ public class NetworkTemplate implements Parcelable {
* Test if given {@link NetworkIdentity} matches this template.
*/
public boolean matches(NetworkIdentity ident) {
+ if (!matchesMetered(ident)) return false;
+ if (!matchesRoaming(ident)) return false;
+ if (!matchesDefaultNetwork(ident)) return false;
+
switch (mMatchRule) {
case MATCH_MOBILE_ALL:
return matchesMobile(ident);
@@ -326,6 +373,24 @@ public class NetworkTemplate implements Parcelable {
}
}
+ private boolean matchesMetered(NetworkIdentity ident) {
+ return (mMetered == METERED_ALL)
+ || (mMetered == METERED_YES && ident.mMetered)
+ || (mMetered == METERED_NO && !ident.mMetered);
+ }
+
+ private boolean matchesRoaming(NetworkIdentity ident) {
+ return (mRoaming == ROAMING_ALL)
+ || (mRoaming == ROAMING_YES && ident.mRoaming)
+ || (mRoaming == ROAMING_NO && !ident.mRoaming);
+ }
+
+ private boolean matchesDefaultNetwork(NetworkIdentity ident) {
+ return (mDefaultNetwork == DEFAULT_NETWORK_ALL)
+ || (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork)
+ || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
+ }
+
public boolean matchesSubscriberId(String subscriberId) {
return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dc271d8639d5..03a8dba5a82c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -35,6 +35,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
@@ -335,6 +336,9 @@ public abstract class BatteryStats implements Parcelable {
private final StringBuilder mFormatBuilder = new StringBuilder(32);
private final Formatter mFormatter = new Formatter(mFormatBuilder);
+ private static final String CELLULAR_CONTROLLER_NAME = "Cellular";
+ private static final String WIFI_CONTROLLER_NAME = "WiFi";
+
/**
* Indicates times spent by the uid at each cpu frequency in all process states.
*
@@ -412,6 +416,13 @@ public abstract class BatteryStats implements Parcelable {
/**
* @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+ * scan state.
+ */
+ public abstract LongCounter getScanTimeCounter();
+
+
+ /**
+ * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
* receive state.
*/
public abstract LongCounter getRxTimeCounter();
@@ -683,6 +694,14 @@ public abstract class BatteryStats implements Parcelable {
public abstract long[] getCpuFreqTimes(int which);
public abstract long[] getScreenOffCpuFreqTimes(int which);
+ /**
+ * Returns cpu active time of an uid.
+ */
+ public abstract long getCpuActiveTime();
+ /**
+ * Returns cpu times of an uid on each cluster
+ */
+ public abstract long[] getCpuClusterTimes();
/**
* Returns cpu times of an uid at a particular process state.
@@ -1500,6 +1519,10 @@ public abstract class BatteryStats implements Parcelable {
public static final int STATE2_WIFI_SIGNAL_STRENGTH_SHIFT = 4;
public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK =
0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT;
+ // Values for NUM_GPS_SIGNAL_QUALITY_LEVELS
+ public static final int STATE2_GPS_SIGNAL_QUALITY_SHIFT = 7;
+ public static final int STATE2_GPS_SIGNAL_QUALITY_MASK =
+ 0x1 << STATE2_GPS_SIGNAL_QUALITY_SHIFT;
public static final int STATE2_POWER_SAVE_FLAG = 1<<31;
public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
@@ -2088,6 +2111,23 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract int getNumConnectivityChange(int which);
+
+ /**
+ * Returns the time in microseconds that the phone has been running with
+ * the given GPS signal quality level
+ *
+ * {@hide}
+ */
+ public abstract long getGpsSignalQualityTime(int strengthBin,
+ long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the GPS battery drain in mA-ms
+ *
+ * {@hide}
+ */
+ public abstract long getGpsBatteryDrainMaMs();
+
/**
* Returns the time in microseconds that the phone has been on while the device was
* running on battery.
@@ -2312,6 +2352,9 @@ public abstract class BatteryStats implements Parcelable {
WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+ new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
+ HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
+ new String[] { "poor", "good"}, new String[] { "poor", "good"}),
};
public static final String[] HISTORY_EVENT_NAMES = new String[] {
@@ -2366,6 +2409,14 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getWifiOnTime(long elapsedRealtimeUs, int which);
/**
+ * Returns the time in microseconds that wifi has been active while the device was
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getWifiActiveTime(long elapsedRealtimeUs, int which);
+
+ /**
* Returns the time in microseconds that wifi has been on and the driver has
* been in the running state while the device was running on battery.
*
@@ -3312,6 +3363,20 @@ public abstract class BatteryStats implements Parcelable {
final long sleepTimeMs
= totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
+ if (controllerName.equals(WIFI_CONTROLLER_NAME)) {
+ final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(controllerName);
+ sb.append(" Scan time: ");
+ formatTimeMs(sb, scanTimeMs);
+ sb.append("(");
+ sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs));
+ sb.append(")");
+ pw.println(sb.toString());
+ }
+
sb.setLength(0);
sb.append(prefix);
sb.append(" ");
@@ -3353,7 +3418,7 @@ public abstract class BatteryStats implements Parcelable {
String [] powerLevel;
switch(controllerName) {
- case "Cellular":
+ case CELLULAR_CONTROLLER_NAME:
powerLevel = new String[] {
" less than 0dBm: ",
" 0dBm to 8dBm: ",
@@ -4641,7 +4706,7 @@ public abstract class BatteryStats implements Parcelable {
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
- printControllerActivity(pw, sb, prefix, "Cellular",
+ printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME,
getModemControllerActivity(), which);
pw.print(prefix);
@@ -4650,6 +4715,16 @@ public abstract class BatteryStats implements Parcelable {
sb.append(" Wifi Statistics:");
pw.println(sb.toString());
+ pw.print(prefix);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Wifi kernel active time: ");
+ final long wifiActiveTime = getWifiActiveTime(rawRealtime, which);
+ formatTimeMs(sb, wifiActiveTime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(wifiActiveTime, whichBatteryRealtime));
+ sb.append(")");
+ pw.println(sb.toString());
+
pw.print(" Wifi data received: "); pw.println(formatBytesLocked(wifiRxTotalBytes));
pw.print(" Wifi data sent: "); pw.println(formatBytesLocked(wifiTxTotalBytes));
pw.print(" Wifi packets received: "); pw.println(wifiRxTotalPackets);
@@ -4727,7 +4802,45 @@ public abstract class BatteryStats implements Parcelable {
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
- printControllerActivity(pw, sb, prefix, "WiFi", getWifiControllerActivity(), which);
+ printControllerActivity(pw, sb, prefix, WIFI_CONTROLLER_NAME,
+ getWifiControllerActivity(), which);
+
+ pw.print(prefix);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" GPS Statistics:");
+ pw.println(sb.toString());
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" GPS signal quality (Top 4 Average CN0):");
+ final String[] gpsSignalQualityDescription = new String[]{
+ "poor (less than 20 dBHz): ",
+ "good (greater than 20 dBHz): "};
+ final int numGpsSignalQualityBins = Math.min(GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS,
+ gpsSignalQualityDescription.length);
+ for (int i=0; i<numGpsSignalQualityBins; i++) {
+ final long time = getGpsSignalQualityTime(i, rawRealtime, which);
+ sb.append("\n ");
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(gpsSignalQualityDescription[i]);
+ formatTimeMs(sb, time/1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(time, whichBatteryRealtime));
+ sb.append(") ");
+ }
+ pw.println(sb.toString());
+
+ final long gpsBatteryDrainMaMs = getGpsBatteryDrainMaMs();
+ if (gpsBatteryDrainMaMs > 0) {
+ pw.print(prefix);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Battery Drain (mAh): ");
+ sb.append(Double.toString(((double) gpsBatteryDrainMaMs)/(3600 * 1000)));
+ pw.println(sb.toString());
+ }
pw.print(prefix);
sb.setLength(0);
@@ -5168,8 +5281,8 @@ public abstract class BatteryStats implements Parcelable {
pw.println(sb.toString());
}
- printControllerActivityIfInteresting(pw, sb, prefix + " ", "Modem",
- u.getModemControllerActivity(), which);
+ printControllerActivityIfInteresting(pw, sb, prefix + " ",
+ CELLULAR_CONTROLLER_NAME, u.getModemControllerActivity(), which);
if (wifiRxBytes > 0 || wifiTxBytes > 0 || wifiRxPackets > 0 || wifiTxPackets > 0) {
pw.print(prefix); pw.print(" Wi-Fi network: ");
@@ -5223,7 +5336,7 @@ public abstract class BatteryStats implements Parcelable {
pw.println(sb.toString());
}
- printControllerActivityIfInteresting(pw, sb, prefix + " ", "WiFi",
+ printControllerActivityIfInteresting(pw, sb, prefix + " ", WIFI_CONTROLLER_NAME,
u.getWifiControllerActivity(), which);
if (btRxBytes > 0 || btTxBytes > 0) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 848ab88d3cbc..33e8c3e47b44 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2352,22 +2352,28 @@ public final class Debug
}
/**
- * Attach a library as a jvmti agent to the current runtime.
+ * Attach a library as a jvmti agent to the current runtime, with the given classloader
+ * determining the library search path.
+ * <p>
+ * Note: agents may only be attached to debuggable apps. Otherwise, this function will
+ * throw a SecurityException.
*
- * @param library library containing the agent
- * @param options options passed to the agent
+ * @param library the library containing the agent.
+ * @param options the options passed to the agent.
+ * @param classLoader the classloader determining the library search path.
*
- * @throws IOException If the agent could not be attached
+ * @throws IOException if the agent could not be attached.
+ * @throws SecurityException if the app is not debuggable.
*/
- public static void attachJvmtiAgent(@NonNull String library, @Nullable String options)
- throws IOException {
+ public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
+ @Nullable ClassLoader classLoader) throws IOException {
Preconditions.checkNotNull(library);
Preconditions.checkArgument(!library.contains("="));
if (options == null) {
- VMDebug.attachAgent(library);
+ VMDebug.attachAgent(library, classLoader);
} else {
- VMDebug.attachAgent(library + "=" + options);
+ VMDebug.attachAgent(library + "=" + options, classLoader);
}
}
}
diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl
index 5e8590af11f1..3de953a2dfbe 100644
--- a/core/java/android/os/IPermissionController.aidl
+++ b/core/java/android/os/IPermissionController.aidl
@@ -22,4 +22,5 @@ interface IPermissionController {
boolean checkPermission(String permission, int pid, int uid);
String[] getPackagesForUid(int uid);
boolean isRuntimePermission(String permission);
+ int getPackageUid(String packageName, int flags);
}
diff --git a/core/java/android/os/ISystemUpdateManager.aidl b/core/java/android/os/ISystemUpdateManager.aidl
new file mode 100644
index 000000000000..f7f50791f528
--- /dev/null
+++ b/core/java/android/os/ISystemUpdateManager.aidl
@@ -0,0 +1,27 @@
+/* //device/java/android/android/os/ISystemUpdateInfo.aidl
+**
+** 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.os;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+/** @hide */
+interface ISystemUpdateManager {
+ Bundle retrieveSystemUpdateInfo();
+ void updateSystemUpdateInfo(in PersistableBundle data);
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e6cf5e6a6cbf..7654e9b6ee22 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -151,6 +151,12 @@ public class Process {
*/
public static final int OTA_UPDATE_UID = 1061;
+ /**
+ * Defines the UID used for incidentd.
+ * @hide
+ */
+ public static final int INCIDENTD_UID = 1067;
+
/** {@hide} */
public static final int NOBODY_UID = 9999;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 673a8ba661b6..57db9d19f42d 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -61,6 +61,7 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
@@ -101,6 +102,9 @@ public class RecoverySystem {
private static final String ACTION_EUICC_FACTORY_RESET =
"com.android.internal.action.EUICC_FACTORY_RESET";
+ /** used in {@link #wipeEuiccData} as package name of callback intent */
+ private static final String PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK = "android";
+
/**
* The recovery image uses this file to identify the location (i.e. blocks)
* of an OTA package on the /data partition. The block map file is
@@ -751,7 +755,7 @@ public class RecoverySystem {
// Block until the ordered broadcast has completed.
condition.block();
- wipeEuiccData(context, wipeEuicc);
+ wipeEuiccData(context, wipeEuicc, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
String shutdownArg = null;
if (shutdown) {
@@ -767,19 +771,29 @@ public class RecoverySystem {
bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
}
- private static void wipeEuiccData(Context context, final boolean isWipeEuicc) {
+ /**
+ * Returns whether wipe Euicc data successfully or not.
+ *
+ * @param isWipeEuicc whether we want to wipe Euicc data or not
+ * @param packageName the package name of the caller app.
+ *
+ * @hide
+ */
+ public static boolean wipeEuiccData(
+ Context context, final boolean isWipeEuicc, final String packageName) {
ContentResolver cr = context.getContentResolver();
if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
// If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles,
// as there's nothing to wipe nor retain.
Log.d(TAG, "Skipping eUICC wipe/retain as it is not provisioned");
- return;
+ return true;
}
EuiccManager euiccManager = (EuiccManager) context.getSystemService(
Context.EUICC_SERVICE);
if (euiccManager != null && euiccManager.isEnabled()) {
CountDownLatch euiccFactoryResetLatch = new CountDownLatch(1);
+ final AtomicBoolean wipingSucceeded = new AtomicBoolean(false);
BroadcastReceiver euiccWipeFinishReceiver = new BroadcastReceiver() {
@Override
@@ -801,6 +815,7 @@ public class RecoverySystem {
} else {
Log.d(TAG, "Successfully retained euicc data.");
}
+ wipingSucceeded.set(true /* newValue */);
}
euiccFactoryResetLatch.countDown();
}
@@ -808,7 +823,7 @@ public class RecoverySystem {
};
Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET);
- intent.setPackage("android");
+ intent.setPackage(packageName);
PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
IntentFilter filterConsent = new IntentFilter();
@@ -839,8 +854,8 @@ public class RecoverySystem {
} else {
Log.e(TAG, "Timeout retaining eUICC data.");
}
+ return false;
}
- context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
if (isWipeEuicc) {
@@ -848,8 +863,13 @@ public class RecoverySystem {
} else {
Log.e(TAG, "Retaining eUICC data interrupted", e);
}
+ return false;
+ } finally {
+ context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
}
+ return wipingSucceeded.get();
}
+ return false;
}
/** {@hide} */
diff --git a/core/java/android/os/SystemUpdateManager.java b/core/java/android/os/SystemUpdateManager.java
new file mode 100644
index 000000000000..ce3e225975f0
--- /dev/null
+++ b/core/java/android/os/SystemUpdateManager.java
@@ -0,0 +1,152 @@
+/*
+ * 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 com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Allows querying and posting system update information.
+ *
+ * {@hide}
+ */
+@SystemApi
+@SystemService(Context.SYSTEM_UPDATE_SERVICE)
+public class SystemUpdateManager {
+ private static final String TAG = "SystemUpdateManager";
+
+ /** The status key of the system update info, expecting an int value. */
+ @SystemApi
+ public static final String KEY_STATUS = "status";
+
+ /** The title of the current update, expecting a String value. */
+ @SystemApi
+ public static final String KEY_TITLE = "title";
+
+ /** Whether it is a security update, expecting a boolean value. */
+ @SystemApi
+ public static final String KEY_IS_SECURITY_UPDATE = "is_security_update";
+
+ /** The build fingerprint after installing the current update, expecting a String value. */
+ @SystemApi
+ public static final String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
+
+ /** The security patch level after installing the current update, expecting a String value. */
+ @SystemApi
+ public static final String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
+
+ /**
+ * The KEY_STATUS value that indicates there's no update status info available.
+ */
+ @SystemApi
+ public static final int STATUS_UNKNOWN = 0;
+
+ /**
+ * The KEY_STATUS value that indicates there's no pending update.
+ */
+ @SystemApi
+ public static final int STATUS_IDLE = 1;
+
+ /**
+ * The KEY_STATUS value that indicates an update is available for download, but pending user
+ * approval to start.
+ */
+ @SystemApi
+ public static final int STATUS_WAITING_DOWNLOAD = 2;
+
+ /**
+ * The KEY_STATUS value that indicates an update is in progress (i.e. downloading or installing
+ * has started).
+ */
+ @SystemApi
+ public static final int STATUS_IN_PROGRESS = 3;
+
+ /**
+ * The KEY_STATUS value that indicates an update is available for install.
+ */
+ @SystemApi
+ public static final int STATUS_WAITING_INSTALL = 4;
+
+ /**
+ * The KEY_STATUS value that indicates an update will be installed after a reboot. This applies
+ * to both of A/B and non-A/B OTAs.
+ */
+ @SystemApi
+ public static final int STATUS_WAITING_REBOOT = 5;
+
+ private final ISystemUpdateManager mService;
+
+ /** @hide */
+ public SystemUpdateManager(ISystemUpdateManager service) {
+ mService = checkNotNull(service, "missing ISystemUpdateManager");
+ }
+
+ /**
+ * Queries the current pending system update info.
+ *
+ * <p>Requires the {@link android.Manifest.permission#READ_SYSTEM_UPDATE_INFO} or
+ * {@link android.Manifest.permission#RECOVERY} permission.
+ *
+ * @return A {@code Bundle} that contains the pending system update information in key-value
+ * pairs.
+ *
+ * @throws SecurityException if the caller is not allowed to read the info.
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_SYSTEM_UPDATE_INFO,
+ android.Manifest.permission.RECOVERY,
+ })
+ public Bundle retrieveSystemUpdateInfo() {
+ try {
+ return mService.retrieveSystemUpdateInfo();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Allows a system updater to publish the pending update info.
+ *
+ * <p>The reported info will not persist across reboots. Because only the reporting updater
+ * understands the criteria to determine a successful/failed update.
+ *
+ * <p>Requires the {@link android.Manifest.permission#RECOVERY} permission.
+ *
+ * @param infoBundle The {@code PersistableBundle} that contains the system update information,
+ * such as the current update status. {@link #KEY_STATUS} is required in the bundle.
+ *
+ * @throws IllegalArgumentException if @link #KEY_STATUS} does not exist.
+ * @throws SecurityException if the caller is not allowed to update the info.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RECOVERY)
+ public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
+ if (infoBundle == null || !infoBundle.containsKey(KEY_STATUS)) {
+ throw new IllegalArgumentException("Missing status in the bundle");
+ }
+ try {
+ mService.updateSystemUpdateInfo(infoBundle);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java b/core/java/android/os/connectivity/GpsBatteryStats.aidl
index dbc61c26e82e..7b96d1a8e062 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java
+++ b/core/java/android/os/connectivity/GpsBatteryStats.aidl
@@ -14,15 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.core.instrumentation;
+package android.os.connectivity;
-public interface Instrumentable {
-
- int METRICS_CATEGORY_UNKNOWN = 0;
-
- /**
- * Instrumented name for a view as defined in
- * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}.
- */
- int getMetricsCategory();
-}
+/** {@hide} */
+parcelable GpsBatteryStats; \ No newline at end of file
diff --git a/core/java/android/os/connectivity/GpsBatteryStats.java b/core/java/android/os/connectivity/GpsBatteryStats.java
new file mode 100644
index 000000000000..f2ac5ef6d40b
--- /dev/null
+++ b/core/java/android/os/connectivity/GpsBatteryStats.java
@@ -0,0 +1,108 @@
+/*
+ * 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.os.connectivity;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.location.gnssmetrics.GnssMetrics;
+
+import java.util.Arrays;
+
+/**
+ * API for GPS power stats
+ *
+ * @hide
+ */
+public final class GpsBatteryStats implements Parcelable {
+
+ private long mLoggingDurationMs;
+ private long mEnergyConsumedMaMs;
+ private long[] mTimeInGpsSignalQualityLevel;
+
+ public static final Parcelable.Creator<GpsBatteryStats> CREATOR = new
+ Parcelable.Creator<GpsBatteryStats>() {
+ public GpsBatteryStats createFromParcel(Parcel in) {
+ return new GpsBatteryStats(in);
+ }
+
+ public GpsBatteryStats[] newArray(int size) {
+ return new GpsBatteryStats[size];
+ }
+ };
+
+ public GpsBatteryStats() {
+ initialize();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mLoggingDurationMs);
+ out.writeLong(mEnergyConsumedMaMs);
+ out.writeLongArray(mTimeInGpsSignalQualityLevel);
+ }
+
+ public void readFromParcel(Parcel in) {
+ mLoggingDurationMs = in.readLong();
+ mEnergyConsumedMaMs = in.readLong();
+ in.readLongArray(mTimeInGpsSignalQualityLevel);
+ }
+
+ public long getLoggingDurationMs() {
+ return mLoggingDurationMs;
+ }
+
+ public long getEnergyConsumedMaMs() {
+ return mEnergyConsumedMaMs;
+ }
+
+ public long[] getTimeInGpsSignalQualityLevel() {
+ return mTimeInGpsSignalQualityLevel;
+ }
+
+ public void setLoggingDurationMs(long t) {
+ mLoggingDurationMs = t;
+ return;
+ }
+
+ public void setEnergyConsumedMaMs(long e) {
+ mEnergyConsumedMaMs = e;
+ return;
+ }
+
+ public void setTimeInGpsSignalQualityLevel(long[] t) {
+ mTimeInGpsSignalQualityLevel = Arrays.copyOfRange(t, 0,
+ Math.min(t.length, GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS));
+ return;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private GpsBatteryStats(Parcel in) {
+ initialize();
+ readFromParcel(in);
+ }
+
+ private void initialize() {
+ mLoggingDurationMs = 0;
+ mEnergyConsumedMaMs = 0;
+ mTimeInGpsSignalQualityLevel = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+ return;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.aidl b/core/java/android/os/connectivity/WifiBatteryStats.aidl
new file mode 100644
index 000000000000..12ac73828eed
--- /dev/null
+++ b/core/java/android/os/connectivity/WifiBatteryStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.os.connectivity;
+
+/** {@hide} */
+parcelable WifiBatteryStats; \ No newline at end of file
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
new file mode 100644
index 000000000000..e5341eeeb17b
--- /dev/null
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -0,0 +1,279 @@
+/*
+ * 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.os.connectivity;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * API for Wifi power stats
+ *
+ * @hide
+ */
+public final class WifiBatteryStats implements Parcelable {
+
+ private long mLoggingDurationMs;
+ private long mKernelActiveTimeMs;
+ private long mNumPacketsTx;
+ private long mNumBytesTx;
+ private long mNumPacketsRx;
+ private long mNumBytesRx;
+ private long mSleepTimeMs;
+ private long mScanTimeMs;
+ private long mIdleTimeMs;
+ private long mRxTimeMs;
+ private long mTxTimeMs;
+ private long mEnergyConsumedMaMs;
+ private long mNumAppScanRequest;
+ private long[] mTimeInStateMs;
+ private long[] mTimeInSupplicantStateMs;
+ private long[] mTimeInRxSignalStrengthLevelMs;
+
+ public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new
+ Parcelable.Creator<WifiBatteryStats>() {
+ public WifiBatteryStats createFromParcel(Parcel in) {
+ return new WifiBatteryStats(in);
+ }
+
+ public WifiBatteryStats[] newArray(int size) {
+ return new WifiBatteryStats[size];
+ }
+ };
+
+ public WifiBatteryStats() {
+ initialize();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mLoggingDurationMs);
+ out.writeLong(mKernelActiveTimeMs);
+ out.writeLong(mNumPacketsTx);
+ out.writeLong(mNumBytesTx);
+ out.writeLong(mNumPacketsRx);
+ out.writeLong(mNumBytesRx);
+ out.writeLong(mSleepTimeMs);
+ out.writeLong(mScanTimeMs);
+ out.writeLong(mIdleTimeMs);
+ out.writeLong(mRxTimeMs);
+ out.writeLong(mTxTimeMs);
+ out.writeLong(mEnergyConsumedMaMs);
+ out.writeLong(mNumAppScanRequest);
+ out.writeLongArray(mTimeInStateMs);
+ out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
+ out.writeLongArray(mTimeInSupplicantStateMs);
+ }
+
+ public void readFromParcel(Parcel in) {
+ mLoggingDurationMs = in.readLong();
+ mKernelActiveTimeMs = in.readLong();
+ mNumPacketsTx = in.readLong();
+ mNumBytesTx = in.readLong();
+ mNumPacketsRx = in.readLong();
+ mNumBytesRx = in.readLong();
+ mSleepTimeMs = in.readLong();
+ mScanTimeMs = in.readLong();
+ mIdleTimeMs = in.readLong();
+ mRxTimeMs = in.readLong();
+ mTxTimeMs = in.readLong();
+ mEnergyConsumedMaMs = in.readLong();
+ mNumAppScanRequest = in.readLong();
+ in.readLongArray(mTimeInStateMs);
+ in.readLongArray(mTimeInRxSignalStrengthLevelMs);
+ in.readLongArray(mTimeInSupplicantStateMs);
+ }
+
+ public long getLoggingDurationMs() {
+ return mLoggingDurationMs;
+ }
+
+ public long getKernelActiveTimeMs() {
+ return mKernelActiveTimeMs;
+ }
+
+ public long getNumPacketsTx() {
+ return mNumPacketsTx;
+ }
+
+ public long getNumBytesTx() {
+ return mNumBytesTx;
+ }
+
+ public long getNumPacketsRx() {
+ return mNumPacketsRx;
+ }
+
+ public long getNumBytesRx() {
+ return mNumBytesRx;
+ }
+
+ public long getSleepTimeMs() {
+ return mSleepTimeMs;
+ }
+
+ public long getScanTimeMs() {
+ return mScanTimeMs;
+ }
+
+ public long getIdleTimeMs() {
+ return mIdleTimeMs;
+ }
+
+ public long getRxTimeMs() {
+ return mRxTimeMs;
+ }
+
+ public long getTxTimeMs() {
+ return mTxTimeMs;
+ }
+
+ public long getEnergyConsumedMaMs() {
+ return mEnergyConsumedMaMs;
+ }
+
+ public long getNumAppScanRequest() {
+ return mNumAppScanRequest;
+ }
+
+ public long[] getTimeInStateMs() {
+ return mTimeInStateMs;
+ }
+
+ public long[] getTimeInRxSignalStrengthLevelMs() {
+ return mTimeInRxSignalStrengthLevelMs;
+ }
+
+ public long[] getTimeInSupplicantStateMs() {
+ return mTimeInSupplicantStateMs;
+ }
+
+ public void setLoggingDurationMs(long t) {
+ mLoggingDurationMs = t;
+ return;
+ }
+
+ public void setKernelActiveTimeMs(long t) {
+ mKernelActiveTimeMs = t;
+ return;
+ }
+
+ public void setNumPacketsTx(long n) {
+ mNumPacketsTx = n;
+ return;
+ }
+
+ public void setNumBytesTx(long b) {
+ mNumBytesTx = b;
+ return;
+ }
+
+ public void setNumPacketsRx(long n) {
+ mNumPacketsRx = n;
+ return;
+ }
+
+ public void setNumBytesRx(long b) {
+ mNumBytesRx = b;
+ return;
+ }
+
+ public void setSleepTimeMs(long t) {
+ mSleepTimeMs = t;
+ return;
+ }
+
+ public void setScanTimeMs(long t) {
+ mScanTimeMs = t;
+ return;
+ }
+
+ public void setIdleTimeMs(long t) {
+ mIdleTimeMs = t;
+ return;
+ }
+
+ public void setRxTimeMs(long t) {
+ mRxTimeMs = t;
+ return;
+ }
+
+ public void setTxTimeMs(long t) {
+ mTxTimeMs = t;
+ return;
+ }
+
+ public void setEnergyConsumedMaMs(long e) {
+ mEnergyConsumedMaMs = e;
+ return;
+ }
+
+ public void setNumAppScanRequest(long n) {
+ mNumAppScanRequest = n;
+ return;
+ }
+
+ public void setTimeInStateMs(long[] t) {
+ mTimeInStateMs = Arrays.copyOfRange(t, 0,
+ Math.min(t.length, BatteryStats.NUM_WIFI_STATES));
+ return;
+ }
+
+ public void setTimeInRxSignalStrengthLevelMs(long[] t) {
+ mTimeInRxSignalStrengthLevelMs = Arrays.copyOfRange(t, 0,
+ Math.min(t.length, BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS));
+ return;
+ }
+
+ public void setTimeInSupplicantStateMs(long[] t) {
+ mTimeInSupplicantStateMs = Arrays.copyOfRange(
+ t, 0, Math.min(t.length, BatteryStats.NUM_WIFI_SUPPL_STATES));
+ return;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ private WifiBatteryStats(Parcel in) {
+ initialize();
+ readFromParcel(in);
+ }
+
+ private void initialize() {
+ mLoggingDurationMs = 0;
+ mKernelActiveTimeMs = 0;
+ mNumPacketsTx = 0;
+ mNumBytesTx = 0;
+ mNumPacketsRx = 0;
+ mNumBytesRx = 0;
+ mSleepTimeMs = 0;
+ mScanTimeMs = 0;
+ mIdleTimeMs = 0;
+ mRxTimeMs = 0;
+ mTxTimeMs = 0;
+ mEnergyConsumedMaMs = 0;
+ mNumAppScanRequest = 0;
+ mTimeInStateMs = new long[BatteryStats.NUM_WIFI_STATES];
+ Arrays.fill(mTimeInStateMs, 0);
+ mTimeInRxSignalStrengthLevelMs = new long[BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS];
+ Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
+ mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES];
+ Arrays.fill(mTimeInSupplicantStateMs, 0);
+ return;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
index 2eca4c98d235..9ac2b3e1136e 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -33,7 +33,6 @@ import java.util.Random;
public class RapporEncoder implements DifferentialPrivacyEncoder {
// Hard-coded seed and secret for insecure encoder
- private static final long INSECURE_RANDOM_SEED = 0x12345678L;
private static final byte[] INSECURE_SECRET = new byte[]{
(byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93,
(byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54,
@@ -66,8 +65,8 @@ public class RapporEncoder implements DifferentialPrivacyEncoder {
// Use SecureRandom as random generator.
random = sSecureRandom;
} else {
- // Hard-coded random generator, to have deterministic result.
- random = new Random(INSECURE_RANDOM_SEED);
+ // To have deterministic result by hard coding encoder id as seed.
+ random = new Random((long) config.mEncoderId.hashCode());
userSecret = INSECURE_SECRET;
}
mEncoder = new Encoder(random, null, null,
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index 21694575631a..7ad9e013c617 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -154,9 +154,12 @@ public final class AlarmClock {
public static final String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
/**
- * Activity Action: Dismiss timers.
+ * Activity Action: Dismiss a timer.
* <p>
- * Dismiss all currently expired timers. If there are no expired timers, then this is a no-op.
+ * The timer to dismiss should be specified using the Intent's data URI, which represents a
+ * deeplink to the timer.
+ * </p><p>
+ * If no data URI is provided, dismiss all expired timers.
* </p>
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 60df467bc20f..c6c8d9d69bfb 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -223,14 +223,13 @@ public class CallLog {
/** Call was WIFI call. */
public static final int FEATURES_WIFI = 1 << 3;
- /** Call was on RTT at some point */
- public static final int FEATURES_RTT = 1 << 4;
-
/**
* Indicates the call underwent Assisted Dialing.
- * @hide
*/
- public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10;
+ public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4;
+
+ /** Call was on RTT at some point */
+ public static final int FEATURES_RTT = 1 << 5;
/**
* The phone number as the user entered it.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8c2a8ae9080a..4228fbb7c40e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,10 +16,12 @@
package android.provider;
+import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR;
import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR;
import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.SettingsValidators.LOCALE_VALIDATOR;
import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR;
import static android.provider.SettingsValidators.URI_VALIDATOR;
@@ -4008,6 +4010,9 @@ public final class Settings {
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
+ * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
* @hide
*/
public static final String[] LEGACY_RESTORE_SETTINGS = {
@@ -4116,6 +4121,9 @@ public final class Settings {
/**
* These are all public system settings
*
+ * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
* @hide
*/
public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
@@ -4391,8 +4399,8 @@ public final class Settings {
public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
- private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
- BOOLEAN_VALIDATOR;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
+ BOOLEAN_VALIDATOR;
/**
* @deprecated Use
@@ -4402,6 +4410,9 @@ public final class Settings {
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT}
* instead
@@ -4409,6 +4420,9 @@ public final class Settings {
@Deprecated
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT;
+ private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} instead
*/
@@ -5190,6 +5204,8 @@ public final class Settings {
@Deprecated
public static final String ALLOW_MOCK_LOCATION = "mock_location";
+ private static final Validator ALLOW_MOCK_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* On Android 8.0 (API level 26) and higher versions of the platform,
* a 64-bit number (expressed as a hexadecimal string), unique to
@@ -5284,6 +5300,8 @@ public final class Settings {
@TestApi
public static final String AUTOFILL_SERVICE = "autofill_service";
+ private static final Validator AUTOFILL_SERVICE_VALIDATOR = COMPONENT_NAME_VALIDATOR;
+
/**
* Boolean indicating if Autofill supports field classification.
*
@@ -5370,9 +5388,38 @@ public final class Settings {
* List of input methods that are currently enabled. This is a string
* containing the IDs of all enabled input methods, each ID separated
* by ':'.
+ *
+ * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0"
+ * where imeId is ComponentName and subtype is int32.
*/
public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
+ private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] inputMethods = value.split(":");
+ boolean valid = true;
+ for (String inputMethod : inputMethods) {
+ if (inputMethod.length() == 0) {
+ return false;
+ }
+ String[] subparts = inputMethod.split(";");
+ for (String subpart : subparts) {
+ // allow either a non negative integer or a ComponentName
+ valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart)
+ || COMPONENT_NAME_VALIDATOR.validate(subpart));
+ }
+ if (!valid) {
+ return false;
+ }
+ }
+ return valid;
+ }
+ };
+
/**
* List of system input methods that are currently disabled. This is a string
* containing the IDs of all disabled input methods, each ID separated
@@ -5388,6 +5435,8 @@ public final class Settings {
*/
public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
+ private static final Validator SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Host name and port for global http proxy. Uses ':' seperator for
* between host and port.
@@ -5677,6 +5726,8 @@ public final class Settings {
*/
public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
+ private static final Validator ACCESSIBILITY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Setting specifying if the accessibility shortcut is enabled.
* @hide
@@ -5684,6 +5735,8 @@ public final class Settings {
public static final String ACCESSIBILITY_SHORTCUT_ENABLED =
"accessibility_shortcut_enabled";
+ private static final Validator ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Setting specifying if the accessibility shortcut is enabled.
* @hide
@@ -5691,6 +5744,9 @@ public final class Settings {
public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN =
"accessibility_shortcut_on_lock_screen";
+ private static final Validator ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting specifying if the accessibility shortcut dialog has been shown to this user.
* @hide
@@ -5698,6 +5754,9 @@ public final class Settings {
public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN =
"accessibility_shortcut_dialog_shown";
+ private static final Validator ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting specifying the accessibility service to be toggled via the accessibility
* shortcut. Must be its flattened {@link ComponentName}.
@@ -5706,6 +5765,9 @@ public final class Settings {
public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
"accessibility_shortcut_target_service";
+ private static final Validator ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR =
+ COMPONENT_NAME_VALIDATOR;
+
/**
* Setting specifying the accessibility service or feature to be toggled via the
* accessibility button in the navigation bar. This is either a flattened
@@ -5716,17 +5778,32 @@ public final class Settings {
public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT =
"accessibility_button_target_component";
+ private static final Validator ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR =
+ new Validator() {
+ @Override
+ public boolean validate(String value) {
+ // technically either ComponentName or class name, but there's proper value
+ // validation at callsites, so allow any non-null string
+ return value != null;
+ }
+ };
+
/**
* If touch exploration is enabled.
*/
public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
+ private static final Validator TOUCH_EXPLORATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* List of the enabled accessibility providers.
*/
public static final String ENABLED_ACCESSIBILITY_SERVICES =
"enabled_accessibility_services";
+ private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* List of the accessibility services to which the user has granted
* permission to put the device into touch exploration mode.
@@ -5736,6 +5813,9 @@ public final class Settings {
public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES =
"touch_exploration_granted_accessibility_services";
+ private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Uri of the slice that's presented on the keyguard.
* Defaults to a slice with the date and next alarm.
@@ -5754,6 +5834,8 @@ public final class Settings {
@Deprecated
public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
+ private static final Validator ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether to draw text with high contrast while in accessibility mode.
*
@@ -5762,6 +5844,9 @@ public final class Settings {
public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED =
"high_text_contrast_enabled";
+ private static final Validator ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies whether the display magnification is enabled via a system-wide
* triple tap gesture. Display magnifications allows the user to zoom in the display content
@@ -5774,6 +5859,9 @@ public final class Settings {
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
"accessibility_display_magnification_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies whether the display magnification is enabled via a shortcut
* affordance within the system's navigation area. Display magnifications allows the user to
@@ -5785,6 +5873,9 @@ public final class Settings {
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
"accessibility_display_magnification_navbar_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR
+ = BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies what the display magnification scale is.
* Display magnifications allows the user to zoom in the display
@@ -5798,6 +5889,9 @@ public final class Settings {
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE =
"accessibility_display_magnification_scale";
+ private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR =
+ new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
+
/**
* Unused mangnification setting
*
@@ -5850,6 +5944,9 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_ENABLED =
"accessibility_captioning_enabled";
+ private static final Validator ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies the language for captions as a locale string,
* e.g. en_US.
@@ -5860,6 +5957,8 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_LOCALE =
"accessibility_captioning_locale";
+ private static final Validator ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR = LOCALE_VALIDATOR;
+
/**
* Integer property that specifies the preset style for captions, one
* of:
@@ -5874,6 +5973,10 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_PRESET =
"accessibility_captioning_preset";
+ private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
+ "3", "4"});
+
/**
* Integer property that specifes the background color for captions as a
* packed 32-bit color.
@@ -5884,6 +5987,9 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR =
"accessibility_captioning_background_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* Integer property that specifes the foreground color for captions as a
* packed 32-bit color.
@@ -5894,6 +6000,9 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR =
"accessibility_captioning_foreground_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* Integer property that specifes the edge type for captions, one of:
* <ul>
@@ -5908,6 +6017,9 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE =
"accessibility_captioning_edge_type";
+ private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"});
+
/**
* Integer property that specifes the edge color for captions as a
* packed 32-bit color.
@@ -5919,6 +6031,9 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR =
"accessibility_captioning_edge_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* Integer property that specifes the window color for captions as a
* packed 32-bit color.
@@ -5929,6 +6044,9 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR =
"accessibility_captioning_window_color";
+ private static final Validator ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR =
+ ANY_INTEGER_VALIDATOR;
+
/**
* String property that specifies the typeface for captions, one of:
* <ul>
@@ -5944,6 +6062,10 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE =
"accessibility_captioning_typeface";
+ private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT",
+ "MONOSPACE", "SANS_SERIF", "SERIF"});
+
/**
* Floating point property that specifies font scaling for captions.
*
@@ -5952,12 +6074,18 @@ public final class Settings {
public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE =
"accessibility_captioning_font_scale";
+ private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR =
+ new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f);
+
/**
* Setting that specifies whether display color inversion is enabled.
*/
public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
"accessibility_display_inversion_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Setting that specifies whether display color space adjustment is
* enabled.
@@ -5967,15 +6095,24 @@ public final class Settings {
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED =
"accessibility_display_daltonizer_enabled";
+ private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Integer property that specifies the type of color space adjustment to
- * perform. Valid values are defined in AccessibilityManager.
+ * perform. Valid values are defined in AccessibilityManager:
+ * - AccessibilityManager.DALTONIZER_DISABLED = -1
+ * - AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY = 0
+ * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY = 12
*
* @hide
*/
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
"accessibility_display_daltonizer";
+ private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "12"});
+
/**
* Setting that specifies whether automatic click when the mouse pointer stops moving is
* enabled.
@@ -5985,6 +6122,9 @@ public final class Settings {
public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
"accessibility_autoclick_enabled";
+ private static final Validator ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Integer setting specifying amount of time in ms the mouse pointer has to stay still
* before performing click when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set.
@@ -5995,6 +6135,9 @@ public final class Settings {
public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
"accessibility_autoclick_delay";
+ private static final Validator ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Whether or not larger size icons are used for the pointer of mouse/trackpad for
* accessibility.
@@ -6004,12 +6147,18 @@ public final class Settings {
public static final String ACCESSIBILITY_LARGE_POINTER_ICON =
"accessibility_large_pointer_icon";
+ private static final Validator ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* The timeout for considering a press to be a long press in milliseconds.
* @hide
*/
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
+ private static final Validator LONG_PRESS_TIMEOUT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* The duration in milliseconds between the first tap's up event and the second tap's
* down event for an interaction to be considered part of the same multi-press.
@@ -6063,16 +6212,22 @@ public final class Settings {
*/
public static final String TTS_DEFAULT_RATE = "tts_default_rate";
+ private static final Validator TTS_DEFAULT_RATE_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Default text-to-speech engine pitch. 100 = 1x
*/
public static final String TTS_DEFAULT_PITCH = "tts_default_pitch";
+ private static final Validator TTS_DEFAULT_PITCH_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Default text-to-speech engine.
*/
public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
+ private static final Validator TTS_DEFAULT_SYNTH_VALIDATOR = PACKAGE_NAME_VALIDATOR;
+
/**
* Default text-to-speech language.
*
@@ -6120,11 +6275,33 @@ public final class Settings {
*/
public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
+ private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null || value.length() == 0) {
+ return false;
+ }
+ String[] ttsLocales = value.split(",");
+ boolean valid = true;
+ for (String ttsLocale : ttsLocales) {
+ String[] parts = ttsLocale.split(":");
+ valid |= ((parts.length == 2)
+ && (parts[0].length() > 0)
+ && ANY_STRING_VALIDATOR.validate(parts[0])
+ && LOCALE_VALIDATOR.validate(parts[1]));
+ }
+ return valid;
+ }
+ };
+
/**
* Space delimited list of plugin packages that are enabled.
*/
public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
+ private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR =
+ new SettingsValidators.PackageNameListValidator(" ");
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON}
* instead.
@@ -6133,8 +6310,8 @@ public final class Settings {
public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
- private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
- BOOLEAN_VALIDATOR;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
+ BOOLEAN_VALIDATOR;
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY}
@@ -6144,6 +6321,9 @@ public final class Settings {
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
+ private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT}
* instead.
@@ -6152,6 +6332,9 @@ public final class Settings {
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT =
Global.WIFI_NUM_OPEN_NETWORKS_KEPT;
+ private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_ON}
* instead.
@@ -6310,6 +6493,9 @@ public final class Settings {
public static final String PREFERRED_TTY_MODE =
"preferred_tty_mode";
+ private static final Validator PREFERRED_TTY_MODE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
+
/**
* Whether the enhanced voice privacy mode is enabled.
* 0 = normal voice privacy
@@ -6318,6 +6504,8 @@ public final class Settings {
*/
public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled";
+ private static final Validator ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the TTY mode mode is enabled.
* 0 = disabled
@@ -6326,6 +6514,8 @@ public final class Settings {
*/
public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
+ private static final Validator TTY_MODE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Controls whether settings backup is enabled.
* Type: int ( 0 = disabled, 1 = enabled )
@@ -6496,24 +6686,32 @@ public final class Settings {
*/
public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd";
+ private static final Validator MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true)
* @hide
*/
public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart";
+ private static final Validator MOUNT_UMS_AUTOSTART_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true)
* @hide
*/
public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt";
+ private static final Validator MOUNT_UMS_PROMPT_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true)
* @hide
*/
public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
+ private static final Validator MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* If nonzero, ANRs in invisible background processes bring up a dialog.
* Otherwise, the process will be silently killed.
@@ -6531,6 +6729,9 @@ public final class Settings {
public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
"show_first_crash_dialog_dev_option";
+ private static final Validator SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* The {@link ComponentName} string of the service to be used as the voice recognition
* service.
@@ -6556,6 +6757,8 @@ public final class Settings {
*/
public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
+ private static final Validator SELECTED_SPELL_CHECKER_VALIDATOR = COMPONENT_NAME_VALIDATOR;
+
/**
* The {@link ComponentName} string of the selected subtype of the selected spell checker
* service which is one of the services managed by the text service manager.
@@ -6565,13 +6768,18 @@ public final class Settings {
public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
"selected_spell_checker_subtype";
+ private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR =
+ COMPONENT_NAME_VALIDATOR;
+
/**
- * The {@link ComponentName} string whether spell checker is enabled or not.
+ * Whether spell checker is enabled or not.
*
* @hide
*/
public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled";
+ private static final Validator SPELL_CHECKER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* What happens when the user presses the Power button while in-call
* and the screen is on.<br/>
@@ -6583,6 +6791,9 @@ public final class Settings {
*/
public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
+ private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"});
+
/**
* INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen".
* @hide
@@ -6638,12 +6849,16 @@ public final class Settings {
*/
public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled";
+ private static final Validator WAKE_GESTURE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the device should doze if configured.
* @hide
*/
public static final String DOZE_ENABLED = "doze_enabled";
+ private static final Validator DOZE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether doze should be always on.
* @hide
@@ -6656,6 +6871,8 @@ public final class Settings {
*/
public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up";
+ private static final Validator DOZE_PULSE_ON_PICK_UP_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the device should pulse on long press gesture.
* @hide
@@ -6668,6 +6885,8 @@ public final class Settings {
*/
public static final String DOZE_PULSE_ON_DOUBLE_TAP = "doze_pulse_on_double_tap";
+ private static final Validator DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* The current night mode that has been selected by the user. Owned
* and controlled by UiModeManagerService. Constants are as per
@@ -6682,6 +6901,8 @@ public final class Settings {
*/
public static final String SCREENSAVER_ENABLED = "screensaver_enabled";
+ private static final Validator SCREENSAVER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* The user's chosen screensaver components.
*
@@ -6691,6 +6912,9 @@ public final class Settings {
*/
public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
+ private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(",");
+
/**
* If screensavers are enabled, whether the screensaver should be automatically launched
* when the device is inserted into a (desk) dock.
@@ -6698,6 +6922,8 @@ public final class Settings {
*/
public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock";
+ private static final Validator SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* If screensavers are enabled, whether the screensaver should be automatically launched
* when the screen times out when not on battery.
@@ -6705,6 +6931,8 @@ public final class Settings {
*/
public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep";
+ private static final Validator SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* If screensavers are enabled, the default screensaver component.
* @hide
@@ -6717,6 +6945,9 @@ public final class Settings {
*/
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
+ private static final Validator NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR =
+ COMPONENT_NAME_VALIDATOR;
+
/**
* Whether NFC payment is handled by the foreground application or a default.
* @hide
@@ -6814,6 +7045,9 @@ public final class Settings {
public static final String ENABLED_NOTIFICATION_ASSISTANT =
"enabled_notification_assistant";
+ private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Read only list of the service components that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
@@ -6825,6 +7059,9 @@ public final class Settings {
@Deprecated
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
+ private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Read only list of the packages that the current user has explicitly allowed to
* manage do not disturb, separated by ':'.
@@ -6837,6 +7074,9 @@ public final class Settings {
public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
"enabled_notification_policy_access_packages";
+ private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR =
+ new SettingsValidators.PackageNameListValidator(":");
+
/**
* Defines whether managed profile ringtones should be synced from it's parent profile
* <p>
@@ -6850,6 +7090,8 @@ public final class Settings {
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
+ private static final Validator SYNC_PARENT_SOUNDS_VALIDATOR = BOOLEAN_VALIDATOR;
+
/** @hide */
public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
@@ -6936,12 +7178,17 @@ public final class Settings {
*/
public static final String SLEEP_TIMEOUT = "sleep_timeout";
+ private static final Validator SLEEP_TIMEOUT_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(-1, Integer.MAX_VALUE);
+
/**
* Controls whether double tap to wake is enabled.
* @hide
*/
public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
+ private static final Validator DOUBLE_TAP_TO_WAKE_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* The current assistant component. It could be a voice interaction service,
* or an activity that handles ACTION_ASSIST, or empty which means using the default
@@ -6958,6 +7205,8 @@ public final class Settings {
*/
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
+ private static final Validator CAMERA_GESTURE_DISABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
@@ -6967,6 +7216,9 @@ public final class Settings {
public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED =
"camera_double_tap_power_gesture_disabled";
+ private static final Validator CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether the camera double twist gesture to flip between front and back mode should be
* enabled.
@@ -6976,6 +7228,9 @@ public final class Settings {
public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED =
"camera_double_twist_to_flip_enabled";
+ private static final Validator CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether or not the smart camera lift trigger that launches the camera when the user moves
* the phone into a position for taking photos should be enabled.
@@ -6998,6 +7253,9 @@ public final class Settings {
*/
public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled";
+ private static final Validator ASSIST_GESTURE_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Sensitivity control for the assist gesture.
*
@@ -7005,6 +7263,9 @@ public final class Settings {
*/
public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
+ private static final Validator ASSIST_GESTURE_SENSITIVITY_VALIDATOR =
+ new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 1.0f);
+
/**
* Whether the assist gesture should silence alerts.
*
@@ -7013,6 +7274,9 @@ public final class Settings {
public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED =
"assist_gesture_silence_alerts_enabled";
+ private static final Validator ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether the assist gesture should wake the phone.
*
@@ -7021,6 +7285,9 @@ public final class Settings {
public static final String ASSIST_GESTURE_WAKE_ENABLED =
"assist_gesture_wake_enabled";
+ private static final Validator ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* Whether Assist Gesture Deferred Setup has been completed
*
@@ -7028,6 +7295,8 @@ public final class Settings {
*/
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
+ private static final Validator ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Control whether Night display is currently activated.
* @hide
@@ -7040,6 +7309,8 @@ public final class Settings {
*/
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
+ private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Control the color temperature of Night Display, represented in Kelvin.
* @hide
@@ -7047,6 +7318,9 @@ public final class Settings {
public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE =
"night_display_color_temperature";
+ private static final Validator NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Custom time when Night display is scheduled to activate.
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
@@ -7055,6 +7329,9 @@ public final class Settings {
public static final String NIGHT_DISPLAY_CUSTOM_START_TIME =
"night_display_custom_start_time";
+ private static final Validator NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Custom time when Night display is scheduled to deactivate.
* Represented as milliseconds from midnight (e.g. 21600000 == 6am).
@@ -7062,6 +7339,9 @@ public final class Settings {
*/
public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
+ private static final Validator NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* A String representing the LocalDateTime when Night display was last activated. Use to
* decide whether to apply the current activated state after a reboot or user change. In
@@ -7079,6 +7359,9 @@ public final class Settings {
*/
public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
+ private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
+ new SettingsValidators.ComponentNameListValidator(":");
+
/**
* Behavior of the display while in VR mode.
*
@@ -7088,6 +7371,9 @@ public final class Settings {
*/
public static final String VR_DISPLAY_MODE = "vr_display_mode";
+ private static final Validator VR_DISPLAY_MODE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"});
+
/**
* Lower the display persistence while the system is in VR mode.
*
@@ -7144,6 +7430,9 @@ public final class Settings {
public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN =
"automatic_storage_manager_days_to_retain";
+ private static final Validator AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Default number of days of information for the automatic storage manager to retain.
*
@@ -7185,12 +7474,30 @@ public final class Settings {
public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
"system_navigation_keys_enabled";
+ private static final Validator SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Holds comma separated list of ordering of QS tiles.
* @hide
*/
public static final String QS_TILES = "sysui_qs_tiles";
+ private static final Validator QS_TILES_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] tiles = value.split(",");
+ boolean valid = true;
+ for (String tile : tiles) {
+ // tile can be any non-empty string as specified by OEM
+ valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
+ }
+ return valid;
+ }
+ };
+
/**
* Specifies whether the web action API is enabled.
*
@@ -7226,18 +7533,38 @@ public final class Settings {
*/
public static final String NOTIFICATION_BADGING = "notification_badging";
+ private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Comma separated list of QS tiles that have been auto-added already.
* @hide
*/
public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
+ private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] tiles = value.split(",");
+ boolean valid = true;
+ for (String tile : tiles) {
+ // tile can be any non-empty string as specified by OEM
+ valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
+ }
+ return valid;
+ }
+ };
+
/**
* Whether the Lockdown button should be shown in the power menu.
* @hide
*/
public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu";
+ private static final Validator LOCKDOWN_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR;
+
/**
* Backup manager behavioral parameters.
* This is encoded as a key=value list, separated by commas. Ex:
@@ -7277,8 +7604,6 @@ public final class Settings {
public static final String[] SETTINGS_TO_BACKUP = {
BUGREPORT_IN_POWER_MENU, // moved to global
ALLOW_MOCK_LOCATION,
- PARENTAL_CONTROL_ENABLED,
- PARENTAL_CONTROL_REDIRECT_URL,
USB_MASS_STORAGE_ENABLED, // moved to global
ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
ACCESSIBILITY_DISPLAY_DALTONIZER,
@@ -7310,12 +7635,9 @@ public final class Settings {
ACCESSIBILITY_CAPTIONING_TYPEFACE,
ACCESSIBILITY_CAPTIONING_FONT_SCALE,
ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
- TTS_USE_DEFAULTS,
TTS_DEFAULT_RATE,
TTS_DEFAULT_PITCH,
TTS_DEFAULT_SYNTH,
- TTS_DEFAULT_LANG,
- TTS_DEFAULT_COUNTRY,
TTS_ENABLED_PLUGINS,
TTS_DEFAULT_LOCALE,
SHOW_IME_WITH_HARD_KEYBOARD,
@@ -7371,7 +7693,158 @@ public final class Settings {
SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
};
- /** @hide */
+ /**
+ * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
+ * @hide
+ */
+ public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
+ static {
+ VALIDATORS.put(BUGREPORT_IN_POWER_MENU, BUGREPORT_IN_POWER_MENU_VALIDATOR);
+ VALIDATORS.put(ALLOW_MOCK_LOCATION, ALLOW_MOCK_LOCATION_VALIDATOR);
+ VALIDATORS.put(USB_MASS_STORAGE_ENABLED, USB_MASS_STORAGE_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+ ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER,
+ ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+ ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR);
+ VALIDATORS.put(AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR);
+ VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES,
+ ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR);
+ VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR);
+ VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR);
+ VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR);
+ VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_ENABLED, ACCESSIBILITY_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ENABLED,
+ ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+ ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_SPEAK_PASSWORD, ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+ ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_PRESET,
+ ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_ENABLED,
+ ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_LOCALE,
+ ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR,
+ ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR,
+ ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_TYPE,
+ ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_COLOR,
+ ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_TYPEFACE,
+ ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FONT_SCALE,
+ ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
+ ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_RATE, TTS_DEFAULT_RATE_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_PITCH, TTS_DEFAULT_PITCH_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_SYNTH, TTS_DEFAULT_SYNTH_VALIDATOR);
+ VALIDATORS.put(TTS_ENABLED_PLUGINS, TTS_ENABLED_PLUGINS_VALIDATOR);
+ VALIDATORS.put(TTS_DEFAULT_LOCALE, TTS_DEFAULT_LOCALE_VALIDATOR);
+ VALIDATORS.put(SHOW_IME_WITH_HARD_KEYBOARD, SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR);
+ VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+ WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
+ VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+ WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR);
+ VALIDATORS.put(WIFI_NUM_OPEN_NETWORKS_KEPT, WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR);
+ VALIDATORS.put(SELECTED_SPELL_CHECKER, SELECTED_SPELL_CHECKER_VALIDATOR);
+ VALIDATORS.put(SELECTED_SPELL_CHECKER_SUBTYPE,
+ SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR);
+ VALIDATORS.put(SPELL_CHECKER_ENABLED, SPELL_CHECKER_ENABLED_VALIDATOR);
+ VALIDATORS.put(MOUNT_PLAY_NOTIFICATION_SND, MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR);
+ VALIDATORS.put(MOUNT_UMS_AUTOSTART, MOUNT_UMS_AUTOSTART_VALIDATOR);
+ VALIDATORS.put(MOUNT_UMS_PROMPT, MOUNT_UMS_PROMPT_VALIDATOR);
+ VALIDATORS.put(MOUNT_UMS_NOTIFY_ENABLED, MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR);
+ VALIDATORS.put(SLEEP_TIMEOUT, SLEEP_TIMEOUT_VALIDATOR);
+ VALIDATORS.put(DOUBLE_TAP_TO_WAKE, DOUBLE_TAP_TO_WAKE_VALIDATOR);
+ VALIDATORS.put(WAKE_GESTURE_ENABLED, WAKE_GESTURE_ENABLED_VALIDATOR);
+ VALIDATORS.put(LONG_PRESS_TIMEOUT, LONG_PRESS_TIMEOUT_VALIDATOR);
+ VALIDATORS.put(CAMERA_GESTURE_DISABLED, CAMERA_GESTURE_DISABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_ENABLED,
+ ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_DELAY, ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_LARGE_POINTER_ICON,
+ ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR);
+ VALIDATORS.put(PREFERRED_TTY_MODE, PREFERRED_TTY_MODE_VALIDATOR);
+ VALIDATORS.put(ENHANCED_VOICE_PRIVACY_ENABLED,
+ ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR);
+ VALIDATORS.put(TTY_MODE_ENABLED, TTY_MODE_ENABLED_VALIDATOR);
+ VALIDATORS.put(INCALL_POWER_BUTTON_BEHAVIOR, INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_START_TIME,
+ NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_END_TIME, NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR);
+ VALIDATORS.put(NIGHT_DISPLAY_AUTO_MODE, NIGHT_DISPLAY_AUTO_MODE_VALIDATOR);
+ VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR);
+ VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+ CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR);
+ VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
+ CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR);
+ VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED,
+ SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR);
+ VALIDATORS.put(QS_TILES, QS_TILES_VALIDATOR);
+ VALIDATORS.put(DOZE_ENABLED, DOZE_ENABLED_VALIDATOR);
+ VALIDATORS.put(DOZE_PULSE_ON_PICK_UP, DOZE_PULSE_ON_PICK_UP_VALIDATOR);
+ VALIDATORS.put(DOZE_PULSE_ON_DOUBLE_TAP, DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR);
+ VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
+ VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
+ AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_SENSITIVITY, ASSIST_GESTURE_SENSITIVITY_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
+ ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
+ VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR);
+ VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR);
+ VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR);
+ VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_COMPONENTS, SCREENSAVER_COMPONENTS_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_DOCK, SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR);
+ VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_SLEEP, SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR);
+ VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR);
+ VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR);
+ VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS,
+ ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting
+ VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT,
+ ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR); //legacy restore setting
+ VALIDATORS.put(ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+ ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR); //legacy restore setting
+ }
+
+ /**
+ * Keys we no longer back up under the current schema, but want to continue to
+ * process when restoring historical backup datasets.
+ *
+ * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
+ * @hide
+ */
public static final String[] LEGACY_RESTORE_SETTINGS = {
ENABLED_NOTIFICATION_LISTENERS,
ENABLED_NOTIFICATION_ASSISTANT,
@@ -8706,6 +9179,9 @@ public final class Settings {
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
"wifi_networks_available_repeat_delay";
+ private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* 802.11 country code in ISO 3166 format
* @hide
@@ -8735,6 +9211,9 @@ public final class Settings {
*/
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+ private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+ NON_NEGATIVE_INTEGER_VALIDATOR;
+
/**
* Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this.
*/
@@ -9649,7 +10128,8 @@ public final class Settings {
* This is encoded as a key=value list, separated by commas. Ex:
*
* "battery_tip_enabled=true,summary_enabled=true,high_usage_enabled=true,"
- * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50"
+ * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50,"
+ * "high_usage_battery_draining=25,high_usage_period_ms=3000"
*
* The following keys are supported:
*
@@ -9659,6 +10139,8 @@ public final class Settings {
* battery_saver_tip_enabled (boolean)
* high_usage_enabled (boolean)
* high_usage_app_count (int)
+ * high_usage_period_ms (long)
+ * high_usage_battery_draining (int)
* app_restriction_enabled (boolean)
* reduced_battery_enabled (boolean)
* reduced_battery_percent (int)
@@ -9869,6 +10351,8 @@ public final class Settings {
* The following keys are supported:
* <pre>
* track_cpu_times_by_proc_state (boolean)
+ * track_cpu_active_cluster_time (boolean)
+ * read_binary_cpu_time (boolean)
* </pre>
*
* <p>
@@ -9896,6 +10380,15 @@ public final class Settings {
public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
/**
+ * Whether or not to enable Forced App Standby on small battery devices.
+ * Type: int (0 for false, 1 for true)
+ * Default: 0
+ * @hide
+ */
+ public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
+ = "forced_app_standby_for_small_battery_enabled";
+
+ /**
* Whether or not Network Watchlist feature is enabled.
* Type: int (0 for false, 1 for true)
* Default: 0
@@ -10780,7 +11273,15 @@ public final class Settings {
LOCATION_GLOBAL_KILL_SWITCH,
};
- /** @hide */
+ /**
+ * Keys we no longer back up under the current schema, but want to continue to
+ * process when restoring historical backup datasets.
+ *
+ * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+ * otherwise they won't be restored.
+ *
+ * @hide
+ */
public static final String[] LEGACY_RESTORE_SETTINGS = {
};
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java
index 22ef3b6f346f..84c9e8867c44 100644
--- a/core/java/android/provider/SettingsValidators.java
+++ b/core/java/android/provider/SettingsValidators.java
@@ -21,6 +21,8 @@ import android.net.Uri;
import com.android.internal.util.ArrayUtils;
+import java.util.Locale;
+
/**
* This class provides both interface for validation and common validators
* used to ensure Settings have meaningful values.
@@ -50,6 +52,18 @@ public class SettingsValidators {
}
};
+ public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ try {
+ Integer.parseInt(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
+
public static final Validator URI_VALIDATOR = new Validator() {
@Override
public boolean validate(String value) {
@@ -80,7 +94,10 @@ public class SettingsValidators {
// and underscores ('_'). However, individual package name parts may only
// start with letters.
// (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
- String[] subparts = value.split(".");
+ if (value == null) {
+ return false;
+ }
+ String[] subparts = value.split("\\.");
boolean isValidPackageName = true;
for (String subpart : subparts) {
isValidPackageName |= isSubpartValidForPackageName(subpart);
@@ -106,10 +123,29 @@ public class SettingsValidators {
@Override
public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
return value.length() <= MAX_IPV6_LENGTH;
}
};
+ public static final Validator LOCALE_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ Locale[] validLocales = Locale.getAvailableLocales();
+ for (Locale locale : validLocales) {
+ if (value.equals(locale.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
public interface Validator {
boolean validate(String value);
}
@@ -166,4 +202,48 @@ public class SettingsValidators {
}
}
}
+
+ public static final class ComponentNameListValidator implements Validator {
+ private final String mSeparator;
+
+ public ComponentNameListValidator(String separator) {
+ mSeparator = separator;
+ }
+
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] elements = value.split(mSeparator);
+ for (String element : elements) {
+ if (!COMPONENT_NAME_VALIDATOR.validate(element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ public static final class PackageNameListValidator implements Validator {
+ private final String mSeparator;
+
+ public PackageNameListValidator(String separator) {
+ mSeparator = separator;
+ }
+
+ @Override
+ public boolean validate(String value) {
+ if (value == null) {
+ return false;
+ }
+ String[] elements = value.split(mSeparator);
+ for (String element : elements) {
+ if (!PACKAGE_NAME_VALIDATOR.validate(element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f409e5b74d23..72afbb8414f6 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -73,6 +73,7 @@ public final class KeymasterDefs {
public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
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_ALL_APPLICATIONS = KM_BOOL | 600;
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 18f6dab9fc59..df63a91790d8 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -15,9 +15,6 @@
*/
package android.service.autofill;
-import static android.view.autofill.AutofillManager.EXTRA_AVAILABLE_ALGORITHMS;
-import static android.view.autofill.AutofillManager.EXTRA_DEFAULT_ALGORITHM;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -58,9 +55,7 @@ public abstract class AutofillFieldClassificationService extends Service {
private static final String TAG = "AutofillFieldClassificationService";
- private static final int MSG_GET_AVAILABLE_ALGORITHMS = 1;
- private static final int MSG_GET_DEFAULT_ALGORITHM = 2;
- private static final int MSG_GET_SCORES = 3;
+ private static final int MSG_GET_SCORES = 1;
/**
* The {@link Intent} action that must be declared as handled by a service
@@ -69,6 +64,20 @@ public abstract class AutofillFieldClassificationService extends Service {
public static final String SERVICE_INTERFACE =
"android.service.autofill.AutofillFieldClassificationService";
+ /**
+ * Manifest metadata key for the resource string containing the name of the default field
+ * classification algorithm.
+ */
+ public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM =
+ "android.autofill.field_classification.default_algorithm";
+ /**
+ * Manifest metadata key for the resource string array containing the names of all field
+ * classification algorithms provided by the service.
+ */
+ public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
+ "android.autofill.field_classification.available_algorithms";
+
+
/** {@hide} **/
public static final String EXTRA_SCORES = "scores";
@@ -79,21 +88,6 @@ public abstract class AutofillFieldClassificationService extends Service {
final Bundle data = new Bundle();
final RemoteCallback callback;
switch (action) {
- case MSG_GET_AVAILABLE_ALGORITHMS:
- callback = (RemoteCallback) msg.obj;
- final List<String> availableAlgorithms = onGetAvailableAlgorithms();
- String[] asArray = null;
- if (availableAlgorithms != null) {
- asArray = new String[availableAlgorithms.size()];
- availableAlgorithms.toArray(asArray);
- }
- data.putStringArray(EXTRA_AVAILABLE_ALGORITHMS, asArray);
- break;
- case MSG_GET_DEFAULT_ALGORITHM:
- callback = (RemoteCallback) msg.obj;
- final String defaultAlgorithm = onGetDefaultAlgorithm();
- data.putString(EXTRA_DEFAULT_ALGORITHM, defaultAlgorithm);
- break;
case MSG_GET_SCORES:
final SomeArgs args = (SomeArgs) msg.obj;
callback = (RemoteCallback) args.arg1;
@@ -103,9 +97,11 @@ public abstract class AutofillFieldClassificationService extends Service {
final List<AutofillValue> actualValues = ((List<AutofillValue>) args.arg4);
@SuppressWarnings("unchecked")
final String[] userDataValues = (String[]) args.arg5;
- final Scores scores = onGetScores(algorithmName, algorithmArgs, actualValues,
+ final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
Arrays.asList(userDataValues));
- data.putParcelable(EXTRA_SCORES, scores);
+ if (scores != null) {
+ data.putParcelable(EXTRA_SCORES, new Scores(scores));
+ }
break;
default:
Log.w(TAG, "Handling unknown message: " + action);
@@ -134,27 +130,6 @@ public abstract class AutofillFieldClassificationService extends Service {
}
/**
- * Gets the name of all available algorithms.
- *
- * @throws UnsupportedOperationException if not implemented by service.
- */
- // TODO(b/70939974): rename to onGetAvailableAlgorithms if not removed
- @NonNull
- public List<String> onGetAvailableAlgorithms() {
- throw new UnsupportedOperationException("Must be implemented by external service");
- }
-
- /**
- * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
- *
- * @throws UnsupportedOperationException if not implemented by service.
- */
- @NonNull
- public String onGetDefaultAlgorithm() {
- throw new UnsupportedOperationException("Must be implemented by external service");
- }
-
- /**
* Calculates field classification scores in a batch.
*
* <p>See {@link AutofillFieldClassificationService} for more info about field classification
@@ -165,31 +140,22 @@ public abstract class AutofillFieldClassificationService extends Service {
* @param args optional arguments to be passed to the algorithm.
* @param actualValues values entered by the user.
* @param userDataValues values predicted from the user data.
- * @return the calculated scores and the algorithm used.
+ * @return the calculated scores, with the first dimension representing actual values and the
+ * second dimension values from {@link UserData}.
*
* {@hide}
*/
@Nullable
@SystemApi
- public Scores onGetScores(@Nullable String algorithm,
+ public float[][] onGetScores(@Nullable String algorithm,
@Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
- throw new UnsupportedOperationException("Must be implemented by external service");
+ Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
+ return null;
}
private final class AutofillFieldClassificationServiceWrapper
extends IAutofillFieldClassificationService.Stub {
-
- @Override
- public void getAvailableAlgorithms(RemoteCallback callback) throws RemoteException {
- mHandlerCaller.obtainMessageO(MSG_GET_AVAILABLE_ALGORITHMS, callback).sendToTarget();
- }
-
- @Override
- public void getDefaultAlgorithm(RemoteCallback callback) throws RemoteException {
- mHandlerCaller.obtainMessageO(MSG_GET_DEFAULT_ALGORITHM, callback).sendToTarget();
- }
-
@Override
public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
List<AutofillValue> actualValues, String[] userDataValues)
@@ -200,52 +166,27 @@ public abstract class AutofillFieldClassificationService extends Service {
}
}
-
- // TODO(b/70939974): it might be simpler to remove this class and return the float[][] directly,
- // ignoring the request if the algorithm name is invalid.
/**
- * Represents field classification scores used in a batch calculation.
+ * Helper class used to encapsulate a float[][] in a Parcelable.
*
* {@hide}
*/
- @SystemApi
public static final class Scores implements Parcelable {
- private final String mAlgorithmName;
- private final float[][] mScores;
-
- /* @hide */
- public Scores(String algorithmName, int size1, int size2) {
- mAlgorithmName = algorithmName;
- mScores = new float[size1][size2];
- }
+ public final float[][] scores;
- public Scores(Parcel parcel) {
- mAlgorithmName = parcel.readString();
+ private Scores(Parcel parcel) {
final int size1 = parcel.readInt();
final int size2 = parcel.readInt();
- mScores = new float[size1][size2];
+ scores = new float[size1][size2];
for (int i = 0; i < size1; i++) {
for (int j = 0; j < size2; j++) {
- mScores[i][j] = parcel.readFloat();
+ scores[i][j] = parcel.readFloat();
}
}
}
- /**
- * Gets the name of algorithm used to calculate the score.
- */
- @NonNull
- public String getAlgorithm() {
- return mAlgorithmName;
- }
-
- /**
- * Gets the resulting scores, with the 1st dimension representing actual values and the 2nd
- * dimension values from {@link UserData}.
- */
- @NonNull
- public float[][] getScores() {
- return mScores;
+ private Scores(float[][] scores) {
+ this.scores = scores;
}
@Override
@@ -255,20 +196,18 @@ public abstract class AutofillFieldClassificationService extends Service {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeString(mAlgorithmName);
- int size1 = mScores.length;
- int size2 = mScores[0].length;
+ int size1 = scores.length;
+ int size2 = scores[0].length;
parcel.writeInt(size1);
parcel.writeInt(size2);
for (int i = 0; i < size1; i++) {
for (int j = 0; j < size2; j++) {
- parcel.writeFloat(mScores[i][j]);
+ parcel.writeFloat(scores[i][j]);
}
}
}
public static final Creator<Scores> CREATOR = new Creator<Scores>() {
-
@Override
public Scores createFromParcel(Parcel parcel) {
return new Scores(parcel);
@@ -278,7 +217,6 @@ public abstract class AutofillFieldClassificationService extends Service {
public Scores[] newArray(int size) {
return new Scores[size];
}
-
};
}
}
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 536180335a83..cd1efd68df6f 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -105,21 +105,16 @@ public final class FieldClassification {
/**
* Represents the score of a {@link UserData} entry for the field.
- *
- * <p>The score is calculated by the given {@link #getAlgorithm() algorithm} and
- * the entry is identified by {@link #getRemoteId()}.
*/
public static final class Match {
private final String mRemoteId;
private final float mScore;
- private final String mAlgorithm;
/** @hide */
- public Match(String remoteId, float score, String algorithm) {
+ public Match(String remoteId, float score) {
mRemoteId = Preconditions.checkNotNull(remoteId);
mScore = score;
- mAlgorithm = algorithm;
}
/**
@@ -150,38 +145,22 @@ public final class FieldClassification {
return mScore;
}
- /**
- * Gets the algorithm used to calculate this score.
- *
- * <p>Typically, this is either the algorithm set by
- * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)},
- * or the
- * {@link android.view.autofill.AutofillManager#getDefaultFieldClassificationAlgorithm()}.
- */
- @NonNull
- public String getAlgorithm() {
- return mAlgorithm;
- }
-
@Override
public String toString() {
if (!sDebug) return super.toString();
final StringBuilder string = new StringBuilder("Match: remoteId=");
Helper.appendRedacted(string, mRemoteId);
- return string.append(", score=").append(mScore)
- .append(", algorithm=").append(mAlgorithm)
- .toString();
+ return string.append(", score=").append(mScore).toString();
}
private void writeToParcel(@NonNull Parcel parcel) {
parcel.writeString(mRemoteId);
parcel.writeFloat(mScore);
- parcel.writeString(mAlgorithm);
}
private static Match readFromParcel(@NonNull Parcel parcel) {
- return new Match(parcel.readString(), parcel.readFloat(), parcel.readString());
+ return new Match(parcel.readString(), parcel.readFloat());
}
}
}
diff --git a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
index d8e829d8f67c..398557d5ad2e 100644
--- a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
+++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
@@ -27,8 +27,6 @@ import java.util.List;
* @hide
*/
oneway interface IAutofillFieldClassificationService {
- void getAvailableAlgorithms(in RemoteCallback callback);
- void getDefaultAlgorithm(in RemoteCallback callback);
void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs,
- in List<AutofillValue> actualValues, in String[] userDataValues);
+ in List<AutofillValue> actualValues, in String[] userDataValues);
}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 2f9225acc520..9017848055e2 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -155,12 +155,9 @@ public final class UserData implements Parcelable {
* <p>The currently available algorithms can be retrieve through
* {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
*
- * <p><b>Note: </b>The available algorithms in the Android System can change dinamically,
- * so it's not guaranteed that the algorithm set here is the one that will be effectually
- * used. If the algorithm set here is not available at runtime, the
- * {@link AutofillManager#getDefaultFieldClassificationAlgorithm()} is used instead.
- * You can verify which algorithm was used by calling
- * {@link FieldClassification.Match#getAlgorithm()}.
+ * <p>If not set, the
+ * {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is
+ * used instead.
*
* @param name name of the algorithm or {@code null} to used default.
* @param args optional arguments to the algorithm.
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 908ef55a6ae9..3b4eea76b390 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -16,71 +16,105 @@
package android.text.style;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * A span that changes the size of the text it's attached to.
+ * <p>
+ * For example, the size of the text can be changed to 55dp like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with absolute size span");
+ *string.setSpan(new AbsoluteSizeSpan(55, true), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/absolutesizespan.png" />
+ * <figcaption>Text with text size updated.</figcaption>
+ */
public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mSize;
- private boolean mDip;
+ private final boolean mDip;
/**
* Set the text size to <code>size</code> physical pixels.
*/
public AbsoluteSizeSpan(int size) {
- mSize = size;
+ this(size, false);
}
/**
- * Set the text size to <code>size</code> physical pixels,
- * or to <code>size</code> device-independent pixels if
- * <code>dip</code> is true.
+ * Set the text size to <code>size</code> physical pixels, or to <code>size</code>
+ * device-independent pixels if <code>dip</code> is true.
*/
public AbsoluteSizeSpan(int size, boolean dip) {
mSize = size;
mDip = dip;
}
- public AbsoluteSizeSpan(Parcel src) {
+ /**
+ * Creates an {@link AbsoluteSizeSpan} from a parcel.
+ */
+ public AbsoluteSizeSpan(@NonNull Parcel src) {
mSize = src.readInt();
mDip = src.readInt() != 0;
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.ABSOLUTE_SIZE_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.writeInt(mSize);
dest.writeInt(mDip ? 1 : 0);
}
+ /**
+ * Get the text size. This is in physical pixels if {@link #getDip()} returns false or in
+ * device-independent pixels if {@link #getDip()} returns true.
+ *
+ * @return the text size, either in physical pixels or device-independent pixels.
+ * @see AbsoluteSizeSpan#AbsoluteSizeSpan(int, boolean)
+ */
public int getSize() {
return mSize;
}
+ /**
+ * Returns whether the size is in device-independent pixels or not, depending on the
+ * <code>dip</code> flag passed in {@link #AbsoluteSizeSpan(int, boolean)}
+ *
+ * @return <code>true</code> if the size is in device-independent pixels, <code>false</code>
+ * otherwise
+ *
+ * @see #AbsoluteSizeSpan(int, boolean)
+ */
public boolean getDip() {
return mDip;
}
@Override
- public void updateDrawState(TextPaint ds) {
+ public void updateDrawState(@NonNull TextPaint ds) {
if (mDip) {
ds.setTextSize(mSize * ds.density);
} else {
@@ -89,7 +123,7 @@ public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableS
}
@Override
- public void updateMeasureState(TextPaint ds) {
+ public void updateMeasureState(@NonNull TextPaint ds) {
if (mDip) {
ds.setTextSize(mSize * ds.density);
} else {
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index de05f50c9860..44e35615ca55 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -16,52 +16,88 @@
package android.text.style;
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * Changes the background color of the text to which the span is attached.
+ * <p>
+ * For example, to set a green background color for a text you would create a {@link
+ * android.text.SpannableString} based on the text and set the span.
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with a background color span");
+ *string.setSpan(new BackgroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" />
+ * <figcaption>Set a background color for the text.</figcaption>
+ */
public class BackgroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
private final int mColor;
- public BackgroundColorSpan(int color) {
+ /**
+ * Creates a {@link BackgroundColorSpan} from a color integer.
+ * <p>
+ *
+ * @param color color integer that defines the background color
+ * @see android.content.res.Resources#getColor(int, Resources.Theme)
+ */
+ public BackgroundColorSpan(@ColorInt int color) {
mColor = color;
}
- public BackgroundColorSpan(Parcel src) {
+ /**
+ * Creates a {@link BackgroundColorSpan} from a parcel.
+ */
+ public BackgroundColorSpan(@NonNull Parcel src) {
mColor = src.readInt();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.BACKGROUND_COLOR_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.writeInt(mColor);
}
+ /**
+ * @return the background color of this span.
+ * @see BackgroundColorSpan#BackgroundColorSpan(int)
+ */
+ @ColorInt
public int getBackgroundColor() {
return mColor;
}
+ /**
+ * Updates the background color of the TextPaint.
+ */
@Override
- public void updateDrawState(TextPaint ds) {
- ds.bgColor = mColor;
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ textPaint.bgColor = mColor;
}
}
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index 2bc6d5406ade..f770674503f2 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -17,53 +17,88 @@
package android.text.style;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * Changes the color of the text to which the span is attached.
+ * <p>
+ * For example, to set a green text color you would create a {@link
+ * android.text.SpannableString} based on the text and set the span.
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with a foreground color span");
+ *string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" />
+ * <figcaption>Set a text color.</figcaption>
+ */
public class ForegroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
private final int mColor;
+ /**
+ * Creates a {@link ForegroundColorSpan} from a color integer.
+ * <p>
+ * To get the color integer associated with a particular color resource ID, use
+ * {@link android.content.res.Resources#getColor(int, Resources.Theme)}
+ *
+ * @param color color integer that defines the text color
+ */
public ForegroundColorSpan(@ColorInt int color) {
mColor = color;
}
- public ForegroundColorSpan(Parcel src) {
+ /**
+ * Creates a {@link ForegroundColorSpan} from a parcel.
+ */
+ public ForegroundColorSpan(@NonNull Parcel src) {
mColor = src.readInt();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.FOREGROUND_COLOR_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.writeInt(mColor);
}
+ /**
+ * @return the foreground color of this span.
+ * @see ForegroundColorSpan#ForegroundColorSpan(int)
+ */
@ColorInt
public int getForegroundColor() {
return mColor;
}
+ /**
+ * Updates the color of the TextPaint to the foreground color.
+ */
@Override
- public void updateDrawState(TextPaint ds) {
- ds.setColor(mColor);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ textPaint.setColor(mColor);
}
}
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 95f048a2e240..3094f27ab72d 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -16,56 +16,85 @@
package android.text.style;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * Uniformly scales the size of the text to which it's attached by a certain proportion.
+ * <p>
+ * For example, a <code>RelativeSizeSpan</code> that increases the text size by 50% can be
+ * constructed like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with relative size span");
+ *string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/relativesizespan.png" />
+ * <figcaption>Text increased by 50% with <code>RelativeSizeSpan</code>.</figcaption>
+ */
public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final float mProportion;
- public RelativeSizeSpan(float proportion) {
+ /**
+ * Creates a {@link RelativeSizeSpan} based on a proportion.
+ *
+ * @param proportion the proportion with which the text is scaled.
+ */
+ public RelativeSizeSpan(@FloatRange(from = 0) float proportion) {
mProportion = proportion;
}
- public RelativeSizeSpan(Parcel src) {
+ /**
+ * Creates a {@link RelativeSizeSpan} from a parcel.
+ */
+ public RelativeSizeSpan(@NonNull Parcel src) {
mProportion = src.readFloat();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.RELATIVE_SIZE_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.writeFloat(mProportion);
}
+ /**
+ * @return the proportion with which the text size is changed.
+ */
public float getSizeChange() {
return mProportion;
}
@Override
- public void updateDrawState(TextPaint ds) {
+ public void updateDrawState(@NonNull TextPaint ds) {
ds.setTextSize(ds.getTextSize() * mProportion);
}
@Override
- public void updateMeasureState(TextPaint ds) {
+ public void updateMeasureState(@NonNull TextPaint ds) {
ds.setTextSize(ds.getTextSize() * mProportion);
}
}
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index d085018572ec..6ef4ceccced1 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -16,45 +16,79 @@
package android.text.style;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * Scales horizontally the size of the text to which it's attached by a certain factor.
+ * <p>
+ * Values > 1.0 will stretch the text wider. Values < 1.0 will stretch the text narrower.
+ * <p>
+ * For example, a <code>ScaleXSpan</code> that stretches the text size by 100% can be
+ * constructed like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with ScaleX span");
+ *string.setSpan(new ScaleXSpan(2f), 10, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/scalexspan.png" />
+ * <figcaption>Text scaled by 100% with <code>ScaleXSpan</code>.</figcaption>
+ */
public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan {
private final float mProportion;
- public ScaleXSpan(float proportion) {
+ /**
+ * Creates a {@link ScaleXSpan} based on a proportion. Values > 1.0 will stretch the text wider.
+ * Values < 1.0 will stretch the text narrower.
+ *
+ * @param proportion the horizontal scale factor.
+ */
+ public ScaleXSpan(@FloatRange(from = 0) float proportion) {
mProportion = proportion;
}
- public ScaleXSpan(Parcel src) {
+ /**
+ * Creates a {@link ScaleXSpan} from a parcel.
+ */
+ public ScaleXSpan(@NonNull Parcel src) {
mProportion = src.readFloat();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.SCALE_X_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.writeFloat(mProportion);
}
+ /**
+ * Get the horizontal scale factor for the text.
+ *
+ * @return the horizontal scale factor.
+ */
public float getScaleX() {
return mProportion;
}
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 1389704f6ad8..a6305050656a 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -16,42 +16,65 @@
package android.text.style;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * A span that strikes through the text it's attached to.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with strikethrough span");
+ *string.setSpan(new StrikethroughSpan(), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/strikethroughspan.png" />
+ * <figcaption>Strikethrough text.</figcaption>
+ */
public class StrikethroughSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
+
+ /**
+ * Creates a {@link StrikethroughSpan}.
+ */
public StrikethroughSpan() {
}
-
- public StrikethroughSpan(Parcel src) {
+
+ /**
+ * Creates a {@link StrikethroughSpan} from a parcel.
+ */
+ public StrikethroughSpan(@NonNull Parcel src) {
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.STRIKETHROUGH_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) {
}
@Override
- public void updateDrawState(TextPaint ds) {
+ public void updateDrawState(@NonNull TextPaint ds) {
ds.setStrikeThruText(true);
}
}
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index f1b0d38cf624..3d15aad662b1 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -16,46 +16,74 @@
package android.text.style;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * The span that moves the position of the text baseline lower.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("☕- C8H10N4O2\n");
+ *string.setSpan(new SubscriptSpan(), 4, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 6, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 9, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 11, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/subscriptspan.png" />
+ * <figcaption>Text with <code>SubscriptSpan</code>.</figcaption>
+ * Note: Since the span affects the position of the text, if the text is on the last line of a
+ * TextView, it may appear cut.
+ */
public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
+
+ /**
+ * Creates a {@link SubscriptSpan}.
+ */
public SubscriptSpan() {
}
-
- public SubscriptSpan(Parcel src) {
+
+ /**
+ * Creates a {@link SubscriptSpan} from a parcel.
+ */
+ public SubscriptSpan(@NonNull Parcel src) {
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.SUBSCRIPT_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
+ @Override
public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
- public void updateDrawState(TextPaint tp) {
- tp.baselineShift -= (int) (tp.ascent() / 2);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ textPaint.baselineShift -= (int) (textPaint.ascent() / 2);
}
@Override
- public void updateMeasureState(TextPaint tp) {
- tp.baselineShift -= (int) (tp.ascent() / 2);
+ public void updateMeasureState(@NonNull TextPaint textPaint) {
+ textPaint.baselineShift -= (int) (textPaint.ascent() / 2);
}
}
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index abcf688f10ff..3dc9d3fdfe05 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -16,46 +16,71 @@
package android.text.style;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * The span that moves the position of the text baseline higher.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("1st example");
+ *string.setSpan(new SuperscriptSpan(), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/superscriptspan.png" />
+ * <figcaption>Text with <code>SuperscriptSpan</code>.</figcaption>
+ * Note: Since the span affects the position of the text, if the text is on the first line of a
+ * TextView, it may appear cut. This can be avoided by decreasing the text size with an {@link
+ * AbsoluteSizeSpan}
+ */
public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
+ /**
+ * Creates a {@link SuperscriptSpan}.
+ */
public SuperscriptSpan() {
}
-
- public SuperscriptSpan(Parcel src) {
+
+ /**
+ * Creates a {@link SuperscriptSpan} from a parcel.
+ */
+ public SuperscriptSpan(@NonNull Parcel src) {
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.SUPERSCRIPT_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) {
}
@Override
- public void updateDrawState(TextPaint tp) {
- tp.baselineShift += (int) (tp.ascent() / 2);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ textPaint.baselineShift += (int) (textPaint.ascent() / 2);
}
@Override
- public void updateMeasureState(TextPaint tp) {
- tp.baselineShift += (int) (tp.ascent() / 2);
+ public void updateMeasureState(@NonNull TextPaint textPaint) {
+ textPaint.baselineShift += (int) (textPaint.ascent() / 2);
}
}
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 9024dcd39256..800838ef92e9 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -16,42 +16,65 @@
package android.text.style;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
+/**
+ * A span that underlines the text it's attached to.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with underline span");
+ *string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/underlinespan.png" />
+ * <figcaption>Underlined text.</figcaption>
+ */
public class UnderlineSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
+
+ /**
+ * Creates an {@link UnderlineSpan}.
+ */
public UnderlineSpan() {
}
-
- public UnderlineSpan(Parcel src) {
+
+ /**
+ * Creates an {@link UnderlineSpan} from a parcel.
+ */
+ public UnderlineSpan(@NonNull Parcel src) {
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.UNDERLINE_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) {
}
@Override
- public void updateDrawState(TextPaint ds) {
+ public void updateDrawState(@NonNull TextPaint ds) {
ds.setUnderlineText(true);
}
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index e94f91a12905..4e98d9b09318 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -43,7 +43,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_battery_v2", "false");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
- DEFAULT_FLAGS.put("settings_zone_picker_v2", "false");
+ DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index ce8998fcb863..5a09dab552e3 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -16,6 +16,7 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -42,6 +43,7 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -105,7 +107,8 @@ public class ApkSignatureSchemeV2Verifier {
*/
public static X509Certificate[][] verify(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
- return verify(apkFile, true);
+ VerifiedSigner vSigner = verify(apkFile, true);
+ return vSigner.certs;
}
/**
@@ -119,10 +122,11 @@ public class ApkSignatureSchemeV2Verifier {
*/
public static X509Certificate[][] plsCertsNoVerifyOnlyCerts(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
- return verify(apkFile, false);
+ VerifiedSigner vSigner = verify(apkFile, false);
+ return vSigner.certs;
}
- private static X509Certificate[][] verify(String apkFile, boolean verifyIntegrity)
+ private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
return verify(apk, verifyIntegrity);
@@ -138,7 +142,7 @@ public class ApkSignatureSchemeV2Verifier {
* verify.
* @throws IOException if an I/O error occurs while reading the APK file.
*/
- private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity)
+ private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
SignatureInfo signatureInfo = findSignature(apk);
return verify(apk, signatureInfo, verifyIntegrity);
@@ -163,7 +167,7 @@ public class ApkSignatureSchemeV2Verifier {
* @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it
* against the APK file.
*/
- private static X509Certificate[][] verify(
+ private static VerifiedSigner verify(
RandomAccessFile apk,
SignatureInfo signatureInfo,
boolean doVerifyIntegrity) throws SecurityException, IOException {
@@ -207,7 +211,14 @@ public class ApkSignatureSchemeV2Verifier {
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
- return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
+ byte[] verityRootHash = null;
+ if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
+ verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ }
+
+ return new VerifiedSigner(
+ signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
+ verityRootHash);
}
private static X509Certificate[] verifySigner(
@@ -383,6 +394,24 @@ public class ApkSignatureSchemeV2Verifier {
return;
}
+ static byte[] getVerityRootHash(String apkPath)
+ throws IOException, SignatureNotFoundException, SecurityException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ VerifiedSigner vSigner = verify(apk, false);
+ return vSigner.verityRootHash;
+ }
+ }
+
+ static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ }
+ }
+
private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -400,4 +429,20 @@ public class ApkSignatureSchemeV2Verifier {
return false;
}
}
+
+ /**
+ * Verified APK Signature Scheme v2 signer.
+ *
+ * @hide for internal use only.
+ */
+ public static class VerifiedSigner {
+ public final X509Certificate[][] certs;
+ public final byte[] verityRootHash;
+
+ public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+ this.certs = certs;
+ this.verityRootHash = verityRootHash;
+ }
+
+ }
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index c9e67fe1c9ef..1b04eb2f7d44 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,6 +16,7 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -43,6 +44,7 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -211,6 +213,10 @@ public class ApkSignatureSchemeV3Verifier {
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
+ if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
+ result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+ }
+
return result;
}
@@ -499,6 +505,24 @@ public class ApkSignatureSchemeV3Verifier {
return new VerifiedProofOfRotation(certs, flagsList);
}
+ static byte[] getVerityRootHash(String apkPath)
+ throws IOException, SignatureNotFoundException, SecurityException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ VerifiedSigner vSigner = verify(apk, false);
+ return vSigner.verityRootHash;
+ }
+ }
+
+ static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ SignatureInfo signatureInfo = findSignature(apk);
+ return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ }
+ }
+
private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -541,6 +565,8 @@ public class ApkSignatureSchemeV3Verifier {
public final X509Certificate[] certs;
public final VerifiedProofOfRotation por;
+ public byte[] verityRootHash;
+
public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
this.certs = certs;
this.por = por;
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 555c4740389a..a2a76169c83a 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -36,7 +36,9 @@ import libcore.io.IoUtils;
import java.io.IOException;
import java.io.InputStream;
+import java.security.DigestException;
import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
@@ -367,4 +369,51 @@ public class ApkSignatureVerifier {
// v2 didn't work, try jarsigner
return verifyV1Signature(apkPath, false);
}
+
+ /**
+ * @return the verity root hash in the Signing Block.
+ */
+ public static byte[] getVerityRootHash(String apkPath)
+ throws IOException, SignatureNotFoundException, SecurityException {
+ // first try v3
+ try {
+ return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath);
+ } catch (SignatureNotFoundException e) {
+ // try older version
+ }
+ return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath);
+ }
+
+ /**
+ * Generates the Merkle tree and verity metadata to the buffer allocated by the {@code
+ * ByteBufferFactory}.
+ *
+ * @return the verity root hash of the generated Merkle tree.
+ */
+ public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ // first try v3
+ try {
+ return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
+ } catch (SignatureNotFoundException e) {
+ // try older version
+ }
+ return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory);
+ }
+
+ /**
+ * Result of a successful APK verification operation.
+ */
+ public static class Result {
+ public final Certificate[][] certs;
+ public final Signature[] sigs;
+ public final int signatureSchemeVersion;
+
+ public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
+ this.certs = certs;
+ this.sigs = sigs;
+ this.signatureSchemeVersion = signingVersion;
+ }
+ }
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 9d53847f8110..4146f6fab011 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -306,6 +306,26 @@ final class ApkSigningBlockUtils {
}
/**
+ * Generates the fsverity header and hash tree to be used by kernel for the given apk. This
+ * method does not check whether the root hash exists in the Signing Block or not.
+ *
+ * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
+ * ByteBufferFactory}.
+ *
+ * @return the root hash of the generated hash tree.
+ */
+ public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
+ SignatureInfo signatureInfo)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
+ signatureInfo, bufferFactory);
+ return result.rootHash;
+ }
+ }
+
+ /**
* Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
*
* @throws IOException if an I/O error occurs while reading the file.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 7412ef411fb4..a0d5e4c2dd8e 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -164,11 +164,11 @@ abstract class ApkVerityBuilder {
}
private void fillUpLastOutputChunk() {
- int extra = (int) (BUFFER_SIZE - mOutput.position() % BUFFER_SIZE);
- if (extra == 0) {
+ int lastBlockSize = (int) (mOutput.position() % BUFFER_SIZE);
+ if (lastBlockSize == 0) {
return;
}
- mOutput.put(ByteBuffer.allocate(extra));
+ mOutput.put(ByteBuffer.allocate(BUFFER_SIZE - lastBlockSize));
}
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 1f7f8b9a0c32..8830c90addac 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -283,6 +283,7 @@ public class Surface implements Parcelable {
*/
public long getNextFrameNumber() {
synchronized (mLock) {
+ checkNotReleasedLocked();
return nativeGetNextFrameNumber(mNativeObject);
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index f54561bfb423..4b24a71c8bfb 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -33,7 +33,6 @@ import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.FillEventHistory;
@@ -58,8 +57,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
// TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -177,11 +174,6 @@ public final class AutofillManager {
public static final String EXTRA_RESTORE_SESSION_TOKEN =
"android.view.autofill.extra.RESTORE_SESSION_TOKEN";
- /** @hide */
- public static final String EXTRA_AVAILABLE_ALGORITHMS = "available_algorithms";
- /** @hide */
- public static final String EXTRA_DEFAULT_ALGORITHM = "default_algorithm";
-
private static final String SESSION_ID_TAG = "android:sessionId";
private static final String STATE_TAG = "android:state";
private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -1174,22 +1166,10 @@ public final class AutofillManager {
* and it's ignored if the caller currently doesn't have an enabled autofill service for
* the user.
*/
- // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
- // the ExtService manifest (instead of calling the service)
@Nullable
public String getDefaultFieldClassificationAlgorithm() {
- final SyncRemoteCallbackListener<String> listener =
- new SyncRemoteCallbackListener<String>() {
-
- @Override
- String getResult(Bundle result) {
- return result == null ? null : result.getString(EXTRA_DEFAULT_ALGORITHM);
- }
- };
-
try {
- mService.getDefaultFieldClassificationAlgorithm(new RemoteCallback(listener));
- return listener.getResult(FC_SERVICE_TIMEOUT);
+ return mService.getDefaultFieldClassificationAlgorithm();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1204,29 +1184,12 @@ public final class AutofillManager {
* and it returns an empty list if the caller currently doesn't have an enabled autofill service
* for the user.
*/
- // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
- // the ExtService manifest (instead of calling the service)
@NonNull
public List<String> getAvailableFieldClassificationAlgorithms() {
- final SyncRemoteCallbackListener<List<String>> listener =
- new SyncRemoteCallbackListener<List<String>>() {
-
- @Override
- List<String> getResult(Bundle result) {
- List<String> algorithms = null;
- if (result != null) {
- final String[] asArray = result.getStringArray(EXTRA_AVAILABLE_ALGORITHMS);
- if (asArray != null) {
- algorithms = Arrays.asList(asArray);
- }
- }
- return algorithms != null ? algorithms : Collections.emptyList();
- }
- };
-
+ final String[] algorithms;
try {
- mService.getAvailableFieldClassificationAlgorithms(new RemoteCallback(listener));
- return listener.getResult(FC_SERVICE_TIMEOUT);
+ algorithms = mService.getAvailableFieldClassificationAlgorithms();
+ return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -2322,36 +2285,4 @@ public final class AutofillManager {
}
}
}
-
- private abstract static class SyncRemoteCallbackListener<T>
- implements RemoteCallback.OnResultListener {
-
- private final CountDownLatch mLatch = new CountDownLatch(1);
- private T mResult;
-
- @Override
- public void onResult(Bundle result) {
- if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener.onResult(): " + result);
- mResult = getResult(result);
- mLatch.countDown();
- }
-
- T getResult(int timeoutMs) {
- T result = null;
- try {
- if (mLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
- result = mResult;
- } else {
- Log.w(TAG, "SyncRemoteCallbackListener not called in " + timeoutMs + "ms");
- }
- } catch (InterruptedException e) {
- Log.w(TAG, "SyncRemoteCallbackListener interrupted: " + e);
- Thread.currentThread().interrupt();
- }
- if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener: returning " + result);
- return result;
- }
-
- abstract T getResult(Bundle result);
- }
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 41672e7aeb9b..1a11fbba0011 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -59,6 +59,6 @@ interface IAutoFillManager {
void setUserData(in UserData userData);
boolean isFieldClassificationEnabled();
ComponentName getAutofillServiceComponentName();
- void getAvailableFieldClassificationAlgorithms(in RemoteCallback callback);
- void getDefaultFieldClassificationAlgorithm(in RemoteCallback callback);
+ String[] getAvailableFieldClassificationAlgorithms();
+ String getDefaultFieldClassificationAlgorithm();
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 388180dc61e0..d3e807d99990 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -23,6 +23,8 @@ import android.net.wifi.WifiActivityEnergyInfo;
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
import android.os.health.HealthStatsParceler;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -91,6 +93,7 @@ interface IBatteryStats {
void noteVibratorOff(int uid);
void noteStartGps(int uid);
void noteStopGps(int uid);
+ void noteGpsSignalQuality(int signalLevel);
void noteScreenState(int state);
void noteScreenBrightness(int brightness);
void noteUserActivity(int uid, int event);
@@ -140,6 +143,12 @@ interface IBatteryStats {
/** {@hide} */
CellularBatteryStats getCellularBatteryStats();
+ /** {@hide} */
+ WifiBatteryStats getWifiBatteryStats();
+
+ /** {@hide} */
+ GpsBatteryStats getGpsBatteryStats();
+
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 15dc6f507093..5a59e70865e5 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -665,14 +665,14 @@ public class BatteryStatsHelper {
/**
* Calculate the baseline power usage for the device when it is in suspend and idle.
- * The device is drawing POWER_CPU_IDLE power at its lowest power state.
- * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held.
+ * The device is drawing POWER_CPU_SUSPEND power at its lowest power state.
+ * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held.
*/
private void addIdleUsage() {
final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) *
- mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
+ mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND);
final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) *
- mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+ mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000);
if (DEBUG && totalPowerMah != 0) {
Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000)
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ac5dbc4e175a..b1c45f729fcb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -34,6 +34,8 @@ import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBatteryPropertiesRegistrar;
@@ -78,6 +80,7 @@ import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
@@ -129,7 +132,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 173 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 174 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -153,11 +156,11 @@ public class BatteryStatsImpl extends BatteryStats {
MAX_HISTORY_BUFFER = 96*1024; // 96KB
MAX_MAX_HISTORY_BUFFER = 128*1024; // 128KB
} else {
- MAX_HISTORY_ITEMS = 2000;
- MAX_MAX_HISTORY_ITEMS = 3000;
- MAX_WAKELOCKS_PER_UID = 100;
- MAX_HISTORY_BUFFER = 256*1024; // 256KB
- MAX_MAX_HISTORY_BUFFER = 320*1024; // 256KB
+ MAX_HISTORY_ITEMS = 4000;
+ MAX_MAX_HISTORY_ITEMS = 6000;
+ MAX_WAKELOCKS_PER_UID = 200;
+ MAX_HISTORY_BUFFER = 512*1024; // 512KB
+ MAX_MAX_HISTORY_BUFFER = 640*1024; // 640KB
}
}
@@ -198,6 +201,12 @@ public class BatteryStatsImpl extends BatteryStats {
protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
new KernelUidCpuFreqTimeReader();
@VisibleForTesting
+ protected KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader =
+ new KernelUidCpuActiveTimeReader();
+ @VisibleForTesting
+ protected KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader =
+ new KernelUidCpuClusterTimeReader();
+ @VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
@@ -666,6 +675,10 @@ public class BatteryStatsImpl extends BatteryStats {
int mCameraOnNesting;
StopwatchTimer mCameraOnTimer;
+ int mGpsSignalQualityBin = -1;
+ final StopwatchTimer[] mGpsSignalQualityTimer =
+ new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+
int mPhoneSignalStrengthBin = -1;
int mPhoneSignalStrengthBinRaw = -1;
final StopwatchTimer[] mPhoneSignalStrengthsTimer =
@@ -739,6 +752,8 @@ public class BatteryStatsImpl extends BatteryStats {
final StopwatchTimer[] mWifiSignalStrengthsTimer =
new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+ StopwatchTimer mWifiActiveTimer;
+
int mBluetoothScanNesting;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected StopwatchTimer mBluetoothScanTimer;
@@ -2773,12 +2788,14 @@ public class BatteryStatsImpl extends BatteryStats {
public static class ControllerActivityCounterImpl extends ControllerActivityCounter
implements Parcelable {
private final LongSamplingCounter mIdleTimeMillis;
+ private final LongSamplingCounter mScanTimeMillis;
private final LongSamplingCounter mRxTimeMillis;
private final LongSamplingCounter[] mTxTimeMillis;
private final LongSamplingCounter mPowerDrainMaMs;
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
mIdleTimeMillis = new LongSamplingCounter(timeBase);
+ mScanTimeMillis = new LongSamplingCounter(timeBase);
mRxTimeMillis = new LongSamplingCounter(timeBase);
mTxTimeMillis = new LongSamplingCounter[numTxStates];
for (int i = 0; i < numTxStates; i++) {
@@ -2789,6 +2806,7 @@ public class BatteryStatsImpl extends BatteryStats {
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
+ mScanTimeMillis = new LongSamplingCounter(timeBase, in);
mRxTimeMillis = new LongSamplingCounter(timeBase, in);
final int recordedTxStates = in.readInt();
if (recordedTxStates != numTxStates) {
@@ -2804,6 +2822,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void readSummaryFromParcel(Parcel in) {
mIdleTimeMillis.readSummaryFromParcelLocked(in);
+ mScanTimeMillis.readSummaryFromParcelLocked(in);
mRxTimeMillis.readSummaryFromParcelLocked(in);
final int recordedTxStates = in.readInt();
if (recordedTxStates != mTxTimeMillis.length) {
@@ -2822,6 +2841,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void writeSummaryToParcel(Parcel dest) {
mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
+ mScanTimeMillis.writeSummaryFromParcelLocked(dest);
mRxTimeMillis.writeSummaryFromParcelLocked(dest);
dest.writeInt(mTxTimeMillis.length);
for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2833,6 +2853,7 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public void writeToParcel(Parcel dest, int flags) {
mIdleTimeMillis.writeToParcel(dest);
+ mScanTimeMillis.writeToParcel(dest);
mRxTimeMillis.writeToParcel(dest);
dest.writeInt(mTxTimeMillis.length);
for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2843,6 +2864,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void reset(boolean detachIfReset) {
mIdleTimeMillis.reset(detachIfReset);
+ mScanTimeMillis.reset(detachIfReset);
mRxTimeMillis.reset(detachIfReset);
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.reset(detachIfReset);
@@ -2852,6 +2874,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void detach() {
mIdleTimeMillis.detach();
+ mScanTimeMillis.detach();
mRxTimeMillis.detach();
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.detach();
@@ -2869,6 +2892,15 @@ public class BatteryStatsImpl extends BatteryStats {
}
/**
+ * @return a LongSamplingCounter, measuring time spent in the scan state in
+ * milliseconds.
+ */
+ @Override
+ public LongSamplingCounter getScanTimeCounter() {
+ return mScanTimeMillis;
+ }
+
+ /**
* @return a LongSamplingCounter, measuring time spent in the receive state in
* milliseconds.
*/
@@ -3880,6 +3912,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
mKernelUidCpuTimeReader.removeUid(isolatedUid);
mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
+ mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
+ }
}
public int mapUid(int uid) {
@@ -4575,10 +4611,37 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ stopAllGpsSignalQualityTimersLocked(-1);
+ mGpsSignalQualityBin = -1;
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
}
+ public void noteGpsSignalQualityLocked(int signalLevel) {
+ if (mGpsNesting == 0) {
+ return;
+ }
+ if (signalLevel < 0 || signalLevel >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
+ stopAllGpsSignalQualityTimersLocked(-1);
+ return;
+ }
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ if (mGpsSignalQualityBin != signalLevel) {
+ if (mGpsSignalQualityBin >= 0) {
+ mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtime);
+ }
+ if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) {
+ mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtime);
+ }
+ mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+ | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mGpsSignalQualityBin = signalLevel;
+ }
+ return;
+ }
+
public void noteScreenStateLocked(int state) {
state = mPretendScreenOff ? Display.STATE_OFF : state;
@@ -4912,6 +4975,18 @@ public class BatteryStatsImpl extends BatteryStats {
mDailyPackageChanges.add(pc);
}
+ void stopAllGpsSignalQualityTimersLocked(int except) {
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ if (i == except) {
+ continue;
+ }
+ while (mGpsSignalQualityTimer[i].isRunningLocked()) {
+ mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtime);
+ }
+ }
+ }
+
public void notePhoneOnLocked() {
if (!mPhoneOn) {
final long elapsedRealtime = mClocks.elapsedRealtime();
@@ -5565,8 +5640,11 @@ public class BatteryStatsImpl extends BatteryStats {
noteWifiRadioApWakeupLocked(elapsedRealtime, uptime, uid);
}
mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+ mWifiActiveTimer.startRunningLocked(elapsedRealtime);
} else {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+ mWifiActiveTimer.stopRunningLocked(
+ timestampNs / (1000 * 1000));
}
if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
+ Integer.toHexString(mHistoryCur.states));
@@ -6123,6 +6201,20 @@ public class BatteryStatsImpl extends BatteryStats {
return val;
}
+ @Override public long getGpsSignalQualityTime(int strengthBin,
+ long elapsedRealtimeUs, int which) {
+ if (strengthBin < 0 || strengthBin >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
+ return 0;
+ }
+ return mGpsSignalQualityTimer[strengthBin].getTotalTimeLocked(
+ elapsedRealtimeUs, which);
+ }
+
+ @Override public long getGpsBatteryDrainMaMs() {
+ //TODO: Add GPS power computation (b/67213967)
+ return 0;
+ }
+
@Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) {
return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@@ -6203,6 +6295,10 @@ public class BatteryStatsImpl extends BatteryStats {
return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
+ @Override public long getWifiActiveTime(long elapsedRealtimeUs, int which) {
+ return mWifiActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
@Override public long getGlobalWifiRunningTime(long elapsedRealtimeUs, int which) {
return mGlobalWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@@ -6479,9 +6575,11 @@ public class BatteryStatsImpl extends BatteryStats {
LongSamplingCounter mUserCpuTime;
LongSamplingCounter mSystemCpuTime;
LongSamplingCounter[][] mCpuClusterSpeedTimesUs;
+ LongSamplingCounter mCpuActiveTimeMs;
LongSamplingCounterArray mCpuFreqTimeMs;
LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
+ LongSamplingCounterArray mCpuClusterTimesMs;
LongSamplingCounterArray[] mProcStateTimeMs;
LongSamplingCounterArray[] mProcStateScreenOffTimeMs;
@@ -6551,6 +6649,8 @@ public class BatteryStatsImpl extends BatteryStats {
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+ mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+ mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase);
mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) {
@Override public Wakelock instantiateObject() {
@@ -6598,6 +6698,17 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public long getCpuActiveTime() {
+ return mCpuActiveTimeMs.getCountLocked(STATS_SINCE_CHARGED);
+ }
+
+ @Override
+ public long[] getCpuClusterTimes() {
+ return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
+ }
+
+
+ @Override
public long[] getCpuFreqTimes(int which, int procState) {
if (which < 0 || which >= NUM_PROCESS_STATE) {
return null;
@@ -7660,6 +7771,9 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenOffCpuFreqTimeMs.reset(false);
}
+ mCpuActiveTimeMs.reset(false);
+ mCpuClusterTimesMs.reset(false);
+
if (mProcStateTimeMs != null) {
for (LongSamplingCounterArray counters : mProcStateTimeMs) {
if (counters != null) {
@@ -7864,6 +7978,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (mScreenOffCpuFreqTimeMs != null) {
mScreenOffCpuFreqTimeMs.detach();
}
+ mCpuActiveTimeMs.detach();
+ mCpuClusterTimesMs.detach();
if (mProcStateTimeMs != null) {
for (LongSamplingCounterArray counters : mProcStateTimeMs) {
@@ -8139,6 +8255,10 @@ public class BatteryStatsImpl extends BatteryStats {
LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
+
+ mCpuActiveTimeMs.writeToParcel(out);
+ mCpuClusterTimesMs.writeToParcel(out);
+
if (mProcStateTimeMs != null) {
out.writeInt(mProcStateTimeMs.length);
for (LongSamplingCounterArray counters : mProcStateTimeMs) {
@@ -8456,6 +8576,9 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
in, mBsi.mOnBatteryScreenOffTimeBase);
+ mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+ mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in);
+
int length = in.readInt();
if (length == NUM_PROCESS_STATE) {
mProcStateTimeMs = new LongSamplingCounterArray[length];
@@ -9822,6 +9945,11 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null,
mOnBatteryTimeBase);
}
+ mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null, mOnBatteryTimeBase);
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, null,
+ mOnBatteryTimeBase);
+ }
mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase);
mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase);
mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase);
@@ -10511,7 +10639,11 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiSignalStrengthsTimer[i].reset(false);
}
mWifiMulticastWakelockTimer.reset(false);
+ mWifiActiveTimer.reset(false);
mWifiActivity.reset(false);
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].reset(false);
+ }
mBluetoothActivity.reset(false);
mModemActivity.reset(false);
mNumConnectivityChange = mLoadedNumConnectivityChange = mUnpluggedNumConnectivityChange = 0;
@@ -10774,6 +10906,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Measured in mAms
final long txTimeMs = info.getControllerTxTimeMillis();
final long rxTimeMs = info.getControllerRxTimeMillis();
+ final long scanTimeMs = info.getControllerScanTimeMillis();
final long idleTimeMs = info.getControllerIdleTimeMillis();
final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
@@ -10786,6 +10919,7 @@ public class BatteryStatsImpl extends BatteryStats {
Slog.d(TAG, " Rx Time: " + rxTimeMs + " ms");
Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms");
Slog.d(TAG, " Total Time: " + totalTimeMs + " ms");
+ Slog.d(TAG, " Scan Time: " + scanTimeMs + " ms");
}
long totalWifiLockTimeMs = 0;
@@ -10919,6 +11053,8 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis());
mWifiActivity.getTxTimeCounters()[0].addCountLocked(
info.getControllerTxTimeMillis());
+ mWifiActivity.getScanTimeCounter().addCountLocked(
+ info.getControllerScanTimeMillis());
mWifiActivity.getIdleTimeCounter().addCountLocked(
info.getControllerIdleTimeMillis());
@@ -10962,6 +11098,39 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
+ if (activityInfo != null) {
+ mHasModemReporting = true;
+ mModemActivity.getIdleTimeCounter().addCountLocked(
+ activityInfo.getIdleTimeMillis());
+ mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
+ for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+ mModemActivity.getTxTimeCounters()[lvl]
+ .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
+ }
+
+ // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+ final double opVolt = mPowerProfile.getAveragePower(
+ PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+ if (opVolt != 0) {
+ double energyUsed =
+ activityInfo.getSleepTimeMillis() *
+ mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
+ + activityInfo.getIdleTimeMillis() *
+ mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
+ + activityInfo.getRxTimeMillis() *
+ mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
+ int[] txCurrentMa = activityInfo.getTxTimeMillis();
+ for (int i = 0; i < Math.min(txCurrentMa.length,
+ SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
+ energyUsed += txCurrentMa[i] * mPowerProfile.getAveragePower(
+ PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ }
+
+ // We store the power drain as mAms.
+ mModemActivity.getPowerCounter().addCountLocked((long) energyUsed);
+ }
+ }
+
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000);
@@ -11060,26 +11229,6 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkStatsPool.release(delta);
delta = null;
}
-
- if (activityInfo != null) {
- mHasModemReporting = true;
- mModemActivity.getIdleTimeCounter().addCountLocked(
- activityInfo.getIdleTimeMillis());
- mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
- for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
- mModemActivity.getTxTimeCounters()[lvl]
- .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
- }
-
- // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
- final double opVolt = mPowerProfile.getAveragePower(
- PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
- if (opVolt != 0) {
- // We store the power drain as mAms.
- mModemActivity.getPowerCounter().addCountLocked(
- (long) (activityInfo.getEnergyUsed() / opVolt));
- }
- }
}
}
@@ -11437,6 +11586,10 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mOnBatteryInternal) {
mKernelUidCpuTimeReader.readDelta(null);
mKernelUidCpuFreqTimeReader.readDelta(null);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ mKernelUidCpuActiveTimeReader.readDelta(null);
+ mKernelUidCpuClusterTimeReader.readDelta(null);
+ }
for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
mKernelCpuSpeedReaders[cluster].readDelta();
}
@@ -11453,6 +11606,10 @@ public class BatteryStatsImpl extends BatteryStats {
updateClusterSpeedTimes(updatedUids);
}
readKernelUidCpuFreqTimesLocked(partialTimersToConsider);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ readKernelUidCpuActiveTimesLocked();
+ readKernelUidCpuClusterTimesLocked();
+ }
}
/**
@@ -11764,6 +11921,64 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ /**
+ * Take a snapshot of the cpu active times spent by each uid and update the corresponding
+ * counters.
+ */
+ @VisibleForTesting
+ public void readKernelUidCpuActiveTimesLocked() {
+ final long startTimeMs = mClocks.uptimeMillis();
+ mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> {
+ uid = mapUid(uid);
+ if (Process.isIsolated(uid)) {
+ mKernelUidCpuActiveTimeReader.removeUid(uid);
+ Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid);
+ return;
+ }
+ if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+ Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
+ mKernelUidCpuActiveTimeReader.removeUid(uid);
+ return;
+ }
+ final Uid u = getUidStatsLocked(uid);
+ u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs);
+ });
+
+ final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+ Slog.d(TAG, "Reading cpu active times took " + elapsedTimeMs + "ms");
+ }
+ }
+
+ /**
+ * Take a snapshot of the cpu cluster times spent by each uid and update the corresponding
+ * counters.
+ */
+ @VisibleForTesting
+ public void readKernelUidCpuClusterTimesLocked() {
+ final long startTimeMs = mClocks.uptimeMillis();
+ mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> {
+ uid = mapUid(uid);
+ if (Process.isIsolated(uid)) {
+ mKernelUidCpuClusterTimeReader.removeUid(uid);
+ Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid);
+ return;
+ }
+ if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+ Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
+ mKernelUidCpuClusterTimeReader.removeUid(uid);
+ return;
+ }
+ final Uid u = getUidStatsLocked(uid);
+ u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs);
+ });
+
+ final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+ Slog.d(TAG, "Reading cpu cluster times took " + elapsedTimeMs + "ms");
+ }
+ }
+
boolean setChargingLocked(boolean charging) {
if (mCharging != charging) {
mCharging = charging;
@@ -12367,6 +12582,71 @@ public class BatteryStatsImpl extends BatteryStats {
return s;
}
+ /*@hide */
+ public WifiBatteryStats getWifiBatteryStats() {
+ WifiBatteryStats s = new WifiBatteryStats();
+ final int which = STATS_SINCE_CHARGED;
+ final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+ final ControllerActivityCounter counter = getWifiControllerActivity();
+ final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+ final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+ final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+ final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
+ final long totalControllerActivityTimeMs
+ = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
+ final long sleepTimeMs
+ = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
+ final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+ long numAppScanRequest = 0;
+ for (int i = 0; i < mUidStats.size(); i++) {
+ numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
+ }
+ long[] timeInStateMs = new long[NUM_WIFI_STATES];
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
+ timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000;
+ }
+ long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
+ for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+ timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
+ }
+ long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+ for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+ timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
+ }
+ s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+ s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
+ s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+ s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+ s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+ s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+ s.setSleepTimeMs(sleepTimeMs);
+ s.setIdleTimeMs(idleTimeMs);
+ s.setRxTimeMs(rxTimeMs);
+ s.setTxTimeMs(txTimeMs);
+ s.setScanTimeMs(scanTimeMs);
+ s.setEnergyConsumedMaMs(energyConsumedMaMs);
+ s.setNumAppScanRequest(numAppScanRequest);
+ s.setTimeInStateMs(timeInStateMs);
+ s.setTimeInSupplicantStateMs(timeInSupplStateMs);
+ s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
+ return s;
+ }
+
+ /*@hide */
+ public GpsBatteryStats getGpsBatteryStats() {
+ GpsBatteryStats s = new GpsBatteryStats();
+ final int which = STATS_SINCE_CHARGED;
+ final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+ s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+ s.setEnergyConsumedMaMs(getGpsBatteryDrainMaMs());
+ long[] time = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+ for (int i=0; i<time.length; i++) {
+ time[i] = getGpsSignalQualityTime(i, rawRealTime, which) / 1000;
+ }
+ s.setTimeInGpsSignalQualityLevel(time);
+ return s;
+ }
+
@Override
public LevelStepTracker getChargeLevelStepTracker() {
return mChargeStepTracker;
@@ -12626,10 +12906,19 @@ public class BatteryStatsImpl extends BatteryStats {
public final class Constants extends ContentObserver {
public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE
= "track_cpu_times_by_proc_state";
+ public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME
+ = "track_cpu_active_cluster_time";
+ public static final String KEY_READ_BINARY_CPU_TIME
+ = "read_binary_cpu_time";
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
+ private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
+ private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
+ public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
+ // Not used right now.
+ public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -12665,6 +12954,11 @@ public class BatteryStatsImpl extends BatteryStats {
updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE,
mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE,
DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
+ TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
+ KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
+ READ_BINARY_CPU_TIME = mParser.getBoolean(
+ KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME);
+
}
}
@@ -12679,6 +12973,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void dumpLocked(PrintWriter pw) {
pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
+ pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("=");
+ pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
+ pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("=");
+ pw.println(READ_BINARY_CPU_TIME);
}
}
@@ -13044,7 +13342,11 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
}
+ mWifiActiveTimer.readSummaryFromParcelLocked(in);
mWifiActivity.readSummaryFromParcel(in);
+ for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in);
+ }
mBluetoothActivity.readSummaryFromParcel(in);
mModemActivity.readSummaryFromParcel(in);
mHasWifiReporting = in.readInt() != 0;
@@ -13249,6 +13551,10 @@ public class BatteryStatsImpl extends BatteryStats {
in, mOnBatteryTimeBase);
u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
in, mOnBatteryScreenOffTimeBase);
+
+ u.mCpuActiveTimeMs.readSummaryFromParcelLocked(in);
+ u.mCpuClusterTimesMs.readSummaryFromParcelLocked(in);
+
int length = in.readInt();
if (length == Uid.NUM_PROCESS_STATE) {
u.mProcStateTimeMs = new LongSamplingCounterArray[length];
@@ -13482,7 +13788,11 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
+ mWifiActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mWifiActivity.writeSummaryToParcel(out);
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ }
mBluetoothActivity.writeSummaryToParcel(out);
mModemActivity.writeSummaryToParcel(out);
out.writeInt(mHasWifiReporting ? 1 : 0);
@@ -13725,6 +14035,9 @@ public class BatteryStatsImpl extends BatteryStats {
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mCpuFreqTimeMs);
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mScreenOffCpuFreqTimeMs);
+ u.mCpuActiveTimeMs.writeSummaryFromParcelLocked(out);
+ u.mCpuClusterTimesMs.writeSummaryToParcelLocked(out);
+
if (u.mProcStateTimeMs != null) {
out.writeInt(u.mProcStateTimeMs.length);
for (LongSamplingCounterArray counters : u.mProcStateTimeMs) {
@@ -13954,9 +14267,14 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i,
null, mOnBatteryTimeBase, in);
}
-
+ mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null,
+ mOnBatteryTimeBase, in);
mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_WIFI_TX_LEVELS, in);
+ for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i,
+ null, mOnBatteryTimeBase, in);
+ }
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_BT_TX_LEVELS, in);
mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
@@ -14154,7 +14472,11 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
+ mWifiActiveTimer.writeToParcel(out, uSecRealtime);
mWifiActivity.writeToParcel(out, 0);
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime);
+ }
mBluetoothActivity.writeToParcel(out, 0);
mModemActivity.writeToParcel(out, 0);
out.writeInt(mHasWifiReporting ? 1 : 0);
@@ -14348,6 +14670,10 @@ public class BatteryStatsImpl extends BatteryStats {
pr.println("*** Wifi signal strength #" + i + ":");
mWifiSignalStrengthsTimer[i].logState(pr, " ");
}
+ for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ pr.println("*** GPS signal quality #" + i + ":");
+ mGpsSignalQualityTimer[i].logState(pr, " ");
+ }
pr.println("*** Flashlight timer:");
mFlashlightOnTimer.logState(pr, " ");
pr.println("*** Camera timer:");
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index bb743c157d1f..a34e7f50c9c9 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -31,8 +31,7 @@ public class CpuPowerCalculator extends PowerCalculator {
@Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
-
+ long rawUptimeUs, int statsType) {
app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
final int numClusters = mProfile.getNumCpuClusters();
@@ -42,7 +41,7 @@ public class CpuPowerCalculator extends PowerCalculator {
for (int speed = 0; speed < speedsForCluster; speed++) {
final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
final double cpuSpeedStepPower = timeUs *
- mProfile.getAveragePowerForCpu(cluster, speed);
+ mProfile.getAveragePowerForCpuCore(cluster, speed);
if (DEBUG) {
Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+ speed + " timeUs=" + timeUs + " power="
@@ -51,6 +50,25 @@ public class CpuPowerCalculator extends PowerCalculator {
cpuPowerMaUs += cpuSpeedStepPower;
}
}
+ cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower(
+ PowerProfile.POWER_CPU_ACTIVE);
+ long[] cpuClusterTimes = u.getCpuClusterTimes();
+ if (cpuClusterTimes != null) {
+ if (cpuClusterTimes.length == numClusters) {
+ for (int i = 0; i < numClusters; i++) {
+ double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i);
+ cpuPowerMaUs += power;
+ if (DEBUG) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
+ + cpuClusterTimes[i] + " power="
+ + BatteryStatsHelper.makemAh(power / MICROSEC_IN_HR));
+ }
+ }
+ } else {
+ Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
+ + numClusters + " actual # " + cpuClusterTimes.length);
+ }
+ }
app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
new file mode 100644
index 000000000000..cb96c5cdfb60
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Reads /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+public class KernelUidCpuActiveTimeReader {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "KernelUidCpuActiveTimeReader";
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time";
+
+ private int mCoreCount;
+ private long mLastTimeReadMs;
+ private long mNowTimeMs;
+ private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>();
+
+ public interface Callback {
+ void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
+ }
+
+ public void readDelta(@Nullable Callback cb) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
+ mNowTimeMs = SystemClock.elapsedRealtime();
+ readDeltaInternal(reader, cb);
+ mLastTimeReadMs = mNowTimeMs;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+
+ public void removeUid(int uid) {
+ mLastUidCpuActiveTimeMs.delete(uid);
+ }
+
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (endUid < startUid) {
+ Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+ return;
+ }
+ mLastUidCpuActiveTimeMs.put(startUid, null);
+ mLastUidCpuActiveTimeMs.put(endUid, null);
+ final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
+ final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
+ mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
+ @VisibleForTesting
+ public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
+ String line = reader.readLine();
+ if (line == null || !line.startsWith("active:")) {
+ Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+ return;
+ }
+ if (mCoreCount == 0) {
+ mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1));
+ }
+ while ((line = reader.readLine()) != null) {
+ final int index = line.indexOf(' ');
+ final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
+ readTimesForUid(uid, line.substring(index + 1), cb);
+ }
+ }
+
+ private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
+ long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid);
+ if (lastActiveTime == null) {
+ lastActiveTime = new long[mCoreCount];
+ mLastUidCpuActiveTimeMs.put(uid, lastActiveTime);
+ }
+ final String[] timesStr = line.split(" ");
+ if (timesStr.length != mCoreCount) {
+ Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d",
+ timesStr.length, mCoreCount));
+ return;
+ }
+ long sumDeltas = 0;
+ final long[] curActiveTime = new long[mCoreCount];
+ boolean notify = false;
+ for (int i = 0; i < mCoreCount; i++) {
+ // Times read will be in units of 10ms
+ curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10;
+ long delta = curActiveTime[i] - lastActiveTime[i];
+ if (delta < 0 || curActiveTime[i] < 0) {
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Malformed cpu active time for UID=%d\n", uid));
+ sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i]));
+ sb.append("times=(");
+ TimeUtils.formatDuration(mLastTimeReadMs, sb);
+ sb.append(",");
+ TimeUtils.formatDuration(mNowTimeMs, sb);
+ sb.append(")");
+ Slog.e(TAG, sb.toString());
+ }
+ return;
+ }
+ notify |= delta > 0;
+ sumDeltas += delta / (i + 1);
+ }
+ if (notify) {
+ System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount);
+ if (cb != null) {
+ cb.onUidCpuActiveTime(uid, sumDeltas);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
new file mode 100644
index 000000000000..85153bc45d07
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+public class KernelUidCpuClusterTimeReader {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "KernelUidCpuClusterTimeReader";
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_policy_time";
+
+ // mCoreOnCluster[i] is the # of cores on cluster i
+ private int[] mCoreOnCluster;
+ private int mCores;
+ private long mLastTimeReadMs;
+ private long mNowTimeMs;
+ private SparseArray<long[]> mLastUidPolicyTimeMs = new SparseArray<>();
+
+ public interface Callback {
+ /**
+ * @param uid
+ * @param cpuActiveTimeMs the first dimension is cluster, the second dimension is the # of
+ * processes running concurrently with this uid.
+ */
+ void onUidCpuPolicyTime(int uid, long[] cpuActiveTimeMs);
+ }
+
+ public void readDelta(@Nullable Callback cb) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
+ mNowTimeMs = SystemClock.elapsedRealtime();
+ readDeltaInternal(reader, cb);
+ mLastTimeReadMs = mNowTimeMs;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+
+ public void removeUid(int uid) {
+ mLastUidPolicyTimeMs.delete(uid);
+ }
+
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (endUid < startUid) {
+ Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+ return;
+ }
+ mLastUidPolicyTimeMs.put(startUid, null);
+ mLastUidPolicyTimeMs.put(endUid, null);
+ final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
+ final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid);
+ mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
+ @VisibleForTesting
+ public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
+ String line = reader.readLine();
+ if (line == null || !line.startsWith("policy")) {
+ Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+ return;
+ }
+ if (mCoreOnCluster == null) {
+ List<Integer> list = new ArrayList<>();
+ String[] policies = line.split(" ");
+
+ if (policies.length == 0 || policies.length % 2 != 0) {
+ Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+ return;
+ }
+
+ for (int i = 0; i < policies.length; i+=2) {
+ list.add(Integer.parseInt(policies[i+1]));
+ }
+
+ mCoreOnCluster = new int[list.size()];
+ for(int i=0;i<list.size();i++){
+ mCoreOnCluster[i] = list.get(i);
+ mCores += mCoreOnCluster[i];
+ }
+ }
+ while ((line = reader.readLine()) != null) {
+ final int index = line.indexOf(' ');
+ final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
+ readTimesForUid(uid, line.substring(index + 1), cb);
+ }
+ }
+
+ private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
+ long[] lastPolicyTime = mLastUidPolicyTimeMs.get(uid);
+ if (lastPolicyTime == null) {
+ lastPolicyTime = new long[mCores];
+ mLastUidPolicyTimeMs.put(uid, lastPolicyTime);
+ }
+ final String[] timeStr = line.split(" ");
+ if (timeStr.length != mCores) {
+ Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, # CPU cores: %d",
+ timeStr.length, mCores));
+ return;
+ }
+ final long[] deltaPolicyTime = new long[mCores];
+ final long[] currPolicyTime = new long[mCores];
+ boolean notify = false;
+ for (int i = 0; i < mCores; i++) {
+ // Times read will be in units of 10ms
+ currPolicyTime[i] = Long.parseLong(timeStr[i], 10) * 10;
+ deltaPolicyTime[i] = currPolicyTime[i] - lastPolicyTime[i];
+ if (deltaPolicyTime[i] < 0 || currPolicyTime[i] < 0) {
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Malformed cpu policy time for UID=%d\n", uid));
+ sb.append(String.format("data=(%d,%d)\n", lastPolicyTime[i], currPolicyTime[i]));
+ sb.append("times=(");
+ TimeUtils.formatDuration(mLastTimeReadMs, sb);
+ sb.append(",");
+ TimeUtils.formatDuration(mNowTimeMs, sb);
+ sb.append(")");
+ Slog.e(TAG, sb.toString());
+ }
+ return;
+ }
+ notify |= deltaPolicyTime[i] > 0;
+ }
+ if (notify) {
+ System.arraycopy(currPolicyTime, 0, lastPolicyTime, 0, mCores);
+ if (cb != null) {
+ final long[] times = new long[mCoreOnCluster.length];
+ int core = 0;
+ for (int i = 0; i < mCoreOnCluster.length; i++) {
+ for (int j = 0; j < mCoreOnCluster[i]; j++) {
+ times[i] += deltaPolicyTime[core++] / (j+1);
+ }
+ }
+ cb.onUidCpuPolicyTime(uid, times);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 872b465a9ca5..240fc513055c 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -43,23 +44,25 @@ public class PowerProfile {
public static final String POWER_NONE = "none";
/**
- * Power consumption when CPU is in power collapse mode.
+ * POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
+ * POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
+ * be zero on devices that can go into full CPU power collapse even when a wake
+ * lock is held. Otherwise, this is the power consumption in addition to
+ * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity.
+ * POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters
+ * and cores.
+ *
+ * CPU Power Equation (assume two clusters):
+ * Total power = POWER_CPU_SUSPEND (always added)
+ * + POWER_CPU_IDLE (skip this and below if in power collapse mode)
+ * + POWER_CPU_ACTIVE (skip this and below if CPU is not running, but a wakelock
+ * is held)
+ * + cluster_power.cluster0 + cluster_power.cluster1 (skip cluster not running)
+ * + core_power.cluster0 * num running cores in cluster 0
+ * + core_power.cluster1 * num running cores in cluster 1
*/
+ public static final String POWER_CPU_SUSPEND = "cpu.suspend";
public static final String POWER_CPU_IDLE = "cpu.idle";
-
- /**
- * Power consumption when CPU is awake (when a wake lock is held). This
- * should be 0 on devices that can go into full CPU power collapse even
- * when a wake lock is held. Otherwise, this is the power consumption in
- * addition to POWER_CPU_IDLE due to a wake lock being held but with no
- * CPU activity.
- */
- public static final String POWER_CPU_AWAKE = "cpu.awake";
-
- /**
- * Power consumption when CPU is in power collapse mode.
- */
- @Deprecated
public static final String POWER_CPU_ACTIVE = "cpu.active";
/**
@@ -94,6 +97,7 @@ public class PowerProfile {
public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
"bluetooth.controller.voltage";
+ public static final String POWER_MODEM_CONTROLLER_SLEEP = "modem.controller.sleep";
public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle";
public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx";
public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx";
@@ -182,9 +186,6 @@ public class PowerProfile {
*/
public static final String POWER_CAMERA = "camera.avg";
- @Deprecated
- public static final String POWER_CPU_SPEEDS = "cpu.speeds";
-
/**
* Power consumed by wif batched scaning. Broken down into bins by
* Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels
@@ -197,7 +198,15 @@ public class PowerProfile {
*/
public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
- static final HashMap<String, Object> sPowerMap = new HashMap<>();
+ /**
+ * A map from Power Use Item to its power consumption.
+ */
+ static final HashMap<String, Double> sPowerItemMap = new HashMap<>();
+ /**
+ * A map from Power Use Item to an array of its power consumption
+ * (for items with variable power e.g. CPU).
+ */
+ static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>();
private static final String TAG_DEVICE = "device";
private static final String TAG_ITEM = "item";
@@ -207,23 +216,32 @@ public class PowerProfile {
private static final Object sLock = new Object();
+ @VisibleForTesting
public PowerProfile(Context context) {
- // Read the XML file for the given profile (normally only one per
- // device)
+ this(context, false);
+ }
+
+ /**
+ * For PowerProfileTest
+ */
+ @VisibleForTesting
+ public PowerProfile(Context context, boolean forTest) {
+ // Read the XML file for the given profile (normally only one per device)
synchronized (sLock) {
- if (sPowerMap.size() == 0) {
- readPowerValuesFromXml(context);
+ if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
+ readPowerValuesFromXml(context, forTest);
}
initCpuClusters();
}
}
- private void readPowerValuesFromXml(Context context) {
- int id = com.android.internal.R.xml.power_profile;
+ private void readPowerValuesFromXml(Context context, boolean forTest) {
+ final int id = forTest ? com.android.internal.R.xml.power_profile_test :
+ com.android.internal.R.xml.power_profile;
final Resources resources = context.getResources();
XmlResourceParser parser = resources.getXml(id);
boolean parsingArray = false;
- ArrayList<Double> array = new ArrayList<Double>();
+ ArrayList<Double> array = new ArrayList<>();
String arrayName = null;
try {
@@ -237,7 +255,7 @@ public class PowerProfile {
if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
// Finish array
- sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+ sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
parsingArray = false;
}
if (element.equals(TAG_ARRAY)) {
@@ -255,7 +273,7 @@ public class PowerProfile {
} catch (NumberFormatException nfe) {
}
if (element.equals(TAG_ITEM)) {
- sPowerMap.put(name, value);
+ sPowerItemMap.put(name, value);
} else if (parsingArray) {
array.add(value);
}
@@ -263,7 +281,7 @@ public class PowerProfile {
}
}
if (parsingArray) {
- sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+ sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
}
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
@@ -279,10 +297,6 @@ public class PowerProfile {
com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
- com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
- com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
- com.android.internal.R.integer.config_wifi_tx_cur_ma,
- com.android.internal.R.integer.config_wifi_operating_voltage_mv,
};
String[] configResIdKeys = new String[]{
@@ -290,62 +304,62 @@ public class PowerProfile {
POWER_BLUETOOTH_CONTROLLER_RX,
POWER_BLUETOOTH_CONTROLLER_TX,
POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
- POWER_WIFI_CONTROLLER_IDLE,
- POWER_WIFI_CONTROLLER_RX,
- POWER_WIFI_CONTROLLER_TX,
- POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
};
for (int i = 0; i < configResIds.length; i++) {
String key = configResIdKeys[i];
// if we already have some of these parameters in power_profile.xml, ignore the
// value in config.xml
- if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) {
+ if ((sPowerItemMap.containsKey(key) && sPowerItemMap.get(key) > 0)) {
continue;
}
int value = resources.getInteger(configResIds[i]);
if (value > 0) {
- sPowerMap.put(key, (double) value);
+ sPowerItemMap.put(key, (double) value);
}
}
}
private CpuClusterKey[] mCpuClusters;
- private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
- private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
- private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
+ private static final String CPU_PER_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
+ private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster";
+ private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster";
+ private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster";
- @SuppressWarnings("deprecation")
private void initCpuClusters() {
- // Figure out how many CPU clusters we're dealing with
- final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
- if (obj == null || !(obj instanceof Double[])) {
+ if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+ final Double[] data = sPowerArrayMap.get(CPU_PER_CLUSTER_CORE_COUNT);
+ mCpuClusters = new CpuClusterKey[data.length];
+ for (int cluster = 0; cluster < data.length; cluster++) {
+ int numCpusInCluster = (int) Math.round(data[cluster]);
+ mCpuClusters[cluster] = new CpuClusterKey(
+ CPU_CORE_SPEED_PREFIX + cluster, CPU_CLUSTER_POWER_COUNT + cluster,
+ CPU_CORE_POWER_PREFIX + cluster, numCpusInCluster);
+ }
+ } else {
// Default to single.
mCpuClusters = new CpuClusterKey[1];
- mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
-
- } else {
- final Double[] array = (Double[]) obj;
- mCpuClusters = new CpuClusterKey[array.length];
- for (int cluster = 0; cluster < array.length; cluster++) {
- int numCpusInCluster = (int) Math.round(array[cluster]);
- mCpuClusters[cluster] = new CpuClusterKey(
- POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
- POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
- numCpusInCluster);
+ int numCpus = 1;
+ if (sPowerItemMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+ numCpus = (int) Math.round(sPowerItemMap.get(CPU_PER_CLUSTER_CORE_COUNT));
}
+ mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0,
+ CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus);
}
}
public static class CpuClusterKey {
- private final String timeKey;
- private final String powerKey;
+ private final String freqKey;
+ private final String clusterPowerKey;
+ private final String corePowerKey;
private final int numCpus;
- private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
- this.timeKey = timeKey;
- this.powerKey = powerKey;
+ private CpuClusterKey(String freqKey, String clusterPowerKey,
+ String corePowerKey, int numCpus) {
+ this.freqKey = freqKey;
+ this.clusterPowerKey = clusterPowerKey;
+ this.corePowerKey = corePowerKey;
this.numCpus = numCpus;
}
}
@@ -354,21 +368,30 @@ public class PowerProfile {
return mCpuClusters.length;
}
- public int getNumCoresInCpuCluster(int index) {
- return mCpuClusters[index].numCpus;
+ public int getNumCoresInCpuCluster(int cluster) {
+ return mCpuClusters[cluster].numCpus;
}
- public int getNumSpeedStepsInCpuCluster(int index) {
- Object value = sPowerMap.get(mCpuClusters[index].timeKey);
- if (value != null && value instanceof Double[]) {
- return ((Double[])value).length;
+ public int getNumSpeedStepsInCpuCluster(int cluster) {
+ if (cluster < 0 || cluster >= mCpuClusters.length) {
+ return 0; // index out of bound
+ }
+ if (sPowerArrayMap.containsKey(mCpuClusters[cluster].freqKey)) {
+ return sPowerArrayMap.get(mCpuClusters[cluster].freqKey).length;
}
return 1; // Only one speed
}
- public double getAveragePowerForCpu(int cluster, int step) {
+ public double getAveragePowerForCpuCluster(int cluster) {
if (cluster >= 0 && cluster < mCpuClusters.length) {
- return getAveragePower(mCpuClusters[cluster].powerKey, step);
+ return getAveragePower(mCpuClusters[cluster].clusterPowerKey);
+ }
+ return 0;
+ }
+
+ public double getAveragePowerForCpuCore(int cluster, int step) {
+ if (cluster >= 0 && cluster < mCpuClusters.length) {
+ return getAveragePower(mCpuClusters[cluster].corePowerKey, step);
}
return 0;
}
@@ -379,14 +402,10 @@ public class PowerProfile {
* @return the number of memory bandwidth buckets.
*/
public int getNumElements(String key) {
- if (sPowerMap.containsKey(key)) {
- Object data = sPowerMap.get(key);
- if (data instanceof Double[]) {
- final Double[] values = (Double[]) data;
- return values.length;
- } else {
- return 1;
- }
+ if (sPowerItemMap.containsKey(key)) {
+ return 1;
+ } else if (sPowerArrayMap.containsKey(key)) {
+ return sPowerArrayMap.get(key).length;
}
return 0;
}
@@ -399,13 +418,10 @@ public class PowerProfile {
* @return the average current in milliAmps.
*/
public double getAveragePowerOrDefault(String type, double defaultValue) {
- if (sPowerMap.containsKey(type)) {
- Object data = sPowerMap.get(type);
- if (data instanceof Double[]) {
- return ((Double[])data)[0];
- } else {
- return (Double) sPowerMap.get(type);
- }
+ if (sPowerItemMap.containsKey(type)) {
+ return sPowerItemMap.get(type);
+ } else if (sPowerArrayMap.containsKey(type)) {
+ return sPowerArrayMap.get(type)[0];
} else {
return defaultValue;
}
@@ -429,19 +445,16 @@ public class PowerProfile {
* @return the average current in milliAmps.
*/
public double getAveragePower(String type, int level) {
- if (sPowerMap.containsKey(type)) {
- Object data = sPowerMap.get(type);
- if (data instanceof Double[]) {
- final Double[] values = (Double[]) data;
- if (values.length > level && level >= 0) {
- return values[level];
- } else if (level < 0 || values.length == 0) {
- return 0;
- } else {
- return values[values.length - 1];
- }
+ if (sPowerItemMap.containsKey(type)) {
+ return sPowerItemMap.get(type);
+ } else if (sPowerArrayMap.containsKey(type)) {
+ final Double[] values = sPowerArrayMap.get(type);
+ if (values.length > level && level >= 0) {
+ return values[level];
+ } else if (level < 0 || values.length == 0) {
+ return 0;
} else {
- return (Double) data;
+ return values[values.length - 1];
}
} else {
return 0;
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index c7897b2bbc3b..486b5842400c 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -26,7 +26,7 @@ public class WakelockPowerCalculator extends PowerCalculator {
private long mTotalAppWakelockTimeMs = 0;
public WakelockPowerCalculator(PowerProfile profile) {
- mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+ mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
}
@Override
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 85655a5dfc6b..e097362a3fe8 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -115,7 +115,7 @@ oneway interface IStatusBar
/**
* Notifies the status bar that a new rotation suggestion is available.
*/
- void onProposedRotationChanged(int rotation);
+ void onProposedRotationChanged(int rotation, boolean isValid);
/**
* Set whether the top app currently hides the statusbar.
diff --git a/core/java/com/android/internal/util/ConcurrentUtils.java b/core/java/com/android/internal/util/ConcurrentUtils.java
index e35f9f45acfe..e08eb587ab97 100644
--- a/core/java/com/android/internal/util/ConcurrentUtils.java
+++ b/core/java/com/android/internal/util/ConcurrentUtils.java
@@ -18,11 +18,13 @@ package com.android.internal.util;
import android.os.Process;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -86,4 +88,27 @@ public class ConcurrentUtils {
}
}
+ /**
+ * Waits for {@link CountDownLatch#countDown()} to be called on the {@param countDownLatch}.
+ * <p>If {@link CountDownLatch#countDown()} doesn't occur within {@param timeoutMs}, this
+ * method will throw {@code IllegalStateException}
+ * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread
+ * and throw {@code IllegalStateException}
+ *
+ * @param countDownLatch the CountDownLatch which {@link CountDownLatch#countDown()} is
+ * being waited on.
+ * @param timeoutMs the maximum time waited for {@link CountDownLatch#countDown()}
+ * @param description a short description of the operation
+ */
+ public static void waitForCountDownNoInterrupt(CountDownLatch countDownLatch, long timeoutMs,
+ String description) {
+ try {
+ if (!countDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
+ throw new IllegalStateException(description + " timed out.");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(description + " interrupted.");
+ }
+ }
}
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 66b777e8e8e6..2b5103377ecb 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -102,6 +102,7 @@ public final class DumpUtils {
case android.os.Process.ROOT_UID:
case android.os.Process.SYSTEM_UID:
case android.os.Process.SHELL_UID:
+ case android.os.Process.INCIDENTD_UID:
return true;
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index f7ea7875c8df..7fd94c6859fb 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,5 +1,6 @@
package com.android.internal.util;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,8 +33,18 @@ public class ScreenshotHelper {
mContext = context;
}
+ /**
+ * Request a screenshot be taken.
+ *
+ * @param screenshotType The type of screenshot, for example either
+ * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN}
+ * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION}
+ * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not.
+ * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not.
+ * @param handler A handler used in case the screenshot times out
+ */
public void takeScreenshot(final int screenshotType, final boolean hasStatus,
- final boolean hasNav, Handler handler) {
+ final boolean hasNav, @NonNull Handler handler) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 7f4b384235e5..dcb81fa623f4 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -66,10 +66,6 @@ public:
static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
- // Given the 'native' long held by the Rasterizer.java object, return a
- // ref to its SkRasterizer* (or NULL).
- static sk_sp<SkRasterizer> refNativeRasterizer(jlong rasterizerHandle);
-
/*
* LegacyBitmapConfig is the old enum in Skia that matched the enum int values
* in Bitmap.Config. Skia no longer supports this config, but has replaced it
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index f3aeb32f3f86..888db32fdfac 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -622,7 +622,7 @@ void util_multiplyMV(JNIEnv *env, jclass clazz,
// ---------------------------------------------------------------------------
-static int checkFormat(SkColorType colorType, int format, int type)
+static int checkInternalFormat(SkColorType colorType, int format, int type)
{
switch(colorType) {
case kN32_SkColorType:
@@ -651,6 +651,20 @@ static int checkFormat(SkColorType colorType, int format, int type)
return -1;
}
+// The internal format is no longer the same as pixel format, per Table 2 in
+// https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
+static int getPixelFormatFromInternalFormat(uint32_t internalFormat) {
+ switch (internalFormat) {
+ // For sized internal format.
+ case GL_RGBA16F:
+ return GL_RGBA;
+ // Base internal formats and pixel formats are still the same, see Table 1 in
+ // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
+ default:
+ return internalFormat;
+ }
+}
+
static int getInternalFormat(SkColorType colorType)
{
switch(colorType) {
@@ -716,7 +730,7 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz,
if (type < 0) {
type = getType(colorType);
}
- int err = checkFormat(colorType, internalformat, type);
+ int err = checkInternalFormat(colorType, internalformat, type);
if (err)
return err;
const int w = bitmap.width();
@@ -725,7 +739,8 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz,
if (internalformat == GL_PALETTE8_RGBA8_OES) {
err = -1;
} else {
- glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
+ glTexImage2D(target, level, internalformat, w, h, border,
+ getPixelFormatFromInternalFormat(internalformat), type, p);
}
return err;
}
@@ -737,12 +752,13 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
SkBitmap bitmap;
GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
SkColorType colorType = bitmap.colorType();
+ int internalFormat = getInternalFormat(colorType);
if (format < 0) {
- format = getInternalFormat(colorType);
+ format = getPixelFormatFromInternalFormat(internalFormat);
if (format == GL_PALETTE8_RGBA8_OES)
return -1; // glCompressedTexSubImage2D() not supported
}
- int err = checkFormat(colorType, format, type);
+ int err = checkInternalFormat(colorType, internalFormat, type);
if (err)
return err;
const int w = bitmap.width();
diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp
index eefcb74e0f70..34544d37a6bf 100644
--- a/core/jni/android_database_SQLiteCommon.cpp
+++ b/core/jni/android_database_SQLiteCommon.cpp
@@ -18,8 +18,108 @@
#include <utils/String8.h>
+#include <map>
+
namespace android {
+static const std::map<int, std::string> sErrorCodesMap = {
+ // Primary Result Code List
+ {4, "SQLITE_ABORT"},
+ {23, "SQLITE_AUTH"},
+ {5, "SQLITE_BUSY"},
+ {14, "SQLITE_CANTOPEN"},
+ {19, "SQLITE_CONSTRAINT"},
+ {11, "SQLITE_CORRUPT"},
+ {101, "SQLITE_DONE"},
+ {16, "SQLITE_EMPTY"},
+ {1, "SQLITE_ERROR"},
+ {24, "SQLITE_FORMAT"},
+ {13, "SQLITE_FULL"},
+ {2, "SQLITE_INTERNAL"},
+ {9, "SQLITE_INTERRUPT"},
+ {10, "SQLITE_IOERR"},
+ {6, "SQLITE_LOCKED"},
+ {20, "SQLITE_MISMATCH"},
+ {21, "SQLITE_MISUSE"},
+ {22, "SQLITE_NOLFS"},
+ {7, "SQLITE_NOMEM"},
+ {26, "SQLITE_NOTADB"},
+ {12, "SQLITE_NOTFOUND"},
+ {27, "SQLITE_NOTICE"},
+ {0, "SQLITE_OK"},
+ {3, "SQLITE_PERM"},
+ {15, "SQLITE_PROTOCOL"},
+ {25, "SQLITE_RANGE"},
+ {8, "SQLITE_READONLY"},
+ {100, "SQLITE_ROW"},
+ {17, "SQLITE_SCHEMA"},
+ {18, "SQLITE_TOOBIG"},
+ {28, "SQLITE_WARNING"},
+ // Extended Result Code List
+ {516, "SQLITE_ABORT_ROLLBACK"},
+ {261, "SQLITE_BUSY_RECOVERY"},
+ {517, "SQLITE_BUSY_SNAPSHOT"},
+ {1038, "SQLITE_CANTOPEN_CONVPATH"},
+ {782, "SQLITE_CANTOPEN_FULLPATH"},
+ {526, "SQLITE_CANTOPEN_ISDIR"},
+ {270, "SQLITE_CANTOPEN_NOTEMPDIR"},
+ {275, "SQLITE_CONSTRAINT_CHECK"},
+ {531, "SQLITE_CONSTRAINT_COMMITHOOK"},
+ {787, "SQLITE_CONSTRAINT_FOREIGNKEY"},
+ {1043, "SQLITE_CONSTRAINT_FUNCTION"},
+ {1299, "SQLITE_CONSTRAINT_NOTNULL"},
+ {1555, "SQLITE_CONSTRAINT_PRIMARYKEY"},
+ {2579, "SQLITE_CONSTRAINT_ROWID"},
+ {1811, "SQLITE_CONSTRAINT_TRIGGER"},
+ {2067, "SQLITE_CONSTRAINT_UNIQUE"},
+ {2323, "SQLITE_CONSTRAINT_VTAB"},
+ {267, "SQLITE_CORRUPT_VTAB"},
+ {3338, "SQLITE_IOERR_ACCESS"},
+ {2826, "SQLITE_IOERR_BLOCKED"},
+ {3594, "SQLITE_IOERR_CHECKRESERVEDLOCK"},
+ {4106, "SQLITE_IOERR_CLOSE"},
+ {6666, "SQLITE_IOERR_CONVPATH"},
+ {2570, "SQLITE_IOERR_DELETE"},
+ {5898, "SQLITE_IOERR_DELETE_NOENT"},
+ {4362, "SQLITE_IOERR_DIR_CLOSE"},
+ {1290, "SQLITE_IOERR_DIR_FSYNC"},
+ {1802, "SQLITE_IOERR_FSTAT"},
+ {1034, "SQLITE_IOERR_FSYNC"},
+ {6410, "SQLITE_IOERR_GETTEMPPATH"},
+ {3850, "SQLITE_IOERR_LOCK"},
+ {6154, "SQLITE_IOERR_MMAP"},
+ {3082, "SQLITE_IOERR_NOMEM"},
+ {2314, "SQLITE_IOERR_RDLOCK"},
+ {266, "SQLITE_IOERR_READ"},
+ {5642, "SQLITE_IOERR_SEEK"},
+ {5130, "SQLITE_IOERR_SHMLOCK"},
+ {5386, "SQLITE_IOERR_SHMMAP"},
+ {4618, "SQLITE_IOERR_SHMOPEN"},
+ {4874, "SQLITE_IOERR_SHMSIZE"},
+ {522, "SQLITE_IOERR_SHORT_READ"},
+ {1546, "SQLITE_IOERR_TRUNCATE"},
+ {2058, "SQLITE_IOERR_UNLOCK"},
+ {778, "SQLITE_IOERR_WRITE"},
+ {262, "SQLITE_LOCKED_SHAREDCACHE"},
+ {539, "SQLITE_NOTICE_RECOVER_ROLLBACK"},
+ {283, "SQLITE_NOTICE_RECOVER_WAL"},
+ {256, "SQLITE_OK_LOAD_PERMANENTLY"},
+ {520, "SQLITE_READONLY_CANTLOCK"},
+ {1032, "SQLITE_READONLY_DBMOVED"},
+ {264, "SQLITE_READONLY_RECOVERY"},
+ {776, "SQLITE_READONLY_ROLLBACK"},
+ {284, "SQLITE_WARNING_AUTOINDEX"},
+};
+
+static std::string sqlite3_error_code_to_msg(int errcode) {
+ auto it = sErrorCodesMap.find(errcode);
+ if (it != sErrorCodesMap.end()) {
+ return std::to_string(errcode) + " " + it->second;
+ } else {
+ return std::to_string(errcode);
+ }
+}
+
/* throw a SQLiteException with a message appropriate for the error in handle */
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
throw_sqlite3_exception(env, handle, NULL);
@@ -123,7 +223,8 @@ void throw_sqlite3_exception(JNIEnv* env, int errcode,
if (sqlite3Message) {
String8 fullMessage;
fullMessage.append(sqlite3Message);
- fullMessage.appendFormat(" (code %d)", errcode); // print extended error code
+ const char* errcode_msg = sqlite3_error_code_to_msg(errcode).c_str();
+ fullMessage.appendFormat(" (code %s)", errcode_msg); // print extended error code
if (message) {
fullMessage.append(": ");
fullMessage.append(message);
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 092aaf62bbc0..c79f5bd96e99 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -33,6 +33,9 @@
#define ENCODING_AAC_HE_V2 12
#define ENCODING_IEC61937 13
#define ENCODING_DOLBY_TRUEHD 14
+#define ENCODING_AAC_ELD 15
+#define ENCODING_AAC_XHE 16
+#define ENCODING_AC4 17
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -71,6 +74,12 @@ static inline audio_format_t audioFormatToNative(int audioFormat)
return AUDIO_FORMAT_DOLBY_TRUEHD;
case ENCODING_IEC61937:
return AUDIO_FORMAT_IEC61937;
+ case ENCODING_AAC_ELD:
+ return AUDIO_FORMAT_AAC_ELD;
+ case ENCODING_AAC_XHE:
+ return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
+ case ENCODING_AC4:
+ return AUDIO_FORMAT_AC4;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -114,6 +123,13 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
return ENCODING_IEC61937;
case AUDIO_FORMAT_DOLBY_TRUEHD:
return ENCODING_DOLBY_TRUEHD;
+ case AUDIO_FORMAT_AAC_ELD:
+ return ENCODING_AAC_ELD;
+ // FIXME needs addition of AUDIO_FORMAT_AAC_XHE
+ //case AUDIO_FORMAT_AAC_XHE:
+ // return ENCODING_AAC_XHE;
+ case AUDIO_FORMAT_AC4:
+ return ENCODING_AC4;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
@@ -121,6 +137,25 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
}
}
+// This function converts Java channel masks to a native channel mask.
+// validity should be checked with audio_is_output_channel().
+static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
+ jint channelPositionMask, jint channelIndexMask)
+{
+ // 0 is the java android.media.AudioFormat.CHANNEL_INVALID value
+ if (channelIndexMask != 0) { // channel index mask takes priority
+ // To convert to a native channel mask, the Java channel index mask
+ // requires adding the index representation.
+ return audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_INDEX,
+ channelIndexMask);
+ }
+ // To convert to a native channel mask, the Java channel position mask
+ // requires a shift by 2 to skip the two deprecated channel
+ // configurations "default" and "mono".
+ return (audio_channel_mask_t)((uint32_t)channelPositionMask >> 2);
+}
+
static inline audio_channel_mask_t outChannelMaskToNative(int channelMask)
{
switch (channelMask) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7ec68edddf52..2be947158fc5 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1770,6 +1770,24 @@ android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz,
(audio_devices_t)device);
}
+static jboolean
+android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz,
+ jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask)
+{
+ audio_offload_info_t format = AUDIO_INFO_INITIALIZER;
+ format.format = (audio_format_t) audioFormatToNative(encoding);
+ format.sample_rate = (uint32_t) sampleRate;
+ format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
+ format.stream_type = AUDIO_STREAM_MUSIC;
+ format.has_video = false;
+ format.is_streaming = false;
+ // offload duration unknown at this point:
+ // client side code cannot access "audio.offload.min.duration.secs" property to make a query
+ // agnostic of duration, so using acceptable estimate of 2mn
+ format.duration_us = 120 * 1000000;
+ return AudioSystem::isOffloadSupported(format);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1823,6 +1841,7 @@ static const JNINativeMethod gMethods[] = {
(void *)android_media_AudioSystem_registerRecordingCallback},
{"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},
};
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 556ac27dfe9e..11011b1d7c16 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -73,6 +73,7 @@ struct audiotrack_callback_cookie {
jobject audioTrack_ref;
bool busy;
Condition cond;
+ bool isOffload;
};
// keep these values in sync with AudioTrack.java
@@ -90,6 +91,7 @@ class AudioTrackJniStorage {
AudioTrackJniStorage() {
mCallbackData.audioTrack_class = 0;
mCallbackData.audioTrack_ref = 0;
+ mCallbackData.isOffload = false;
}
~AudioTrackJniStorage() {
@@ -132,27 +134,34 @@ static void audioCallback(int event, void* user, void *info) {
}
switch (event) {
- case AudioTrack::EVENT_MARKER: {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (user != NULL && env != NULL) {
- env->CallStaticVoidMethod(
- callbackInfo->audioTrack_class,
- javaAudioTrackFields.postNativeEventInJava,
- callbackInfo->audioTrack_ref, event, 0,0, NULL);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- env->ExceptionClear();
+ // Offload only events
+ case AudioTrack::EVENT_STREAM_END:
+ case AudioTrack::EVENT_MORE_DATA:
+ // a.k.a. tear down
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK:
+ if (callbackInfo->isOffload) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (user != NULL && env != NULL) {
+ env->CallStaticVoidMethod(
+ callbackInfo->audioTrack_class,
+ javaAudioTrackFields.postNativeEventInJava,
+ callbackInfo->audioTrack_ref, event, 0,0, NULL);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
}
- }
} break;
+ // PCM and offload events
+ case AudioTrack::EVENT_MARKER:
case AudioTrack::EVENT_NEW_POS: {
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user != NULL && env != NULL) {
env->CallStaticVoidMethod(
- callbackInfo->audioTrack_class,
- javaAudioTrackFields.postNativeEventInJava,
- callbackInfo->audioTrack_ref, event, 0,0, NULL);
+ callbackInfo->audioTrack_class,
+ javaAudioTrackFields.postNativeEventInJava,
+ callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
@@ -198,30 +207,12 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio
return getAudioTrack(env, audioTrackObj);
}
-// This function converts Java channel masks to a native channel mask.
-// validity should be checked with audio_is_output_channel().
-static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
- jint channelPositionMask, jint channelIndexMask)
-{
- if (channelIndexMask != 0) { // channel index mask takes priority
- // To convert to a native channel mask, the Java channel index mask
- // requires adding the index representation.
- return audio_channel_mask_from_representation_and_bits(
- AUDIO_CHANNEL_REPRESENTATION_INDEX,
- channelIndexMask);
- }
- // To convert to a native channel mask, the Java channel position mask
- // requires a shift by 2 to skip the two deprecated channel
- // configurations "default" and "mono".
- return (audio_channel_mask_t)(channelPositionMask >> 2);
-}
-
// ----------------------------------------------------------------------------
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
- jlong nativeAudioTrack) {
+ jlong nativeAudioTrack, jboolean offload) {
ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
"nativeAudioTrack=0x%" PRIX64,
@@ -322,8 +313,19 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
// we use a weak reference so the AudioTrack object can be garbage collected.
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+ lpJniStorage->mCallbackData.isOffload = offload;
lpJniStorage->mCallbackData.busy = false;
+ audio_offload_info_t offloadInfo;
+ if (offload) {
+ offloadInfo = AUDIO_INFO_INITIALIZER;
+ offloadInfo.format = format;
+ offloadInfo.sample_rate = sampleRateInHertz;
+ offloadInfo.channel_mask = nativeChannelMask;
+ offloadInfo.has_video = false;
+ offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
+ }
+
// initialize the native AudioTrack object
status_t status = NO_ERROR;
switch (memoryMode) {
@@ -342,7 +344,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SYNC,
- NULL, // default offloadInfo
+ offload ? &offloadInfo : NULL,
-1, -1, // default uid, pid values
paa);
break;
@@ -1234,7 +1236,7 @@ static const JNINativeMethod gMethods[] = {
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
{"native_flush", "()V", (void *)android_media_AudioTrack_flush},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
(void *)android_media_AudioTrack_setup},
{"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
{"native_release", "()V", (void *)android_media_AudioTrack_release},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 2752a7e07ff4..e5efb90c922e 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,7 +21,6 @@ option java_outer_classname = "IncidentProtoMetadata";
import "frameworks/base/core/proto/android/os/batterytype.proto";
import "frameworks/base/core/proto/android/os/cpufreq.proto";
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
-import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
import "frameworks/base/core/proto/android/os/procrank.proto";
@@ -46,6 +45,7 @@ import "frameworks/base/core/proto/android/service/print.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
+import "frameworks/base/libs/incident/proto/android/os/header.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 8753bf768321..c9f7d52ae83f 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -41,4 +41,13 @@ message ForceAppStandbyTrackerProto {
// Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
+
+ // Whether device is a small battery device
+ optional bool is_small_battery_device = 6;
+
+ // Whether force app standby for small battery device setting is enabled
+ optional bool force_all_apps_standby_for_small_battery = 7;
+
+ // Whether device is charging
+ optional bool is_charging = 8;
}
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index 23613fdabf76..ad9191cac2e4 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -63,6 +63,8 @@ message NetworkIdentityProto {
optional bool roaming = 4;
optional bool metered = 5;
+
+ optional bool default_network = 6;
}
// Corresponds to NetworkStatsRecorder.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 547e83c144a4..93d852ca1326 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1747,6 +1747,12 @@
<permission android:name="android.permission.SEND_EMBMS_INTENTS"
android:protectionLevel="signature|privileged" />
+
+ <!-- Allows internal management of the sensor framework
+ @hide -->
+ <permission android:name="android.permission.MANAGE_SENSORS"
+ android:protectionLevel="signature" />
+
<!-- Must be required by an ImsService to ensure that only the
system can bind to it.
<p>Protection level: signature|privileged
@@ -2307,6 +2313,11 @@
<permission android:name="android.permission.RECOVERY"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to read system update info.
+ @hide -->
+ <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
+ android:protectionLevel="signature" />
+
<!-- Allows the system to bind to an application's task services
@hide -->
<permission android:name="android.permission.BIND_JOB_SERVICE"
@@ -2837,6 +2848,14 @@
<permission android:name="android.permission.INSTALL_SELF_UPDATES"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to install updates. This is a limited version
+ of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an application to clear user data.
<p>Not for use by third-party applications
@hide
@@ -3186,10 +3205,14 @@
<permission android:name="android.permission.BIND_APPWIDGET"
android:protectionLevel="signature|privileged" />
+ <!-- @hide Allows sysui to manage user grants of slice permissions. -->
+ <permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to bind app's slices and get their
content. This content will be surfaced to the
user and not to leave the device.
- <p>Not for use by third-party applications. -->
+ <p>Not for use by third-party applications.-->
<permission android:name="android.permission.BIND_SLICE"
android:protectionLevel="signature|privileged|development" />
@@ -3692,11 +3715,20 @@
<permission android:name="android.permission.READ_RUNTIME_PROFILES"
android:protectionLevel="signature|privileged" />
+ <!-- @hide Allows audio policy management. -->
+ <permission android:name="android.permission.MANAGE_AUDIO_POLICY"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to turn on / off quiet mode.
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_QUIET_MODE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows internal management of the camera framework
+ @hide -->
+ <permission android:name="android.permission.MANAGE_CAMERA"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to control remote animations. See
{@link ActivityOptions#makeRemoteAnimation}
@hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/drawable/ic_screenshot.xml b/core/res/res/drawable/ic_screenshot.xml
index 3074b28497cf..24dd4d86edaf 100644
--- a/core/res/res/drawable/ic_screenshot.xml
+++ b/core/res/res/drawable/ic_screenshot.xml
@@ -17,10 +17,8 @@ Copyright (C) 2018 The Android Open Source Project
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M0,0h24v24H0V0z"
- android:fillColor="#00000000"/>
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M17.0,1.0L7.0,1.0C5.9,1.0 5.0,1.9 5.0,3.0l0.0,18.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L19.0,3.0C19.0,1.9 18.1,1.0 17.0,1.0zM17.0,20.0L7.0,20.0L7.0,4.0l10.0,0.0L17.0,20.0z"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f084159b3f58..c623c9aa0fb7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -430,7 +430,7 @@
<integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer>
<!-- Activity name to enable wifi tethering after provisioning app succeeds -->
- <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.TetherService</string>
+ <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
<!-- Controls the WiFi wakeup feature.
0 = Not available.
@@ -640,18 +640,12 @@
<!-- Wifi driver supports batched scan -->
<bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
- <!-- Idle Receive current for wifi radio. 0 by default-->
- <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
-
- <!-- Rx current for wifi radio. 0 by default-->
- <integer translatable="false" name="config_wifi_active_rx_cur_ma">0</integer>
-
- <!-- Tx current for wifi radio. 0 by default-->
- <integer translatable="false" name="config_wifi_tx_cur_ma">0</integer>
-
- <!-- Operating volatage for wifi radio. 0 by default-->
- <integer translatable="false" name="config_wifi_operating_voltage_mv">0</integer>
+ <!-- Wifi driver supports Automatic channel selection (ACS) for softap -->
+ <bool translatable="false" name="config_wifi_softap_acs_supported">false</bool>
+ <!-- Wifi driver supports IEEE80211AC for softap -->
+ <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
+
<!-- Flag indicating whether the we should enable the automatic brightness in Settings.
Software implementation will be used if config_hardware_auto_brightness_available is not set -->
<bool name="config_automatic_brightness_available">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2cfe919fb8f5..170ba4264e04 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4817,4 +4817,11 @@
<string name="harmful_app_warning_launch_anyway">Launch anyway</string>
<!-- Title for the harmful app warning dialog. -->
<string name="harmful_app_warning_title">Uninstall harmful app?</string>
+
+ <!-- Text describing a permission request for one app to show another app's
+ slices [CHAR LIMIT=NONE] -->
+ <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
+
+ <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
+ <string name="screenshot_edit">Edit</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1993ab4b5924..4ef0a6c93d9e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -309,6 +309,8 @@
<java-symbol type="bool" name="config_useFixedVolume" />
<java-symbol type="bool" name="config_forceDefaultOrientation" />
<java-symbol type="bool" name="config_wifi_batched_scan_supported" />
+ <java-symbol type="bool" name="config_wifi_softap_acs_supported" />
+ <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" />
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="bool" name="config_hasRecents" />
@@ -382,10 +384,6 @@
<java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
<java-symbol type="string" name="config_wifi_random_mac_oui" />
<java-symbol type="integer" name="config_wifi_network_switching_blacklist_time" />
- <java-symbol type="integer" name="config_wifi_idle_receive_cur_ma" />
- <java-symbol type="integer" name="config_wifi_active_rx_cur_ma" />
- <java-symbol type="integer" name="config_wifi_tx_cur_ma" />
- <java-symbol type="integer" name="config_wifi_operating_voltage_mv" />
<java-symbol type="string" name="config_wifi_framework_sap_2G_channel_list" />
<java-symbol type="integer" name="config_wifi_framework_max_tx_rate_for_full_scan" />
<java-symbol type="integer" name="config_wifi_framework_max_rx_rate_for_full_scan" />
@@ -1508,6 +1506,7 @@
<java-symbol type="xml" name="password_kbd_symbols" />
<java-symbol type="xml" name="password_kbd_symbols_shift" />
<java-symbol type="xml" name="power_profile" />
+ <java-symbol type="xml" name="power_profile_test" />
<java-symbol type="xml" name="sms_short_codes" />
<java-symbol type="xml" name="audio_assets" />
<java-symbol type="xml" name="global_keys" />
@@ -3224,4 +3223,8 @@
<java-symbol type="string" name="config_defaultAssistantAccessPackage" />
<java-symbol type="bool" name="config_supportBluetoothPersistedState" />
+
+ <java-symbol type="string" name="slices_permission_request" />
+
+ <java-symbol type="string" name="screenshot_edit" />
</resources>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 6e31cd289463..bc4b10f827ce 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -51,15 +51,6 @@
<value>0.1</value> <!-- ~1mA -->
</array>
-
- <!-- Radio related values. For modems WITH energy reporting support in firmware, use
- modem.controller.idle, modem.controller.tx, modem.controller.rx, modem.controller.voltage.
- -->
- <item name="modem.controller.idle">0</item>
- <item name="modem.controller.rx">0</item>
- <item name="modem.controller.tx">0</item>
- <item name="modem.controller.voltage">0</item>
-
<!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
number of CPU cores for that cluster.
@@ -123,4 +114,17 @@
<value>2</value> <!-- 4097-/hr -->
</array>
+ <!-- Cellular modem related values. Default is 0.-->
+ <item name="modem.controller.sleep">0</item>
+ <item name="modem.controller.idle">0</item>
+ <item name="modem.controller.rx">0</item>
+ <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+ <value>0</value>
+ <value>0</value>
+ <value>0</value>
+ <value>0</value>
+ <value>0</value>
+ </array>
+ <item name="modem.controller.voltage">0</item>
+
</device>
diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml
new file mode 100644
index 000000000000..cdb71343e5e5
--- /dev/null
+++ b/core/res/res/xml/power_profile_test.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<device name="Android">
+ <!-- All values are in mAh except as noted.
+ This file is for PowerProfileTest.java. Changes must be synced between these two. Since
+ power_profile.xml may be overridden by actual device's power_profile.xml at compile time,
+ this test config ensures we have something constant to test against. Values below are
+ sample values, not meant to reflect any real device.
+ -->
+
+ <!-- Nothing -->
+ <item name="none">0</item>
+
+ <!-- This is the battery capacity in mAh -->
+ <item name="battery.capacity">3000</item>
+
+ <!-- Number of cores each CPU cluster contains -->
+ <array name="cpu.clusters.cores">
+ <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) -->
+ <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) -->
+ </array>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">5</item>
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">1.11</item>
+ <!-- Additional power consumption by CPU excluding cluster and core when running -->
+ <item name="cpu.active">2.55</item>
+
+ <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster0">2.11</item>
+ <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster1">2.22</item>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster0">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2000000</value> <!-- 2000 MHz CPU speed -->
+ </array>
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster1">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2500000</value> <!-- 2500 MHz CPU speed -->
+ <value>3000000</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Additional power used by a CPU from cluster 0 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster0">
+ <value>10</value> <!-- 300 MHz CPU speed -->
+ <value>20</value> <!-- 1000 MHz CPU speed -->
+ <value>30</value> <!-- 1900 MHz CPU speed -->
+ </array>
+ <!-- Additional power used by a CPU from cluster 1 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster1">
+ <value>25</value> <!-- 300 MHz CPU speed -->
+ <value>35</value> <!-- 1000 MHz CPU speed -->
+ <value>50</value> <!-- 2500 MHz CPU speed -->
+ <value>60</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">100</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">800</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">500</item>
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">600</item>
+
+ <!-- Additional power used when audio decoding/encoding via DSP -->
+ <item name="dsp.audio">100</item>
+
+ <!-- Additional power used when GPS is acquiring a signal -->
+ <item name="gps.on">10</item>
+
+ <!-- Additional power used when cellular radio is transmitting/receiving -->
+ <item name="radio.active">60</item>
+ <!-- Additional power used when cellular radio is paging the tower -->
+ <item name="radio.scanning">3</item>
+ <!-- Additional power used when the cellular radio is on. Multi-value entry,
+ one per signal strength (no signal, weak, moderate, strong) -->
+ <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+ <value>6</value> <!-- none -->
+ <value>5</value> <!-- poor -->
+ <value>4</value> <!-- moderate -->
+ <value>3</value> <!-- good -->
+ <value>3</value> <!-- great -->
+ </array>
+</device>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 37b318065918..fa0ea5c3aa85 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -213,6 +213,7 @@ public class SettingsBackupTest {
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
Settings.Global.GLOBAL_HTTP_PROXY_HOST,
@@ -534,7 +535,9 @@ public class SettingsBackupTest {
Settings.Secure.VOICE_RECOGNITION_SERVICE,
Settings.Secure.INSTANT_APPS_ENABLED,
Settings.Secure.BACKUP_MANAGER_CONSTANTS,
- Settings.Secure.KEYGUARD_SLICE_URI);
+ Settings.Secure.KEYGUARD_SLICE_URI,
+ Settings.Secure.PARENTAL_CONTROL_ENABLED,
+ Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
index 00732b09f821..4c4aeaf49855 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
@@ -36,20 +36,28 @@ public class SettingsValidatorsTest {
@Test
public void ensureAllBackedUpSystemSettingsHaveValidators() {
- String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP,
- Settings.System.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
+ Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
failIfOffendersPresent(offenders, "Settings.System");
}
@Test
public void ensureAllBackedUpGlobalSettingsHaveValidators() {
- String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP,
- Settings.Global.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
+ Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
failIfOffendersPresent(offenders, "Settings.Global");
}
+ @Test
+ public void ensureAllBackedUpSecureSettingsHaveValidators() {
+ String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
+ Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
+
+ failIfOffendersPresent(offenders, "Settings.Secure");
+ }
+
private void failIfOffendersPresent(String offenders, String settingsType) {
if (offenders.length() > 0) {
fail("All " + settingsType + " settings that are backed up have to have a non-null"
@@ -66,4 +74,16 @@ public class SettingsValidatorsTest {
}
return offenders.toString();
}
+
+ private String[] concat(String[] first, String[] second) {
+ if (second == null || second.length == 0) {
+ return first;
+ }
+ final int firstLen = first.length;
+ final int secondLen = second.length;
+ String[] both = new String[firstLen + secondLen];
+ System.arraycopy(first, 0, both, 0, firstLen);
+ System.arraycopy(second, 0, both, firstLen, secondLen);
+ return both;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index b5a7bec75197..32053e30e886 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -62,9 +62,9 @@ import java.util.Arrays;
*
* Build: m FrameworksCoreTests
* Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
* Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
*
* or
*
@@ -73,10 +73,18 @@ import java.util.Arrays;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryStatsCpuTimesTest {
- @Mock KernelUidCpuTimeReader mKernelUidCpuTimeReader;
- @Mock KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
- @Mock BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
- @Mock PowerProfile mPowerProfile;
+ @Mock
+ KernelUidCpuTimeReader mKernelUidCpuTimeReader;
+ @Mock
+ KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
+ @Mock
+ KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader;
+ @Mock
+ KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader;
+ @Mock
+ BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
+ @Mock
+ PowerProfile mPowerProfile;
private MockClocks mClocks;
private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -90,6 +98,8 @@ public class BatteryStatsCpuTimesTest {
mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
.setKernelUidCpuTimeReader(mKernelUidCpuTimeReader)
.setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader)
+ .setKernelUidCpuActiveTimeReader(mKernelUidCpuActiveTimeReader)
+ .setKernelUidCpuClusterTimeReader(mKernelUidCpuClusterTimeReader)
.setUserInfoProvider(mUserInfoProvider);
}
@@ -134,6 +144,10 @@ public class BatteryStatsCpuTimesTest {
verify(mKernelUidCpuFreqTimeReader, times(2)).perClusterTimesAvailable();
verify(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
+ verify(mKernelUidCpuActiveTimeReader).readDelta(
+ any(KernelUidCpuActiveTimeReader.Callback.class));
+ verify(mKernelUidCpuClusterTimeReader).readDelta(
+ any(KernelUidCpuClusterTimeReader.Callback.class));
verifyNoMoreInteractions(mKernelUidCpuFreqTimeReader);
for (int i = 0; i < numClusters; ++i) {
verify(mKernelCpuSpeedReaders[i]).readDelta();
@@ -228,7 +242,7 @@ public class BatteryStatsCpuTimesTest {
updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -301,7 +315,7 @@ public class BatteryStatsCpuTimesTest {
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
isolatedAppId,
FIRST_APPLICATION_UID + 33
@@ -389,7 +403,7 @@ public class BatteryStatsCpuTimesTest {
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -431,7 +445,7 @@ public class BatteryStatsCpuTimesTest {
updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -514,10 +528,10 @@ public class BatteryStatsCpuTimesTest {
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
- FIRST_APPLICATION_UID + 22,
- FIRST_APPLICATION_UID + 27,
- FIRST_APPLICATION_UID + 33
+ final int[] testUids = getUids(testUserId, new int[]{
+ FIRST_APPLICATION_UID + 22,
+ FIRST_APPLICATION_UID + 27,
+ FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
@@ -589,7 +603,7 @@ public class BatteryStatsCpuTimesTest {
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -693,7 +707,7 @@ public class BatteryStatsCpuTimesTest {
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -782,7 +796,7 @@ public class BatteryStatsCpuTimesTest {
}
}
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
- for (int speed = 0 ; speed < clusterFreqs[cluster]; ++speed) {
+ for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) {
assertEquals("There shouldn't be any left-overs: "
+ Arrays.deepToString(expectedWakeLockUidTimesUs),
0, expectedWakeLockUidTimesUs[cluster][speed]);
@@ -797,7 +811,7 @@ public class BatteryStatsCpuTimesTest {
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -874,7 +888,7 @@ public class BatteryStatsCpuTimesTest {
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
isolatedAppId,
FIRST_APPLICATION_UID + 33
@@ -969,7 +983,7 @@ public class BatteryStatsCpuTimesTest {
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
- final int[] testUids = getUids(testUserId, new int[] {
+ final int[] testUids = getUids(testUserId, new int[]{
FIRST_APPLICATION_UID + 22,
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
@@ -986,7 +1000,7 @@ public class BatteryStatsCpuTimesTest {
callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
- callback.onUidCpuFreqTime(invalidUid, new long[] {12, 839, 32, 34, 21});
+ callback.onUidCpuFreqTime(invalidUid, new long[]{12, 839, 32, 34, 21});
return null;
}).when(mKernelUidCpuFreqTimeReader).readDelta(
any(KernelUidCpuFreqTimeReader.Callback.class));
@@ -1009,6 +1023,136 @@ public class BatteryStatsCpuTimesTest {
verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
}
+ @Test
+ public void testReadKernelUidCpuActiveTimesLocked() {
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+ final int testUserId = 11;
+ when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+ final int[] testUids = getUids(testUserId, new int[]{
+ FIRST_APPLICATION_UID + 22,
+ FIRST_APPLICATION_UID + 27,
+ FIRST_APPLICATION_UID + 33
+ });
+ final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
+ doAnswer(invocation -> {
+ final KernelUidCpuActiveTimeReader.Callback callback =
+ (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuActiveTimeReader).readDelta(
+ any(KernelUidCpuActiveTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i],
+ u.getCpuActiveTime());
+ }
+
+ // Repeat the test when the screen is off.
+
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+ final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000};
+ doAnswer(invocation -> {
+ final KernelUidCpuActiveTimeReader.Callback callback =
+ (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuActiveTime(testUids[i], deltasMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuActiveTimeReader).readDelta(
+ any(KernelUidCpuActiveTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertEquals("Unexpected cpu active time for uid=" + testUids[i],
+ uidTimesMs[i] + deltasMs[i], u.getCpuActiveTime());
+ }
+ }
+
+ @Test
+ public void testReadKernelUidCpuClusterTimesLocked() {
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+ final int testUserId = 11;
+ when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+ final int[] testUids = getUids(testUserId, new int[]{
+ FIRST_APPLICATION_UID + 22,
+ FIRST_APPLICATION_UID + 27,
+ FIRST_APPLICATION_UID + 33
+ });
+ final long[][] uidTimesMs = {
+ {4000, 10000},
+ {5000, 1000},
+ {8000, 0}
+ };
+ doAnswer(invocation -> {
+ final KernelUidCpuClusterTimeReader.Callback callback =
+ (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuClusterTimeReader).readDelta(
+ any(KernelUidCpuClusterTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], uidTimesMs[i],
+ u.getCpuClusterTimes());
+ }
+
+ // Repeat the test when the screen is off.
+
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+ final long[][] deltasMs = {
+ {3000, 12000},
+ {3248327490475L, 0},
+ {43000, 3345000}
+ };
+ doAnswer(invocation -> {
+ final KernelUidCpuClusterTimeReader.Callback callback =
+ (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuPolicyTime(testUids[i], deltasMs[i]);
+ }
+ return null;
+ }).when(mKernelUidCpuClusterTimeReader).readDelta(
+ any(KernelUidCpuClusterTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked();
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], sum(uidTimesMs[i], deltasMs[i]),
+ u.getCpuClusterTimes());
+ }
+ }
+
private void updateTimeBasesLocked(boolean unplugged, int screenState,
long upTime, long realTime) {
// Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index e8f24567599e..702f4b8c0dc5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,8 +39,11 @@ import org.junit.runners.Suite;
KernelMemoryBandwidthStatsTest.class,
KernelSingleUidTimeReaderTest.class,
KernelUidCpuFreqTimeReaderTest.class,
+ KernelUidCpuActiveTimeReaderTest.class,
+ KernelUidCpuClusterTimeReaderTest.class,
KernelWakelockReaderTest.class,
- LongSamplingCounterArrayTest.class
+ LongSamplingCounterArrayTest.class,
+ PowerProfileTest.class
})
public class BatteryStatsTests {
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
new file mode 100644
index 000000000000..1ac82bd1dc96
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelUidCpuActiveTimeReader}.
+ *
+ * To run it:
+ * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest
+ *
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelUidCpuActiveTimeReaderTest {
+ @Mock private BufferedReader mBufferedReader;
+ @Mock private KernelUidCpuActiveTimeReader.Callback mCallback;
+
+ private KernelUidCpuActiveTimeReader mReader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mReader = new KernelUidCpuActiveTimeReader();
+ }
+
+ public class Temp {
+
+ public void method() {
+ method1(new long[][]{{1,2,3}, {2,3,4}});
+ method1(new long[][]{{2,2,3}, {2,3,4}});
+ }
+ public int method1(long[][] array) {
+ return array.length * array[0].length;
+ }
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final int cores = 8;
+ final String info = "active: 8";
+ final int[] uids = {1, 22, 333, 4444, 5555};
+
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that a second call will only return deltas.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times1 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ Mockito.reset(mCallback, mBufferedReader);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times1);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, null);
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i = 0; i < uids.length; ++i) {
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+ }
+
+ @Test
+ public void testReadDelta_malformedData() throws Exception {
+ final int cores = 8;
+ final String info = "active: 8";
+ final int[] uids = {1, 22, 333, 4444, 5555};
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there is no callback if subsequent call provides wrong # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] temp = increaseTime(times);
+ final long[][] times1 = new long[uids.length][];
+ for(int i=0;i<temp.length;i++){
+ times1[i] = Arrays.copyOfRange(temp[i], 0, 6);
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified if the given core count does not match
+ // the following # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for(int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there is no callback if any value in the proc file is -ve.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ times3[uids.length - 1][cores - 1] *= -1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i = 0; i < uids.length - 1; ++i) {
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had -ve value.
+ Mockito.reset(mCallback, mBufferedReader);
+ for (int i = 0; i < cores; i++) {
+ times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1])));
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that there is no callback if the values in the proc file are decreased.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times4 = increaseTime(times3);
+ times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i = 0; i < uids.length - 1; ++i) {
+ verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had decreased values.
+ Mockito.reset(mCallback, mBufferedReader);
+ for (int i = 0; i < cores; i++) {
+ times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1])));
+ verifyNoMoreInteractions(mCallback);
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private String[] formatTime(int[] uids, long[][] times) {
+ String[] lines = new String[uids.length + 1];
+ for (int i=0;i<uids.length;i++){
+ StringBuilder sb = new StringBuilder();
+ sb.append(uids[i]).append(':');
+ for(int j=0;j<times[i].length;j++){
+ sb.append(' ').append(times[i][j]);
+ }
+ lines[i] = sb.toString();
+ }
+ lines[uids.length] = null;
+ return lines;
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ Random rand = new Random();
+ for(int i = 0;i<original.length;i++){
+ for(int j=0;j<original[0].length;j++){
+ newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+ }
+ }
+ return newTime;
+ }
+
+ private long getTotal(long[] times) {
+ long sum = 0;
+ for(int i=0;i<times.length;i++){
+ sum+=times[i] * 10 / (i+1);
+ }
+ return sum;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
new file mode 100644
index 000000000000..0d1f85277ebb
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelUidCpuClusterTimeReader}.
+ *
+ * To run it:
+ * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest
+ *
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelUidCpuClusterTimeReaderTest {
+ @Mock private BufferedReader mBufferedReader;
+ @Mock private KernelUidCpuClusterTimeReader.Callback mCallback;
+
+ private KernelUidCpuClusterTimeReader mReader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mReader = new KernelUidCpuClusterTimeReader();
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final String info = "policy0: 2 policy4: 4";
+ final int cores = 6;
+ final int[] cluster = {2, 4};
+ final int[] uids = {1, 22, 333, 4444, 5555};
+
+ // Verify initial call
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+ }
+
+ // Verify that a second call will only return deltas.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times1 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times1[i], times[i])));
+ }
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ Mockito.reset(mCallback, mBufferedReader);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times1);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, null);
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+ }
+
+ }
+
+ @Test
+ public void testReadDelta_malformedData() throws Exception {
+ final String info = "policy0: 2 policy4: 4";
+ final int cores = 6;
+ final int[] cluster = {2, 4};
+ final int[] uids = {1, 22, 333, 4444, 5555};
+
+ // Verify initial call
+ final long[][] times = increaseTime(new long[uids.length][cores]);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+ }
+
+ // Verify that there is no callback if subsequent call provides wrong # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] temp = increaseTime(times);
+ final long[][] times1 = new long[uids.length][];
+ for(int i=0;i<temp.length;i++){
+ times1[i] = Arrays.copyOfRange(temp[i], 0, 4);
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified if the given core count does not match
+ // the following # of entries.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times2 = increaseTime(times);
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times2[i], times[i])));
+ }
+
+ // Verify that there is no callback if any value in the proc file is -ve.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times3 = increaseTime(times2);
+ times3[uids.length - 1][cores - 1] *= -1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length-1;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had -ve value.
+ Mockito.reset(mCallback, mBufferedReader);
+ for(int i=0;i<cores;i++){
+ times3[uids.length -1][i] = times2[uids.length -1][i] + uids[uids.length -1]*1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback, times(1)).onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
+
+ // // Verify that there is no callback if the values in the proc file are decreased.
+ Mockito.reset(mCallback, mBufferedReader);
+ final long[][] times4 = increaseTime(times3);
+ times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ for (int i=0;i<uids.length-1;i++){
+ verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times4[i], times3[i])));
+ }
+ verifyNoMoreInteractions(mCallback);
+
+ // Verify that the internal state was not modified when the proc file had decreased values.
+ Mockito.reset(mCallback, mBufferedReader);
+ for(int i=0;i<cores;i++){
+ times4[uids.length -1][i] = times3[uids.length -1][i] + uids[uids.length -1]*1000;
+ }
+ when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+ mReader.readDeltaInternal(mBufferedReader, mCallback);
+ verify(mCallback, times(1))
+ .onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
+
+ }
+
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private String[] formatTime(int[] uids, long[][] times) {
+ String[] lines = new String[uids.length + 1];
+ for (int i=0;i<uids.length;i++){
+ StringBuilder sb = new StringBuilder();
+ sb.append(uids[i]).append(':');
+ for(int j=0;j<times[i].length;j++){
+ sb.append(' ').append(times[i][j]);
+ }
+ lines[i] = sb.toString();
+ }
+ lines[uids.length] = null;
+ return lines;
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ Random rand = new Random();
+ for(int i = 0;i<original.length;i++){
+ for(int j=0;j<original[0].length;j++){
+ newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+ }
+ }
+ return newTime;
+ }
+
+ // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader
+ private long[] getTotal(int[] cluster, long[] times) {
+ int core = 0;
+ long[] sum = new long[cluster.length];
+ for(int i=0;i<cluster.length;i++){
+ for(int j=0;j<cluster[i];j++){
+ sum[i] += times[core++] * 10 / (j+1);
+ }
+ }
+ return sum;
+ }
+
+ // Compare array1 against flattened 2d array array2 element by element
+ private boolean testEqual(long[] array1, long[][] array2) {
+ int k=0;
+ for(int i=0;i<array2.length;i++){
+ for(int j=0;j<array2[i].length;j++){
+ if (k >= array1.length || array1[k++]!=array2[i][j])return false;
+ }
+ }
+ return k == array1.length;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 6c5a2aac159b..660c744f050d 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -88,6 +88,16 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
return this;
}
+ public MockBatteryStatsImpl setKernelUidCpuActiveTimeReader(KernelUidCpuActiveTimeReader reader) {
+ mKernelUidCpuActiveTimeReader = reader;
+ return this;
+ }
+
+ public MockBatteryStatsImpl setKernelUidCpuClusterTimeReader(KernelUidCpuClusterTimeReader reader) {
+ mKernelUidCpuClusterTimeReader = reader;
+ return this;
+ }
+
public MockBatteryStatsImpl setKernelUidCpuTimeReader(KernelUidCpuTimeReader reader) {
mKernelUidCpuTimeReader = reader;
return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
new file mode 100644
index 000000000000..eb7da9ca4ffc
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+
+package com.android.internal.os;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/*
+ * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml
+ */
+@SmallTest
+public class PowerProfileTest extends TestCase {
+
+ private PowerProfile mProfile;
+
+ @Before
+ public void setUp() {
+ mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true);
+ }
+
+ @Test
+ public void testPowerProfile() {
+ assertEquals(2, mProfile.getNumCpuClusters());
+ assertEquals(4, mProfile.getNumCoresInCpuCluster(0));
+ assertEquals(4, mProfile.getNumCoresInCpuCluster(1));
+ assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND));
+ assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
+ assertEquals(2.55, mProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
+ assertEquals(2.11, mProfile.getAveragePowerForCpuCluster(0));
+ assertEquals(2.22, mProfile.getAveragePowerForCpuCluster(1));
+ assertEquals(3, mProfile.getNumSpeedStepsInCpuCluster(0));
+ assertEquals(30.0, mProfile.getAveragePowerForCpuCore(0, 2));
+ assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1));
+ assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
+ assertEquals(3000.0, mProfile.getBatteryCapacity());
+ }
+
+}
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 91664381efe0..6fe19a263e8b 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -72,13 +72,13 @@ public class LongitudinalReportingEncoderTest {
final LongitudinalReportingEncoder encoder =
LongitudinalReportingEncoder.createInsecureEncoderForTest(
config);
- assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
- assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
@@ -86,12 +86,12 @@ public class LongitudinalReportingEncoderTest {
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
- assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
- assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
// Test if IRR returns original result when f = 0
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index dad98b8e4a35..fa0343df88b4 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -80,7 +80,7 @@ public class RapporEncoderTest {
int numBits = 8;
final long inputValue = 254L;
final long prrValue = 250L;
- final long prrAndIrrValue = 184L;
+ final long prrAndIrrValue = 244L;
final RapporConfig config1 = new RapporConfig(
"Foo", // encoderId
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 993bae1eea00..4be6408dca16 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -162,9 +162,13 @@
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" />
<assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="cameraserver" />
+ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
+ <assign-permission name="android.permission.DUMP" uid="incidentd" />
+ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" />
+
<assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
<assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
diff --git a/docs/html/reference/images/text/style/absolutesizespan.png b/docs/html/reference/images/text/style/absolutesizespan.png
new file mode 100644
index 000000000000..40d5a79ae37e
--- /dev/null
+++ b/docs/html/reference/images/text/style/absolutesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/backgroundcolorspan.png b/docs/html/reference/images/text/style/backgroundcolorspan.png
new file mode 100644
index 000000000000..e7e72714c5bb
--- /dev/null
+++ b/docs/html/reference/images/text/style/backgroundcolorspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/foregroundcolorspan.png b/docs/html/reference/images/text/style/foregroundcolorspan.png
new file mode 100644
index 000000000000..f60db6c55254
--- /dev/null
+++ b/docs/html/reference/images/text/style/foregroundcolorspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/relativesizespan.png b/docs/html/reference/images/text/style/relativesizespan.png
new file mode 100644
index 000000000000..eaca5ad0e93f
--- /dev/null
+++ b/docs/html/reference/images/text/style/relativesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/scalexspan.png b/docs/html/reference/images/text/style/scalexspan.png
new file mode 100644
index 000000000000..a5ca26f571c6
--- /dev/null
+++ b/docs/html/reference/images/text/style/scalexspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/strikethroughspan.png b/docs/html/reference/images/text/style/strikethroughspan.png
new file mode 100644
index 000000000000..a49ecadea063
--- /dev/null
+++ b/docs/html/reference/images/text/style/strikethroughspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/subscriptspan.png b/docs/html/reference/images/text/style/subscriptspan.png
new file mode 100644
index 000000000000..aac7092a3322
--- /dev/null
+++ b/docs/html/reference/images/text/style/subscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/superscriptspan.png b/docs/html/reference/images/text/style/superscriptspan.png
new file mode 100644
index 000000000000..996f59d55dde
--- /dev/null
+++ b/docs/html/reference/images/text/style/superscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/underlinespan.png b/docs/html/reference/images/text/style/underlinespan.png
new file mode 100644
index 000000000000..dbcd0d9f1498
--- /dev/null
+++ b/docs/html/reference/images/text/style/underlinespan.png
Binary files differ
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 05dadc97a5ba..4d715d1cb9ea 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -33,6 +33,8 @@ import android.graphics.drawable.NinePatchDrawable;
import android.net.Uri;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
import libcore.io.IoUtils;
import dalvik.system.CloseGuard;
@@ -64,6 +66,19 @@ public final class ImageDecoder implements AutoCloseable {
Resources getResources() { return null; }
/* @hide */
+ int getDensity() { return Bitmap.DENSITY_NONE; }
+
+ /* @hide */
+ int computeDstDensity() {
+ Resources res = getResources();
+ if (res == null) {
+ return Bitmap.getDefaultDensity();
+ }
+
+ return res.getDisplayMetrics().densityDpi;
+ }
+
+ /* @hide */
abstract ImageDecoder createImageDecoder() throws IOException;
};
@@ -170,26 +185,73 @@ public final class ImageDecoder implements AutoCloseable {
return decoder;
}
+ private static class InputStreamSource extends Source {
+ InputStreamSource(Resources res, InputStream is, int inputDensity) {
+ if (is == null) {
+ throw new IllegalArgumentException("The InputStream cannot be null");
+ }
+ mResources = res;
+ mInputStream = is;
+ mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
+ }
+
+ final Resources mResources;
+ InputStream mInputStream;
+ final int mInputDensity;
+
+ @Override
+ public Resources getResources() { return mResources; }
+
+ @Override
+ public int getDensity() { return mInputDensity; }
+
+ @Override
+ public ImageDecoder createImageDecoder() throws IOException {
+
+ synchronized (this) {
+ if (mInputStream == null) {
+ throw new IOException("Cannot reuse InputStreamSource");
+ }
+ InputStream is = mInputStream;
+ mInputStream = null;
+ return createFromStream(is);
+ }
+ }
+ }
+
private static class ResourceSource extends Source {
ResourceSource(Resources res, int resId) {
mResources = res;
mResId = resId;
+ mResDensity = Bitmap.DENSITY_NONE;
}
final Resources mResources;
final int mResId;
+ int mResDensity;
@Override
public Resources getResources() { return mResources; }
@Override
+ public int getDensity() { return mResDensity; }
+
+ @Override
public ImageDecoder createImageDecoder() throws IOException {
// This is just used in order to access the underlying Asset and
// keep it alive. FIXME: Can we skip creating this object?
InputStream is = null;
ImageDecoder decoder = null;
+ TypedValue value = new TypedValue();
try {
- is = mResources.openRawResource(mResId);
+ is = mResources.openRawResource(mResId, value);
+
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ mResDensity = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ mResDensity = value.density;
+ }
+
if (!(is instanceof AssetManager.AssetInputStream)) {
// This should never happen.
throw new RuntimeException("Resource is not an asset?");
@@ -421,6 +483,22 @@ public final class ImageDecoder implements AutoCloseable {
}
/**
+ * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+ * @hide
+ */
+ public static Source createSource(Resources res, InputStream is) {
+ return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
+ }
+
+ /**
+ * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+ * @hide
+ */
+ public static Source createSource(Resources res, InputStream is, int density) {
+ return new InputStreamSource(res, is, density);
+ }
+
+ /**
* Return the width and height of a given sample size.
*
* This takes an input that functions like
@@ -476,6 +554,10 @@ public final class ImageDecoder implements AutoCloseable {
this.resize(dimensions.x, dimensions.y);
}
+ private boolean requestedResize() {
+ return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
+ }
+
// These need to stay in sync with ImageDecoder.cpp's Allocator enum.
/**
* Use the default allocation for the pixel memory.
@@ -730,6 +812,9 @@ public final class ImageDecoder implements AutoCloseable {
"Drawable!");
}
+ // this call potentially manipulates the decoder so it must be performed prior to
+ // decoding the bitmap and after decode set the density on the resulting bitmap
+ final int srcDensity = computeDensity(src, decoder);
if (decoder.mAnimated) {
// AnimatedImageDrawable calls postProcessAndRelease only if
// mPostProcess exists.
@@ -737,7 +822,8 @@ public final class ImageDecoder implements AutoCloseable {
null : decoder;
Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
postProcessPtr, decoder.mDesiredWidth,
- decoder.mDesiredHeight, decoder.mCropRect,
+ decoder.mDesiredHeight, srcDensity,
+ src.computeDstDensity(), decoder.mCropRect,
decoder.mInputStream, decoder.mAssetFd);
// d has taken ownership of these objects.
decoder.mInputStream = null;
@@ -746,13 +832,15 @@ public final class ImageDecoder implements AutoCloseable {
}
Bitmap bm = decoder.decodeBitmap();
- Resources res = src.getResources();
- if (res == null) {
- bm.setDensity(Bitmap.DENSITY_NONE);
- }
+ bm.setDensity(srcDensity);
+ Resources res = src.getResources();
byte[] np = bm.getNinePatchChunk();
if (np != null && NinePatch.isNinePatchChunk(np)) {
+ if (res != null) {
+ bm.setDensity(res.getDisplayMetrics().densityDpi);
+ }
+
Rect opticalInsets = new Rect();
bm.getOpticalInsets(opticalInsets);
Rect padding = new Rect();
@@ -799,8 +887,46 @@ public final class ImageDecoder implements AutoCloseable {
}
}
- return decoder.decodeBitmap();
+ // this call potentially manipulates the decoder so it must be performed prior to
+ // decoding the bitmap
+ final int srcDensity = computeDensity(src, decoder);
+ Bitmap bm = decoder.decodeBitmap();
+ bm.setDensity(srcDensity);
+ return bm;
+ }
+ }
+
+ // This method may modify the decoder so it must be called prior to performing the decode
+ private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) {
+ // if the caller changed the size then we treat the density as unknown
+ if (decoder.requestedResize()) {
+ return Bitmap.DENSITY_NONE;
+ }
+
+ // Special stuff for compatibility mode: if the target density is not
+ // the same as the display density, but the resource -is- the same as
+ // the display density, then don't scale it down to the target density.
+ // This allows us to load the system's density-correct resources into
+ // an application in compatibility mode, without scaling those down
+ // to the compatibility density only to have them scaled back up when
+ // drawn to the screen.
+ Resources res = src.getResources();
+ final int srcDensity = src.getDensity();
+ if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
+ return srcDensity;
}
+
+ // downscale the bitmap if the asset has a higher density than the default
+ final int dstDensity = src.computeDstDensity();
+ if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) {
+ float scale = (float) dstDensity / srcDensity;
+ int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
+ int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
+ decoder.resize(scaledWidth, scaledHeight);
+ return dstDensity;
+ }
+
+ return srcDensity;
}
private String getMimeType() {
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index ce3bd9af73b6..da170c0fae24 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -20,12 +20,14 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
+import android.util.DisplayMetrics;
import libcore.io.IoUtils;
import libcore.util.NativeAllocationRegistry;
@@ -59,22 +61,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable {
* decoder is only non-null if it has a PostProcess
*/
public AnimatedImageDrawable(long nativeImageDecoder,
- @Nullable ImageDecoder decoder, int width, int height, Rect cropRect,
+ @Nullable ImageDecoder decoder, int width, int height,
+ int srcDensity, int dstDensity, Rect cropRect,
InputStream inputStream, AssetFileDescriptor afd)
throws IOException {
- mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
- mInputStream = inputStream;
- mAssetFd = afd;
+ width = Bitmap.scaleFromDensity(width, srcDensity, dstDensity);
+ height = Bitmap.scaleFromDensity(height, srcDensity, dstDensity);
if (cropRect == null) {
mIntrinsicWidth = width;
mIntrinsicHeight = height;
} else {
+ cropRect.set(Bitmap.scaleFromDensity(cropRect.left, srcDensity, dstDensity),
+ Bitmap.scaleFromDensity(cropRect.top, srcDensity, dstDensity),
+ Bitmap.scaleFromDensity(cropRect.right, srcDensity, dstDensity),
+ Bitmap.scaleFromDensity(cropRect.bottom, srcDensity, dstDensity));
mIntrinsicWidth = cropRect.width();
mIntrinsicHeight = cropRect.height();
}
- long nativeSize = nNativeByteSize(mNativePtr);
+ mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
+ mInputStream = inputStream;
+ mAssetFd = afd;
+
+ // FIXME: Use the right size for the native allocation.
+ long nativeSize = 200;
NativeAllocationRegistry registry = new NativeAllocationRegistry(
AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
registry.registerNativeAllocation(this, mNativePtr);
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e3740e3cf284..f74c39d84bce 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")
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),
+ (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
+ } 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")
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),
+ (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
+ } 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, (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
} 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..291b0a0be4f4 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, (info, decoder) -> {
+ decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+ });
+ } 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/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index e2f02df62b30..77925fd87fc7 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -142,6 +142,7 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
SkPaint* paint) {
+ paint->setFilterQuality(kLow_SkFilterQuality);
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
@@ -200,18 +201,15 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
// composing a hardware layer
if (renderNode->getLayerSurface() && mComposeLayer) {
SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
- SkPaint* paint = nullptr;
- SkPaint tmpPaint;
- if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
- paint = &tmpPaint;
- }
+ SkPaint paint;
+ layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
// surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
// we need to restrict the portion of the surface drawn to the size of the renderNode.
SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
- bounds, bounds, paint);
+ bounds, bounds, &paint);
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index c7f57fee1d5a..2953ea8b21e9 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1046,6 +1046,40 @@ TEST(RenderNodeDrawable, renderNode) {
EXPECT_EQ(2, canvas.mDrawCounter);
}
+// Verify that layers are composed with kLow_SkFilterQuality filter quality.
+RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+ static const int CANVAS_WIDTH = 1;
+ static const int CANVAS_HEIGHT = 1;
+ static const int LAYER_WIDTH = 1;
+ static const int LAYER_HEIGHT = 1;
+ class FrameTestCanvas : public TestCanvasBase {
+ public:
+ FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+ void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint) override {
+ mDrawCounter++;
+ EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
+ }
+ };
+
+ auto layerNode = TestUtils::createSkiaNode(
+ 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
+ [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ canvas.drawPaint(SkPaint());
+ });
+
+ layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT));
+
+ FrameTestCanvas canvas;
+ RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed
+
+ // clean up layer pointer, so we can safely destruct RenderNode
+ layerNode->setLayerSurface(nullptr);
+}
+
TEST(ReorderBarrierDrawable, testShadowMatrix) {
static const int CANVAS_WIDTH = 100;
static const int CANVAS_HEIGHT = 100;
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
index 5f3e4071afef..b63400f14609 100644
--- a/libs/incident/Android.mk
+++ b/libs/incident/Android.mk
@@ -32,6 +32,7 @@ LOCAL_C_INCLUDES := \
LOCAL_SRC_FILES := \
../../core/java/android/os/IIncidentManager.aidl \
../../core/java/android/os/IIncidentReportStatusListener.aidl \
+ proto/android/os/header.proto \
src/IncidentReportArgs.cpp
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index 2849d580d88b..c56f689b7419 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -24,6 +24,8 @@
#include <set>
#include <vector>
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+
namespace android {
namespace os {
@@ -47,7 +49,7 @@ public:
void setAll(bool all);
void setDest(int dest);
void addSection(int section);
- void addHeader(const vector<uint8_t>& header);
+ void addHeader(const IncidentHeaderProto& headerProto);
inline bool all() const { return mAll; }
bool containsSection(int section) const;
diff --git a/core/proto/android/os/incidentheader.proto b/libs/incident/proto/android/os/header.proto
index f0c736a866a6..a84dc48b8b34 100644
--- a/core/proto/android/os/incidentheader.proto
+++ b/libs/incident/proto/android/os/header.proto
@@ -19,31 +19,22 @@ option java_multiple_files = true;
package android.os;
-// IncidentHeaderProto contains extra information the caller of incidentd want to
-// attach in an incident report, the data should just be informative.
+// IncidentHeaderProto contains extra information the caller of incidentd wants
+// to attach in an incident report, the data should just be informative.
message IncidentHeaderProto {
-
- // From statsd config, the name of the anomaly, usually human readable.
- optional string incident_name = 1;
+ // From statsd config, the id of the anomaly alert, unique among alerts.
+ optional int64 alert_id = 1;
// Format a human readable reason why an incident report is requested.
- // It's optional and may directly come from a user clicking the bug-report button.
+ // It's optional and may directly come from a user input clicking the
+ // bug-report button.
optional string reason = 2;
- // From statsd, the metric which causes the anomaly triggered.
- optional string metric_name = 3;
-
- // From statsd, the metric value exceeds the threshold. This is useful for
- // ValueMetric and GaugeMetric.
- oneof metric_value {
- int64 int64_value = 4;
- double double_value = 5;
- }
-
- // Defines which stats config used to fire the request.
+ // Defines which stats config used to fire the request, incident report will
+ // only be uploaded if this value is given.
message StatsdConfigKey {
- optional int32 uid = 1;
- optional string name = 2;
+ optional int32 uid = 1; // The uid pushes the config to statsd.
+ optional int64 id = 2; // The unique id of the statsd config.
}
- optional StatsdConfigKey config_key = 6;
+ optional StatsdConfigKey config_key = 3;
}
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index bd9c8eeb1d74..fbc21e558806 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -161,8 +161,14 @@ IncidentReportArgs::addSection(int section)
}
void
-IncidentReportArgs::addHeader(const vector<uint8_t>& header)
+IncidentReportArgs::addHeader(const IncidentHeaderProto& headerProto)
{
+ vector<uint8_t> header;
+ auto serialized = headerProto.SerializeAsString();
+ if (serialized.empty()) return;
+ for (auto it = serialized.begin(); it != serialized.end(); it++) {
+ header.push_back((uint8_t)*it);
+ }
mHeaders.push_back(header);
}
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 833376cfa057..603926f4fe4d 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -19,10 +19,12 @@ package com.android.internal.location.gnssmetrics;
import android.os.SystemClock;
import android.util.Base64;
+import android.util.Log;
import android.util.TimeUtils;
import java.util.Arrays;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.location.nano.GnssLogsProto.GnssLog;
/**
@@ -31,14 +33,29 @@ import com.android.internal.location.nano.GnssLogsProto.GnssLog;
*/
public class GnssMetrics {
+ private static final String TAG = GnssMetrics.class.getSimpleName();
+
+ /* Constant which indicates GPS signal quality is poor */
+ public static final int GPS_SIGNAL_QUALITY_POOR = 0;
+
+ /* Constant which indicates GPS signal quality is good */
+ public static final int GPS_SIGNAL_QUALITY_GOOD = 1;
+
+ /* Number of GPS signal quality levels */
+ public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1;
+
/** Default time between location fixes (in millisecs) */
private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000;
/* The time since boot when logging started */
private String logStartInElapsedRealTime;
+ /* GNSS power metrics */
+ private GnssPowerMetrics mGnssPowerMetrics;
+
/** Constructor */
- public GnssMetrics() {
+ public GnssMetrics(IBatteryStats stats) {
+ mGnssPowerMetrics = new GnssPowerMetrics(stats);
locationFailureStatistics = new Statistics();
timeToFirstFixSecStatistics = new Statistics();
positionAccuracyMeterStatistics = new Statistics();
@@ -103,11 +120,18 @@ public class GnssMetrics {
*
*/
public void logCn0(float[] cn0s, int numSv) {
- if (numSv < 4) {
+ if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) {
+ if (numSv == 0) {
+ mGnssPowerMetrics.reportSignalQuality(null, 0);
+ }
return;
}
float[] cn0Array = Arrays.copyOf(cn0s, numSv);
Arrays.sort(cn0Array);
+ mGnssPowerMetrics.reportSignalQuality(cn0Array, numSv);
+ if (numSv < 4) {
+ return;
+ }
if (cn0Array[numSv - 4] > 0.0) {
double top4AvgCn0 = 0.0;
for (int i = numSv - 4; i < numSv; i++) {
@@ -265,4 +289,62 @@ public class GnssMetrics {
topFourAverageCn0Statistics.reset();
return;
}
+
+ /* Class for handling GNSS power related metrics */
+ private class GnssPowerMetrics {
+
+ /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */
+ private static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0;
+
+ /* Minimum change in Top Four Average CN0 needed to trigger a report */
+ private static final double REPORTING_THRESHOLD_DB_HZ = 1.0;
+
+ /* BatteryStats API */
+ private final IBatteryStats mBatteryStats;
+
+ /* Last reported Top Four Average CN0 */
+ private double mLastAverageCn0;
+
+ public GnssPowerMetrics(IBatteryStats stats) {
+ mBatteryStats = stats;
+ // Used to initialize the variable to a very small value (unachievable in practice) so that
+ // the first CNO report will trigger an update to BatteryStats
+ mLastAverageCn0 = -100.0;
+ }
+
+ /**
+ * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If
+ * the number of SVs seen is less than 4, then signal quality is the average CN0.
+ * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ.
+ */
+ public void reportSignalQuality(float[] ascendingCN0Array, int numSv) {
+ double avgCn0 = 0.0;
+ if (numSv > 0) {
+ for (int i = Math.max(0, numSv - 4); i < numSv; i++) {
+ avgCn0 += (double) ascendingCN0Array[i];
+ }
+ avgCn0 /= Math.min(numSv, 4);
+ }
+ if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) {
+ return;
+ }
+ try {
+ mBatteryStats.noteGpsSignalQuality(getSignalLevel(avgCn0));
+ mLastAverageCn0 = avgCn0;
+ } catch (Exception e) {
+ Log.w(TAG, "Exception", e);
+ }
+ return;
+ }
+
+ /**
+ * Obtains signal level based on CN0
+ */
+ private int getSignalLevel(double cn0) {
+ if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) {
+ return GnssMetrics.GPS_SIGNAL_QUALITY_GOOD;
+ }
+ return GnssMetrics.GPS_SIGNAL_QUALITY_POOR;
+ }
+ }
} \ No newline at end of file
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 93fc3da54550..b07d04220046 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -238,22 +238,15 @@ public final class AudioFormat implements Parcelable {
public static final int ENCODING_DTS = 7;
/** Audio data format: DTS HD compressed */
public static final int ENCODING_DTS_HD = 8;
- /** Audio data format: MP3 compressed
- * @hide
- * */
+ /** Audio data format: MP3 compressed */
public static final int ENCODING_MP3 = 9;
- /** Audio data format: AAC LC compressed
- * @hide
- * */
+ /** Audio data format: AAC LC compressed */
public static final int ENCODING_AAC_LC = 10;
- /** Audio data format: AAC HE V1 compressed
- * @hide
- * */
+ /** Audio data format: AAC HE V1 compressed */
public static final int ENCODING_AAC_HE_V1 = 11;
- /** Audio data format: AAC HE V2 compressed
- * @hide
- * */
+ /** Audio data format: AAC HE V2 compressed */
public static final int ENCODING_AAC_HE_V2 = 12;
+
/** Audio data format: compressed audio wrapped in PCM for HDMI
* or S/PDIF passthrough.
* IEC61937 uses a stereo stream of 16-bit samples as the wrapper.
@@ -266,6 +259,12 @@ public final class AudioFormat implements Parcelable {
/** Audio data format: DOLBY TRUEHD compressed
**/
public static final int ENCODING_DOLBY_TRUEHD = 14;
+ /** Audio data format: AAC ELD compressed */
+ public static final int ENCODING_AAC_ELD = 15;
+ /** Audio data format: AAC xHE compressed */
+ public static final int ENCODING_AAC_XHE = 16;
+ /** Audio data format: AC-4 sync frame transport format */
+ public static final int ENCODING_AC4 = 17;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -298,6 +297,12 @@ public final class AudioFormat implements Parcelable {
return "ENCODING_IEC61937";
case ENCODING_DOLBY_TRUEHD:
return "ENCODING_DOLBY_TRUEHD";
+ case ENCODING_AAC_ELD:
+ return "ENCODING_AAC_ELD";
+ case ENCODING_AAC_XHE:
+ return "ENCODING_AAC_XHE";
+ case ENCODING_AC4:
+ return "ENCODING_AC4";
default :
return "invalid encoding " + enc;
}
@@ -514,6 +519,9 @@ public final class AudioFormat implements Parcelable {
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_IEC61937:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return true;
default:
return false;
@@ -532,6 +540,13 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
+ case ENCODING_MP3:
+ case ENCODING_AAC_LC:
+ case ENCODING_AAC_HE_V1:
+ case ENCODING_AAC_HE_V2:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return true;
default:
return false;
@@ -556,6 +571,9 @@ public final class AudioFormat implements Parcelable {
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_IEC61937: // wrapped in PCM but compressed
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return false;
case ENCODING_INVALID:
default:
@@ -581,6 +599,9 @@ public final class AudioFormat implements Parcelable {
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return false;
case ENCODING_INVALID:
default:
@@ -794,14 +815,7 @@ public final class AudioFormat implements Parcelable {
/**
* Sets the data encoding format.
- * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT},
- * {@link AudioFormat#ENCODING_PCM_8BIT},
- * {@link AudioFormat#ENCODING_PCM_16BIT},
- * {@link AudioFormat#ENCODING_PCM_FLOAT},
- * {@link AudioFormat#ENCODING_AC3},
- * {@link AudioFormat#ENCODING_E_AC3}.
- * {@link AudioFormat#ENCODING_DTS},
- * {@link AudioFormat#ENCODING_DTS_HD}.
+ * @param encoding the specified encoding or default.
* @return the same Builder instance.
* @throws java.lang.IllegalArgumentException
*/
@@ -818,6 +832,13 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
+ case ENCODING_MP3:
+ case ENCODING_AAC_LC:
+ case ENCODING_AAC_HE_V1:
+ case ENCODING_AAC_HE_V2:
+ case ENCODING_AAC_ELD:
+ case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1016,7 +1037,7 @@ public final class AudioFormat implements Parcelable {
}
/** @hide */
- @IntDef({
+ @IntDef(flag = false, prefix = "ENCODING", value = {
ENCODING_DEFAULT,
ENCODING_PCM_8BIT,
ENCODING_PCM_16BIT,
@@ -1025,8 +1046,14 @@ public final class AudioFormat implements Parcelable {
ENCODING_E_AC3,
ENCODING_DTS,
ENCODING_DTS_HD,
- ENCODING_IEC61937
- })
+ ENCODING_IEC61937,
+ ENCODING_AAC_HE_V1,
+ ENCODING_AAC_HE_V2,
+ ENCODING_AAC_LC,
+ ENCODING_AAC_ELD,
+ ENCODING_AAC_XHE,
+ ENCODING_AC4 }
+ )
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 913b5e841112..2ac4063d1b08 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1329,6 +1329,19 @@ public class AudioManager {
}
//====================================================================
+ // Offload query
+ /**
+ * Returns whether offloaded playback of an audio format is supported on the device.
+ * Offloaded playback is where the decoding of an audio stream is not competing with other
+ * software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
+ * @param format the audio format (codec, sample rate, channels) being checked.
+ * @return true if the given audio format can be offloaded.
+ */
+ public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
+ return AudioSystem.isOffloadSupported(format);
+ }
+
+ //====================================================================
// Bluetooth SCO control
/**
* Sticky broadcast intent action indicating that the Bluetooth SCO audio
@@ -3746,6 +3759,33 @@ public class AudioManager {
}
/**
+ * Indicate A2DP source or sink connection state change and eventually suppress
+ * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+ * @param device Bluetooth device connected/disconnected
+ * @param state new connection state (BluetoothProfile.STATE_xxx)
+ * @param profile profile for the A2DP device
+ * (either {@link android.bluetooth.BluetoothProfile.A2DP} or
+ * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
+ * @param suppressNoisyIntent if true the
+ * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+ * @return a delay in ms that the caller should wait before broadcasting
+ * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
+ * {@hide}
+ */
+ public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent) {
+ final IAudioService service = getService();
+ int delay = 0;
+ try {
+ delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
+ state, profile, suppressNoisyIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return delay;
+ }
+
+ /**
* Indicate A2DP device configuration has changed.
* @param device Bluetooth device whose configuration has changed.
* {@hide}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e56944dff782..b4316ba9b1df 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.audiopolicy.AudioMix;
@@ -818,6 +819,14 @@ public class AudioSystem
public static native float getStreamVolumeDB(int stream, int index, int device);
+ static boolean isOffloadSupported(@NonNull AudioFormat format) {
+ return native_is_offload_supported(format.getEncoding(), format.getSampleRate(),
+ format.getChannelMask(), format.getChannelIndexMask());
+ }
+
+ private static native boolean native_is_offload_supported(int encoding, int sampleRate,
+ int channelMask, int channelIndexMask);
+
// Items shared with audio service
/**
@@ -914,7 +923,8 @@ public class AudioSystem
(1 << STREAM_MUSIC) |
(1 << STREAM_RING) |
(1 << STREAM_NOTIFICATION) |
- (1 << STREAM_SYSTEM);
+ (1 << STREAM_SYSTEM) |
+ (1 << STREAM_VOICE_CALL);
/**
* Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e535fdf53d01..5928d03dc4a1 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -24,7 +24,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
import java.util.Collection;
+import java.util.concurrent.Executor;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -185,6 +187,22 @@ public class AudioTrack extends PlayerBase
* Event id denotes when previously set update period has elapsed during playback.
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
+ /**
+ * Callback for more data
+ * TODO only for offload
+ */
+ private static final int NATIVE_EVENT_MORE_DATA = 0;
+ /**
+ * IAudioTrack tear down for offloaded tracks
+ * TODO: when received, java AudioTrack must be released
+ */
+ private static final int NATIVE_EVENT_NEW_IAUDIOTRACK = 6;
+ /**
+ * Event id denotes when all the buffers queued in AF and HW are played
+ * back (after stop is called) for an offloaded track.
+ * TODO: not just for offload
+ */
+ private static final int NATIVE_EVENT_STREAM_END = 7;
private final static String TAG = "android.media.AudioTrack";
@@ -540,6 +558,12 @@ public class AudioTrack extends PlayerBase
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
throws IllegalArgumentException {
+ this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/);
+ }
+
+ private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
+ int mode, int sessionId, boolean offload)
+ throws IllegalArgumentException {
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
// mState already == STATE_UNINITIALIZED
@@ -601,7 +625,8 @@ public class AudioTrack extends PlayerBase
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
- mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
+ mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
+ offload);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -681,7 +706,8 @@ public class AudioTrack extends PlayerBase
0 /*mNativeBufferSizeInBytes - NA*/,
0 /*mDataLoadMode - NA*/,
session,
- nativeTrackInJavaObj);
+ nativeTrackInJavaObj,
+ false /*offload*/);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -729,6 +755,7 @@ public class AudioTrack extends PlayerBase
* <code>MODE_STREAM</code> will be used.
* <br>If the session ID is not specified with {@link #setSessionId(int)}, a new one will
* be generated.
+ * <br>Offload is false by default.
*/
public static class Builder {
private AudioAttributes mAttributes;
@@ -737,6 +764,7 @@ public class AudioTrack extends PlayerBase
private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
private int mMode = MODE_STREAM;
private int mPerformanceMode = PERFORMANCE_MODE_NONE;
+ private boolean mOffload = false;
/**
* Constructs a new Builder with the default values as described above.
@@ -867,6 +895,21 @@ public class AudioTrack extends PlayerBase
}
/**
+ * Sets whether this track will play through the offloaded audio path.
+ * When set to true, at build time, the audio format will be checked against
+ * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
+ * used by this track is supported on the device's offload path (if any).
+ * <br>Offload is only supported for media audio streams, and therefore requires that
+ * the usage be {@link AudioAttributes#USAGE_MEDIA}.
+ * @param offload true to require the offload path for playback.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setOffloadedPlayback(boolean offload) {
+ mOffload = offload;
+ return this;
+ }
+
+ /**
* Builds an {@link AudioTrack} instance initialized with all the parameters set
* on this <code>Builder</code>.
* @return a new successfully initialized {@link AudioTrack} instance.
@@ -909,6 +952,19 @@ public class AudioTrack extends PlayerBase
.setEncoding(AudioFormat.ENCODING_DEFAULT)
.build();
}
+
+ //TODO tie offload to PERFORMANCE_MODE_POWER_SAVING?
+ if (mOffload) {
+ if (mAttributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
+ throw new UnsupportedOperationException(
+ "Cannot create AudioTrack, offload requires USAGE_MEDIA");
+ }
+ if (!AudioSystem.isOffloadSupported(mFormat)) {
+ throw new UnsupportedOperationException(
+ "Cannot create AudioTrack, offload format not supported");
+ }
+ }
+
try {
// If the buffer size is not specified in streaming mode,
// use a single frame for the buffer size and let the
@@ -918,7 +974,7 @@ public class AudioTrack extends PlayerBase
* mFormat.getBytesPerSample(mFormat.getEncoding());
}
final AudioTrack track = new AudioTrack(
- mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId);
+ mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload);
if (track.getState() == STATE_UNINITIALIZED) {
// release is not necessary
throw new UnsupportedOperationException("Cannot create AudioTrack");
@@ -2882,6 +2938,69 @@ public class AudioTrack extends PlayerBase
void onPeriodicNotification(AudioTrack track);
}
+ /**
+ * Abstract class to receive event notification about the stream playback.
+ * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
+ * the callback on the given {@link AudioTrack} instance.
+ */
+ public abstract static class StreamEventCallback {
+ /** @hide */ // add hidden empty constructor so it doesn't show in SDK
+ public StreamEventCallback() { }
+ /**
+ * Called when an offloaded track is no longer valid and has been discarded by the system.
+ * An example of this happening is when an offloaded track has been paused too long, and
+ * gets invalidated by the system to prevent any other offload.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
+ public void onTearDown(AudioTrack track) { }
+ /**
+ * Called when all the buffers of an offloaded track that were queued in the audio system
+ * (e.g. the combination of the Android audio framework and the device's audio hardware)
+ * have been played after {@link AudioTrack#stop()} has been called.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
+ public void onStreamPresentationEnd(AudioTrack track) { }
+ /**
+ * Called when more audio data can be written without blocking on an offloaded track.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
+ public void onStreamDataRequest(AudioTrack track) { }
+ }
+
+ private Executor mStreamEventExec;
+ private StreamEventCallback mStreamEventCb;
+ private final Object mStreamEventCbLock = new Object();
+
+ /**
+ * Sets the callback for the notification of stream events.
+ * @param executor {@link Executor} to handle the callbacks
+ * @param eventCallback the callback to receive the stream event notifications
+ */
+ public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull StreamEventCallback eventCallback) {
+ if (eventCallback == null) {
+ throw new IllegalArgumentException("Illegal null StreamEventCallback");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Illegal null Executor for the StreamEventCallback");
+ }
+ synchronized (mStreamEventCbLock) {
+ mStreamEventExec = executor;
+ mStreamEventCb = eventCallback;
+ }
+ }
+
+ /**
+ * Unregisters the callback for notification of stream events, previously set
+ * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
+ */
+ public void removeStreamEventCallback() {
+ synchronized (mStreamEventCbLock) {
+ mStreamEventExec = null;
+ mStreamEventCb = null;
+ }
+ }
+
//---------------------------------------------------------
// Inner classes
//--------------------
@@ -2965,7 +3084,7 @@ public class AudioTrack extends PlayerBase
private static void postEventFromNative(Object audiotrack_ref,
int what, int arg1, int arg2, Object obj) {
//logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
- AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
+ final AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
if (track == null) {
return;
}
@@ -2974,6 +3093,32 @@ public class AudioTrack extends PlayerBase
track.broadcastRoutingChange();
return;
}
+
+ if (what == NATIVE_EVENT_MORE_DATA || what == NATIVE_EVENT_NEW_IAUDIOTRACK
+ || what == NATIVE_EVENT_STREAM_END) {
+ final Executor exec;
+ final StreamEventCallback cb;
+ synchronized (track.mStreamEventCbLock) {
+ exec = track.mStreamEventExec;
+ cb = track.mStreamEventCb;
+ }
+ if ((exec == null) || (cb == null)) {
+ return;
+ }
+ switch (what) {
+ case NATIVE_EVENT_MORE_DATA:
+ exec.execute(() -> cb.onStreamDataRequest(track));
+ return;
+ case NATIVE_EVENT_NEW_IAUDIOTRACK:
+ // TODO also release track as it's not longer usable
+ exec.execute(() -> cb.onTearDown(track));
+ return;
+ case NATIVE_EVENT_STREAM_END:
+ exec.execute(() -> cb.onStreamPresentationEnd(track));
+ return;
+ }
+ }
+
NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
if (delegate != null) {
Handler handler = delegate.getHandler();
@@ -2995,7 +3140,8 @@ public class AudioTrack extends PlayerBase
private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
Object /*AudioAttributes*/ attributes,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
- int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack);
+ int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
+ boolean offload);
private native final void native_finalize();
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bb6ae9863d31..6c6522328e8d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -203,5 +203,8 @@ interface IAudioService {
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
+ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
+ int state, int profile, boolean suppressNoisyIntent);
+
// WARNING: read warning at top of file, it is recommended to add new methods at the end
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 3c49b80b4b5e..78477f757e2a 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1380,7 +1380,8 @@ public class MediaRecorder implements AudioRouting
if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
enableNativeRoutingCallbacksLocked(true);
mRoutingChangeListeners.put(
- listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ listener, new NativeRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : mEventHandler));
}
}
}
@@ -1401,36 +1402,6 @@ public class MediaRecorder implements AudioRouting
}
}
- /**
- * Helper class to handle the forwarding of native events to the appropriate listener
- * (potentially) handled in a different thread
- */
- private class NativeRoutingEventHandlerDelegate {
- private MediaRecorder mMediaRecorder;
- private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
- private Handler mHandler;
-
- NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder,
- final AudioRouting.OnRoutingChangedListener listener, Handler handler) {
- mMediaRecorder = mediaRecorder;
- mOnRoutingChangedListener = listener;
- mHandler = handler != null ? handler : mEventHandler;
- }
-
- void notifyClient() {
- if (mHandler != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mOnRoutingChangedListener != null) {
- mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder);
- }
- }
- });
- }
- }
- }
-
private native final boolean native_setInputDevice(int deviceId);
private native final int native_getRoutedDeviceId();
private native final void native_enableDeviceCallback(boolean enabled);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index fadb76d80fe5..4c96d890a751 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -197,7 +197,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
assertFalse(metadata.isEmpty());
CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
- CameraCaptureSession.SESSION_ID_NONE);
+ CameraCaptureSession.SESSION_ID_NONE, mCameraId, /*physicalCameraIdSet*/null);
assertFalse(request.isEmpty());
assertFalse(metadata.isEmpty());
if (needStream) {
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index 63d3623c468a..45e557c00333 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -56,6 +56,12 @@
<intent-filter>
<action android:name="android.service.autofill.AutofillFieldClassificationService" />
</intent-filter>
+ <meta-data
+ android:name="android.autofill.field_classification.default_algorithm"
+ android:resource="@string/autofill_field_classification_default_algorithm" />
+ <meta-data
+ android:name="android.autofill.field_classification.available_algorithms"
+ android:resource="@array/autofill_field_classification_available_algorithms" />
</service>
<library android:name="android.ext.services"/>
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index a2e65bc8f9fd..72647ab8ae3f 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -19,4 +19,9 @@
<string name="notification_assistant">Notification Assistant</string>
<string name="prompt_block_reason">Too many dismissals:views</string>
+
+ <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
+ <string-array name="autofill_field_classification_available_algorithms">
+ <item>EDIT_DISTANCE</item>
+ </string-array>
</resources>
diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
index ea516a1db8b8..4709d35018c2 100644
--- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
@@ -24,28 +24,17 @@ import android.view.autofill.AutofillValue;
import com.android.internal.util.ArrayUtils;
-import java.util.Arrays;
import java.util.List;
public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
private static final String TAG = "AutofillFieldClassificationServiceImpl";
- private static final boolean DEBUG = false;
- private static final List<String> sAvailableAlgorithms = Arrays.asList(EditDistanceScorer.NAME);
-
- @Override
- public List<String> onGetAvailableAlgorithms() {
- return sAvailableAlgorithms;
- }
-
- @Override
- public String onGetDefaultAlgorithm() {
- return EditDistanceScorer.NAME;
- }
+ // TODO(b/70291841): set to false before launching
+ private static final boolean DEBUG = true;
@Nullable
@Override
- public Scores onGetScores(@Nullable String algorithmName,
+ public float[][] onGetScores(@Nullable String algorithmName,
@Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
@@ -66,14 +55,13 @@ public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassif
Log.d(TAG, "getScores() will return a " + actualValuesSize + "x"
+ userDataValuesSize + " matrix for " + actualAlgorithmName);
}
- final Scores scores = new Scores(actualAlgorithmName, actualValuesSize, userDataValuesSize);
- final float[][] scoresMatrix = scores.getScores();
+ final float[][] scores = new float[actualValuesSize][userDataValuesSize];
final EditDistanceScorer algorithm = EditDistanceScorer.getInstance();
for (int i = 0; i < actualValuesSize; i++) {
for (int j = 0; j < userDataValuesSize; j++) {
final float score = algorithm.getScore(actualValues.get(i), userDataValues.get(j));
- scoresMatrix[i][j] = score;
+ scores[i][j] = score;
}
}
return scores;
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b13de2ec5ce1..f6541bb92fb4 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1015,4 +1015,10 @@
<!-- About phone, status item value if the actual value is not available. -->
<string name="status_unavailable">Unavailable</string>
+ <!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
+ <plurals name="wifi_tether_connected_summary">
+ <item quantity="one">%1$d device connected</item>
+ <item quantity="other">%1$d devices connected</item>
+ </plurals>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 764c5922cc64..9b69304be308 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -128,14 +128,18 @@ public class A2dpProfile implements LocalBluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
- List<BluetoothDevice> sinks = getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- if (sink.equals(device)) {
- Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
- continue;
+ int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
+ if (max_connected_devices == 1) {
+ // Original behavior: disconnect currently connected device
+ List<BluetoothDevice> sinks = getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ if (sink.equals(device)) {
+ Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+ continue;
+ }
+ mService.disconnect(sink);
}
- mService.disconnect(sink);
}
}
return mService.connect(device);
@@ -157,6 +161,16 @@ public class A2dpProfile implements LocalBluetoothProfile {
return mService.getConnectionState(device);
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.setActiveDevice(device);
+ }
+
+ public BluetoothDevice getActiveDevice() {
+ if (mService == null) return null;
+ return mService.getActiveDevice();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
@@ -180,8 +194,8 @@ public class A2dpProfile implements LocalBluetoothProfile {
boolean isA2dpPlaying() {
if (mService == null) return false;
List<BluetoothDevice> sinks = mService.getConnectedDevices();
- if (!sinks.isEmpty()) {
- if (mService.isA2dpPlaying(sinks.get(0))) {
+ for (BluetoothDevice device : sinks) {
+ if (mService.isA2dpPlaying(device)) {
return true;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4c41b490d403..ac3599cae05b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -28,4 +28,5 @@ public interface BluetoothCallback {
void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
+ void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index f57d02bb92fa..3cda9c9e3789 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,9 +16,12 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +34,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -106,6 +110,12 @@ public class BluetoothEventManager {
// Dock event broadcasts
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
+ // Active device broadcasts
+ addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
+ new ActiveDeviceChangedHandler());
+ addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
+ new ActiveDeviceChangedHandler());
+
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
}
@@ -409,4 +419,35 @@ public class BluetoothEventManager {
return deviceAdded;
}
+
+ private class ActiveDeviceChangedHandler implements Handler {
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
+ return;
+ }
+ CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+ int bluetoothProfile = 0;
+ if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
+ bluetoothProfile = BluetoothProfile.A2DP;
+ } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
+ bluetoothProfile = BluetoothProfile.HEADSET;
+ } else {
+ Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+ return;
+ }
+ dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
+ }
+ }
+
+ private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9caff100cb9d..fb0f75b522b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -105,6 +105,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
+ // Active device state
+ private boolean mIsActiveDeviceA2dp = false;
+ private boolean mIsActiveDeviceHeadset = false;
+
/**
* Describes the current device and profile for logging.
*
@@ -156,6 +160,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mRemovedProfiles.add(profile);
mLocalNapRoleConnected = false;
}
+ fetchActiveDevices();
}
CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
fetchName();
fetchBtClass();
updateProfiles();
+ fetchActiveDevices();
migratePhonebookPermissionChoice();
migrateMessagePermissionChoice();
fetchMessageRejectionCount();
@@ -454,6 +460,33 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
return mDevice.getBondState();
}
+ /**
+ * Set the device status as active or non-active per Bluetooth profile.
+ *
+ * @param isActive true if the device is active
+ * @param bluetoothProfile the Bluetooth profile
+ */
+ public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+ boolean changed = false;
+ switch (bluetoothProfile) {
+ case BluetoothProfile.A2DP:
+ changed = (mIsActiveDeviceA2dp != isActive);
+ mIsActiveDeviceA2dp = isActive;
+ break;
+ case BluetoothProfile.HEADSET:
+ changed = (mIsActiveDeviceHeadset != isActive);
+ mIsActiveDeviceHeadset = isActive;
+ break;
+ default:
+ Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+ " isActive " + isActive);
+ break;
+ }
+ if (changed) {
+ dispatchAttributesChanged();
+ }
+ }
+
void setRssi(short rssi) {
if (mRssi != rssi) {
mRssi = rssi;
@@ -529,6 +562,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
return true;
}
+ private void fetchActiveDevices() {
+ A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null) {
+ mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
+ }
+ HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+ if (headsetProfile != null) {
+ mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
+ }
+ }
+
/**
* Refreshes the UI for the BT class, including fetching the latest value
* for the class.
@@ -896,37 +940,60 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
+ // TODO: A temporary workaround solution using string description the device is active.
+ // Issue tracked by b/72317067 .
+ // An alternative solution would be visual indication.
+ // Intentionally not adding the strings to strings.xml for now:
+ // 1) If this is just a short-term solution, no need to waste translation effort
+ // 2) The number of strings with all possible combinations becomes enormously large.
+ // If string description becomes part of the final solution, we MUST NOT
+ // concatenate the strings here: this does not translate well.
+ String activeString = null;
+ if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
+ activeString = ", active";
+ } else {
+ if (mIsActiveDeviceA2dp) {
+ activeString = ", active(media)";
+ }
+ if (mIsActiveDeviceHeadset) {
+ activeString = ", active(phone)";
+ }
+ }
+ if (activeString == null) activeString = "";
+
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(
R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
+ return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
+ activeString;
}
} else if (a2dpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected_no_a2dp);
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
}
} else if (hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset);
+ return mContext.getString(R.string.bluetooth_connected_no_headset)
+ + activeString;
}
} else {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected);
+ return mContext.getString(R.string.bluetooth_connected) + activeString;
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index d45fe1a38a22..ee1219126fe3 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -153,6 +153,16 @@ public class HeadsetProfile implements LocalBluetoothProfile {
return BluetoothProfile.STATE_DISCONNECTED;
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.setActiveDevice(device);
+ }
+
+ public BluetoothDevice getActiveDevice() {
+ if (mService == null) return null;
+ return mService.getActiveDevice();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 22674cb6de9b..cda4e454fe74 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -239,4 +239,8 @@ public class LocalBluetoothAdapter {
public BluetoothDevice getRemoteDevice(String address) {
return mAdapter.getRemoteDevice(address);
}
+
+ public int getMaxConnectedAudioDevices() {
+ return mAdapter.getMaxConnectedAudioDevices();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
deleted file mode 100644
index 72273046ef29..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.core.instrumentation;
-
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-
-/**
- * {@link LogWriter} that writes data to eventlog.
- */
-public class EventLogWriter implements LogWriter {
-
- private final MetricsLogger mMetricsLogger = new MetricsLogger();
-
- public void visible(Context context, int source, int category) {
- final LogMaker logMaker = new LogMaker(category)
- .setType(MetricsProto.MetricsEvent.TYPE_OPEN)
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source);
- MetricsLogger.action(logMaker);
- }
-
- public void hidden(Context context, int category) {
- MetricsLogger.hidden(context, category);
- }
-
- public void action(int category, int value, Pair<Integer, Object>... taggedData) {
- if (taggedData == null || taggedData.length == 0) {
- mMetricsLogger.action(category, value);
- } else {
- final LogMaker logMaker = new LogMaker(category)
- .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
- .setSubtype(value);
- for (Pair<Integer, Object> pair : taggedData) {
- logMaker.addTaggedData(pair.first, pair.second);
- }
- mMetricsLogger.write(logMaker);
- }
- }
-
- public void action(int category, boolean value, Pair<Integer, Object>... taggedData) {
- action(category, value ? 1 : 0, taggedData);
- }
-
- public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
- action(context, category, "", taggedData);
- }
-
- public void actionWithSource(Context context, int source, int category) {
- final LogMaker logMaker = new LogMaker(category)
- .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
- if (source != MetricsProto.MetricsEvent.VIEW_UNKNOWN) {
- logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source);
- }
- MetricsLogger.action(logMaker);
- }
-
- /** @deprecated use {@link #action(int, int, Pair[])} */
- @Deprecated
- public void action(Context context, int category, int value) {
- MetricsLogger.action(context, category, value);
- }
-
- /** @deprecated use {@link #action(int, boolean, Pair[])} */
- @Deprecated
- public void action(Context context, int category, boolean value) {
- MetricsLogger.action(context, category, value);
- }
-
- public void action(Context context, int category, String pkg,
- Pair<Integer, Object>... taggedData) {
- if (taggedData == null || taggedData.length == 0) {
- MetricsLogger.action(context, category, pkg);
- } else {
- final LogMaker logMaker = new LogMaker(category)
- .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
- .setPackageName(pkg);
- for (Pair<Integer, Object> pair : taggedData) {
- logMaker.addTaggedData(pair.first, pair.second);
- }
- MetricsLogger.action(logMaker);
- }
- }
-
- public void count(Context context, String name, int value) {
- MetricsLogger.count(context, name, value);
- }
-
- public void histogram(Context context, String name, int bucket) {
- MetricsLogger.histogram(context, name, bucket);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
deleted file mode 100644
index 4b9f5727208d..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import android.content.Context;
-import android.util.Pair;
-
-/**
- * Generic log writer interface.
- */
-public interface LogWriter {
-
- /**
- * Logs a visibility event when view becomes visible.
- */
- void visible(Context context, int source, int category);
-
- /**
- * Logs a visibility event when view becomes hidden.
- */
- void hidden(Context context, int category);
-
- /**
- * Logs a user action.
- */
- void action(int category, int value, Pair<Integer, Object>... taggedData);
-
- /**
- * Logs a user action.
- */
- void action(int category, boolean value, Pair<Integer, Object>... taggedData);
-
- /**
- * Logs an user action.
- */
- void action(Context context, int category, Pair<Integer, Object>... taggedData);
-
- /**
- * Logs an user action.
- */
- void actionWithSource(Context context, int source, int category);
-
- /**
- * Logs an user action.
- * @deprecated use {@link #action(int, int, Pair[])}
- */
- @Deprecated
- void action(Context context, int category, int value);
-
- /**
- * Logs an user action.
- * @deprecated use {@link #action(int, boolean, Pair[])}
- */
- @Deprecated
- void action(Context context, int category, boolean value);
-
- /**
- * Logs an user action.
- */
- void action(Context context, int category, String pkg, Pair<Integer, Object>... taggedData);
-
- /**
- * Logs a count.
- */
- void count(Context context, String name, int value);
-
- /**
- * Logs a histogram event.
- */
- void histogram(Context context, String name, int bucket);
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
deleted file mode 100644
index 1e5b378e931c..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * FeatureProvider for metrics.
- */
-public class MetricsFeatureProvider {
- private List<LogWriter> mLoggerWriters;
-
- public MetricsFeatureProvider() {
- mLoggerWriters = new ArrayList<>();
- installLogWriters();
- }
-
- protected void installLogWriters() {
- mLoggerWriters.add(new EventLogWriter());
- }
-
- public void visible(Context context, int source, int category) {
- for (LogWriter writer : mLoggerWriters) {
- writer.visible(context, source, category);
- }
- }
-
- public void hidden(Context context, int category) {
- for (LogWriter writer : mLoggerWriters) {
- writer.hidden(context, category);
- }
- }
-
- public void actionWithSource(Context context, int source, int category) {
- for (LogWriter writer : mLoggerWriters) {
- writer.actionWithSource(context, source, category);
- }
- }
-
- /**
- * Logs a user action. Includes the elapsed time since the containing
- * fragment has been visible.
- */
- public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) {
- for (LogWriter writer : mLoggerWriters) {
- writer.action(category, value,
- sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
- }
- }
-
- /**
- * Logs a user action. Includes the elapsed time since the containing
- * fragment has been visible.
- */
- public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) {
- for (LogWriter writer : mLoggerWriters) {
- writer.action(category, value,
- sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
- }
- }
-
- public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
- for (LogWriter writer : mLoggerWriters) {
- writer.action(context, category, taggedData);
- }
- }
-
- /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */
- @Deprecated
- public void action(Context context, int category, int value) {
- for (LogWriter writer : mLoggerWriters) {
- writer.action(context, category, value);
- }
- }
-
- /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */
- @Deprecated
- public void action(Context context, int category, boolean value) {
- for (LogWriter writer : mLoggerWriters) {
- writer.action(context, category, value);
- }
- }
-
- public void action(Context context, int category, String pkg,
- Pair<Integer, Object>... taggedData) {
- for (LogWriter writer : mLoggerWriters) {
- writer.action(context, category, pkg, taggedData);
- }
- }
-
- public void count(Context context, String name, int value) {
- for (LogWriter writer : mLoggerWriters) {
- writer.count(context, name, value);
- }
- }
-
- public void histogram(Context context, String name, int bucket) {
- for (LogWriter writer : mLoggerWriters) {
- writer.histogram(context, name, bucket);
- }
- }
-
- public int getMetricsCategory(Object object) {
- if (object == null || !(object instanceof Instrumentable)) {
- return MetricsEvent.VIEW_UNKNOWN;
- }
- return ((Instrumentable) object).getMetricsCategory();
- }
-
- public void logDashboardStartIntent(Context context, Intent intent,
- int sourceMetricsCategory) {
- if (intent == null) {
- return;
- }
- final ComponentName cn = intent.getComponent();
- if (cn == null) {
- final String action = intent.getAction();
- if (TextUtils.isEmpty(action)) {
- // Not loggable
- return;
- }
- action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action,
- Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
- return;
- } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) {
- // Going to a Setting internal page, skip click logging in favor of page's own
- // visibility logging.
- return;
- }
- action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(),
- Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
- }
-
- private Pair<Integer, Object> sinceVisibleTaggedData(long timestamp) {
- return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
deleted file mode 100644
index facce4e0bcbb..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.settingslib.core.instrumentation;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentSkipListSet;
-
-public class SharedPreferencesLogger implements SharedPreferences {
-
- private static final String LOG_TAG = "SharedPreferencesLogger";
-
- private final String mTag;
- private final Context mContext;
- private final MetricsFeatureProvider mMetricsFeature;
- private final Set<String> mPreferenceKeySet;
-
- public SharedPreferencesLogger(Context context, String tag,
- MetricsFeatureProvider metricsFeature) {
- mContext = context;
- mTag = tag;
- mMetricsFeature = metricsFeature;
- mPreferenceKeySet = new ConcurrentSkipListSet<>();
- }
-
- @Override
- public Map<String, ?> getAll() {
- return null;
- }
-
- @Override
- public String getString(String key, @Nullable String defValue) {
- return defValue;
- }
-
- @Override
- public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
- return defValues;
- }
-
- @Override
- public int getInt(String key, int defValue) {
- return defValue;
- }
-
- @Override
- public long getLong(String key, long defValue) {
- return defValue;
- }
-
- @Override
- public float getFloat(String key, float defValue) {
- return defValue;
- }
-
- @Override
- public boolean getBoolean(String key, boolean defValue) {
- return defValue;
- }
-
- @Override
- public boolean contains(String key) {
- return false;
- }
-
- @Override
- public Editor edit() {
- return new EditorLogger();
- }
-
- @Override
- public void registerOnSharedPreferenceChangeListener(
- OnSharedPreferenceChangeListener listener) {
- }
-
- @Override
- public void unregisterOnSharedPreferenceChangeListener(
- OnSharedPreferenceChangeListener listener) {
- }
-
- private void logValue(String key, Object value) {
- logValue(key, value, false /* forceLog */);
- }
-
- private void logValue(String key, Object value, boolean forceLog) {
- final String prefKey = buildPrefKey(mTag, key);
- if (!forceLog && !mPreferenceKeySet.contains(prefKey)) {
- // Pref key doesn't exist in set, this is initial display so we skip metrics but
- // keeps track of this key.
- mPreferenceKeySet.add(prefKey);
- return;
- }
- // TODO: Remove count logging to save some resource.
- mMetricsFeature.count(mContext, buildCountName(prefKey, value), 1);
-
- final Pair<Integer, Object> valueData;
- if (value instanceof Long) {
- final Long longVal = (Long) value;
- final int intVal;
- if (longVal > Integer.MAX_VALUE) {
- intVal = Integer.MAX_VALUE;
- } else if (longVal < Integer.MIN_VALUE) {
- intVal = Integer.MIN_VALUE;
- } else {
- intVal = longVal.intValue();
- }
- valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
- intVal);
- } else if (value instanceof Integer) {
- valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
- value);
- } else if (value instanceof Boolean) {
- valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
- (Boolean) value ? 1 : 0);
- } else if (value instanceof Float) {
- valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE,
- value);
- } else if (value instanceof String) {
- Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value);
- valueData = null;
- } else {
- Log.w(LOG_TAG, "Tried to log unloggable object" + value);
- valueData = null;
- }
- if (valueData != null) {
- // Pref key exists in set, log it's change in metrics.
- mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE,
- Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey),
- valueData);
- }
- }
-
- @VisibleForTesting
- void logPackageName(String key, String value) {
- final String prefKey = mTag + "/" + key;
- mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value,
- Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey));
- }
-
- private void safeLogValue(String key, String value) {
- new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value);
- }
-
- public static String buildCountName(String prefKey, Object value) {
- return prefKey + "|" + value;
- }
-
- public static String buildPrefKey(String tag, String key) {
- return tag + "/" + key;
- }
-
- private class AsyncPackageCheck extends AsyncTask<String, Void, Void> {
- @Override
- protected Void doInBackground(String... params) {
- String key = params[0];
- String value = params[1];
- PackageManager pm = mContext.getPackageManager();
- try {
- // Check if this might be a component.
- ComponentName name = ComponentName.unflattenFromString(value);
- if (value != null) {
- value = name.getPackageName();
- }
- } catch (Exception e) {
- }
- try {
- pm.getPackageInfo(value, PackageManager.MATCH_ANY_USER);
- logPackageName(key, value);
- } catch (PackageManager.NameNotFoundException e) {
- // Clearly not a package, and it's unlikely this preference is in prefSet, so
- // lets force log it.
- logValue(key, value, true /* forceLog */);
- }
- return null;
- }
- }
-
- public class EditorLogger implements Editor {
- @Override
- public Editor putString(String key, @Nullable String value) {
- safeLogValue(key, value);
- return this;
- }
-
- @Override
- public Editor putStringSet(String key, @Nullable Set<String> values) {
- safeLogValue(key, TextUtils.join(",", values));
- return this;
- }
-
- @Override
- public Editor putInt(String key, int value) {
- logValue(key, value);
- return this;
- }
-
- @Override
- public Editor putLong(String key, long value) {
- logValue(key, value);
- return this;
- }
-
- @Override
- public Editor putFloat(String key, float value) {
- logValue(key, value);
- return this;
- }
-
- @Override
- public Editor putBoolean(String key, boolean value) {
- logValue(key, value);
- return this;
- }
-
- @Override
- public Editor remove(String key) {
- return this;
- }
-
- @Override
- public Editor clear() {
- return this;
- }
-
- @Override
- public boolean commit() {
- return true;
- }
-
- @Override
- public void apply() {
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
deleted file mode 100644
index 79838962ef1e..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.core.instrumentation;
-
-import android.app.Activity;
-import android.content.Context;
-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 {
-
- private static final String TAG = "VisibilityLoggerMixin";
-
- private final int mMetricsCategory;
-
- private MetricsFeatureProvider mMetricsFeature;
- private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
- private long mVisibleTimestamp;
-
- /**
- * The metrics category constant for logging source when a setting fragment is opened.
- */
- public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics";
-
- private VisibilityLoggerMixin() {
- mMetricsCategory = METRICS_CATEGORY_UNKNOWN;
- }
-
- public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) {
- mMetricsCategory = metricsCategory;
- mMetricsFeature = metricsFeature;
- }
-
- @Override
- public void onResume() {
- mVisibleTimestamp = SystemClock.elapsedRealtime();
- if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
- mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory);
- }
- }
-
- @Override
- public void onPause() {
- mVisibleTimestamp = 0;
- if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
- mMetricsFeature.hidden(null /* context */, mMetricsCategory);
- }
- }
-
- /**
- * Sets source metrics category for this logger. Source is the caller that opened this UI.
- */
- public void setSourceMetricsCategory(Activity activity) {
- if (mSourceMetricsCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN || activity == null) {
- return;
- }
- final Intent intent = activity.getIntent();
- if (intent == null) {
- return;
- }
- mSourceMetricsCategory = intent.getIntExtra(EXTRA_SOURCE_METRICS_CATEGORY,
- MetricsProto.MetricsEvent.VIEW_UNKNOWN);
- }
-
- /** Returns elapsed time since onResume() */
- public long elapsedTimeSinceVisible() {
- if (mVisibleTimestamp == 0) {
- return 0;
- }
- return SystemClock.elapsedRealtime() - mVisibleTimestamp;
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
deleted file mode 100644
index 8bea51d1696d..000000000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Pair;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.TestConfig;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class MetricsFeatureProviderTest {
- private static int CATEGORY = 10;
- private static boolean SUBTYPE_BOOLEAN = true;
- private static int SUBTYPE_INTEGER = 1;
- private static long ELAPSED_TIME = 1000;
-
- @Mock private LogWriter mockLogWriter;
- @Mock private VisibilityLoggerMixin mockVisibilityLogger;
-
- private Context mContext;
- private MetricsFeatureProvider mProvider;
-
- @Captor
- private ArgumentCaptor<Pair> mPairCaptor;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mProvider = new MetricsFeatureProvider();
- List<LogWriter> writers = new ArrayList<>();
- writers.add(mockLogWriter);
- ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers);
-
- when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME);
- }
-
- @Test
- public void logDashboardStartIntent_intentEmpty_shouldNotLog() {
- mProvider.logDashboardStartIntent(mContext, null /* intent */,
- MetricsEvent.SETTINGS_GESTURES);
-
- verifyNoMoreInteractions(mockLogWriter);
- }
-
- @Test
- public void logDashboardStartIntent_intentHasNoComponent_shouldLog() {
- final Intent intent = new Intent(Intent.ACTION_ASSIST);
-
- mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
-
- verify(mockLogWriter).action(
- eq(mContext),
- eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
- anyString(),
- eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
- }
-
- @Test
- public void logDashboardStartIntent_intentIsExternal_shouldLog() {
- final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
-
- mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
-
- verify(mockLogWriter).action(
- eq(mContext),
- eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
- anyString(),
- eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
- }
-
- @Test
- public void action_BooleanLogsElapsedTime() {
- mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN);
- verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture());
-
- Pair value = mPairCaptor.getValue();
- assertThat(value.first instanceof Integer).isTrue();
- assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
- assertThat(value.second).isEqualTo(ELAPSED_TIME);
- }
-
- @Test
- public void action_IntegerLogsElapsedTime() {
- mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER);
- verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture());
-
- Pair value = mPairCaptor.getValue();
- assertThat(value.first instanceof Integer).isTrue();
- assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
- assertThat(value.second).isEqualTo(ELAPSED_TIME);
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
deleted file mode 100644
index d558a645aeb7..000000000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Pair;
-
-import com.android.settingslib.TestConfig;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import com.google.common.truth.Platform;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class SharedPreferenceLoggerTest {
-
- private static final String TEST_TAG = "tag";
- private static final String TEST_KEY = "key";
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Context mContext;
-
- private ArgumentMatcher<Pair<Integer, Object>> mNamePairMatcher;
- @Mock
- private MetricsFeatureProvider mMetricsFeature;
- private SharedPreferencesLogger mSharedPrefLogger;
-
- @Before
- public void init() {
- MockitoAnnotations.initMocks(this);
- mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG, mMetricsFeature);
- mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class);
- }
-
- @Test
- public void putInt_shouldNotLogInitialPut() {
- final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
- editor.putInt(TEST_KEY, 1);
- editor.putInt(TEST_KEY, 1);
- editor.putInt(TEST_KEY, 1);
- editor.putInt(TEST_KEY, 2);
- editor.putInt(TEST_KEY, 2);
- editor.putInt(TEST_KEY, 2);
- editor.putInt(TEST_KEY, 2);
-
- verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(),
- argThat(mNamePairMatcher),
- argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
- }
-
- @Test
- public void putBoolean_shouldNotLogInitialPut() {
- final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
- editor.putBoolean(TEST_KEY, true);
- editor.putBoolean(TEST_KEY, true);
- editor.putBoolean(TEST_KEY, false);
- editor.putBoolean(TEST_KEY, false);
- editor.putBoolean(TEST_KEY, false);
-
-
- verify(mMetricsFeature).action(any(Context.class), anyInt(),
- argThat(mNamePairMatcher),
- argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true)));
- verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(),
- argThat(mNamePairMatcher),
- argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false)));
- }
-
- @Test
- public void putLong_shouldNotLogInitialPut() {
- final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
- editor.putLong(TEST_KEY, 1);
- editor.putLong(TEST_KEY, 1);
- editor.putLong(TEST_KEY, 1);
- editor.putLong(TEST_KEY, 1);
- editor.putLong(TEST_KEY, 2);
-
- verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
- argThat(mNamePairMatcher),
- argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
- }
-
- @Test
- public void putLong_biggerThanIntMax_shouldLogIntMax() {
- final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
- final long veryBigNumber = 500L + Integer.MAX_VALUE;
- editor.putLong(TEST_KEY, 1);
- editor.putLong(TEST_KEY, veryBigNumber);
-
- verify(mMetricsFeature).action(any(Context.class), anyInt(),
- argThat(mNamePairMatcher),
- argThat(pairMatches(
- FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE)));
- }
-
- @Test
- public void putLong_smallerThanIntMin_shouldLogIntMin() {
- final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
- final long veryNegativeNumber = -500L + Integer.MIN_VALUE;
- editor.putLong(TEST_KEY, 1);
- editor.putLong(TEST_KEY, veryNegativeNumber);
-
- verify(mMetricsFeature).action(any(Context.class), anyInt(),
- argThat(mNamePairMatcher),
- argThat(pairMatches(
- FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE)));
- }
-
- @Test
- public void putFloat_shouldNotLogInitialPut() {
- final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
- editor.putFloat(TEST_KEY, 1);
- editor.putFloat(TEST_KEY, 1);
- editor.putFloat(TEST_KEY, 1);
- editor.putFloat(TEST_KEY, 1);
- editor.putFloat(TEST_KEY, 2);
-
- verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
- argThat(mNamePairMatcher),
- argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class)));
- }
-
- @Test
- public void logPackage_shouldUseLogPackageApi() {
- mSharedPrefLogger.logPackageName("key", "com.android.settings");
- verify(mMetricsFeature).action(any(Context.class),
- eq(ACTION_SETTINGS_PREFERENCE_CHANGE),
- eq("com.android.settings"),
- any(Pair.class));
- }
-
- private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, Class clazz) {
- return pair -> pair.first == tag && Platform.isInstanceOfType(pair.second, clazz);
- }
-
- private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, boolean bool) {
- return pair -> pair.first == tag
- && Platform.isInstanceOfType(pair.second, Integer.class)
- && pair.second.equals((bool ? 1 : 0));
- }
-
- private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, int val) {
- return pair -> pair.first == tag
- && Platform.isInstanceOfType(pair.second, Integer.class)
- && pair.second.equals(val);
- }
-}
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
deleted file mode 100644
index a2648861d1d8..000000000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.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.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-import com.android.settingslib.TestConfig;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class VisibilityLoggerMixinTest {
-
- @Mock
- private MetricsFeatureProvider mMetricsFeature;
-
- private VisibilityLoggerMixin mMixin;
-
- @Before
- public void init() {
- MockitoAnnotations.initMocks(this);
- mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature);
- }
-
- @Test
- public void shouldLogVisibleOnResume() {
- mMixin.onResume();
-
- verify(mMetricsFeature, times(1))
- .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN),
- eq(TestInstrumentable.TEST_METRIC));
- }
-
- @Test
- public void shouldLogVisibleWithSource() {
- final Intent sourceIntent = new Intent()
- .putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY,
- MetricsProto.MetricsEvent.SETTINGS_GESTURES);
- final Activity activity = mock(Activity.class);
- when(activity.getIntent()).thenReturn(sourceIntent);
- mMixin.setSourceMetricsCategory(activity);
- mMixin.onResume();
-
- verify(mMetricsFeature, times(1))
- .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES),
- eq(TestInstrumentable.TEST_METRIC));
- }
-
- @Test
- public void shouldLogHideOnPause() {
- mMixin.onPause();
-
- verify(mMetricsFeature, times(1))
- .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC));
- }
-
- @Test
- public void shouldNotLogIfMetricsFeatureIsNull() {
- mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, null);
- mMixin.onResume();
- mMixin.onPause();
-
- verify(mMetricsFeature, never())
- .hidden(nullable(Context.class), anyInt());
- }
-
- @Test
- public void shouldNotLogIfMetricsCategoryIsUnknown() {
- mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature);
-
- mMixin.onResume();
- mMixin.onPause();
-
- verify(mMetricsFeature, never())
- .hidden(nullable(Context.class), anyInt());
- }
-
- private final class TestInstrumentable implements Instrumentable {
-
- public static final int TEST_METRIC = 12345;
-
- @Override
- public int getMetricsCategory() {
- return TEST_METRIC;
- }
- }
-}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 9ee205f9cde7..2a697b8d5034 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -302,6 +302,7 @@ public class SettingsProvider extends ContentProvider {
// fail to boot if there're any backed up settings that don't have a non-null validator
ensureAllBackedUpSystemSettingsHaveValidators();
ensureAllBackedUpGlobalSettingsHaveValidators();
+ ensureAllBackedUpSecureSettingsHaveValidators();
synchronized (mLock) {
mUserManager = UserManager.get(getContext());
@@ -321,19 +322,26 @@ public class SettingsProvider extends ContentProvider {
}
private void ensureAllBackedUpSystemSettingsHaveValidators() {
- String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP,
- Settings.System.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
+ Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
failToBootIfOffendersPresent(offenders, "Settings.System");
}
private void ensureAllBackedUpGlobalSettingsHaveValidators() {
- String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP,
- Settings.Global.VALIDATORS);
+ String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
+ Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
failToBootIfOffendersPresent(offenders, "Settings.Global");
}
+ private void ensureAllBackedUpSecureSettingsHaveValidators() {
+ String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
+ Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
+
+ failToBootIfOffendersPresent(offenders, "Settings.Secure");
+ }
+
private void failToBootIfOffendersPresent(String offenders, String settingsType) {
if (offenders.length() > 0) {
throw new RuntimeException("All " + settingsType + " settings that are backed up"
@@ -352,6 +360,18 @@ public class SettingsProvider extends ContentProvider {
return offenders.toString();
}
+ private final String[] concat(String[] first, String[] second) {
+ if (second == null || second.length == 0) {
+ return first;
+ }
+ final int firstLen = first.length;
+ final int secondLen = second.length;
+ String[] both = new String[firstLen + secondLen];
+ System.arraycopy(first, 0, both, 0, firstLen);
+ System.arraycopy(second, 0, both, firstLen, secondLen);
+ return both;
+ }
+
@Override
public Bundle call(String method, String name, Bundle args) {
final int requestingUserId = getRequestingUserId(args);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0f43db052622..64b2ae6e23d3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -135,6 +135,9 @@
<uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" />
<uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
+ <uses-permission android:name="android.permission.MANAGE_SENSORS" />
+ <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
+ <uses-permission android:name="android.permission.MANAGE_CAMERA" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index aa2cdbb7730f..80ac82576d13 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -121,7 +121,7 @@
<uses-permission android:name="android.permission.TRUST_LISTENER" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
- <uses-permission android:name="android.permission.BIND_SLICE" />
+ <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
@@ -435,6 +435,16 @@
android:launchMode="singleTop"
androidprv:alwaysFocusable="true" />
+ <!-- started from SliceProvider -->
+ <activity android:name=".SlicePermissionActivity"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.REQUEST_SLICE_PERMISSION" />
+ </intent-filter>
+ </activity>
+
<!-- platform logo easter egg activity -->
<activity
android:name=".DessertCase"
@@ -572,6 +582,7 @@
<provider android:name=".keyguard.KeyguardSliceProvider"
android:authorities="com.android.systemui.keyguard"
+ android:grantUriPermissions="true"
android:exported="true">
</provider>
diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml
index c19fa0803245..24afebde046b 100644
--- a/packages/SystemUI/res/color/qs_background_dark.xml
+++ b/packages/SystemUI/res/color/qs_background_dark.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.87"
+ <item android:alpha="1"
android:color="?android:attr/colorBackgroundFloating"/>
</selector>
diff --git a/packages/SystemUI/res/drawable/ic_screenshot_edit.xml b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
new file mode 100644
index 000000000000..d90129260fcc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.81,9.94l-3.75,-3.75L3.0,17.25zM20.71,7.04c0.39,-0.3 0.39,-1.02 0.0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+ <path
+ android:pathData="M0 0h24v24H0z"
+ android:fillColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_bg_gradient.xml b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
new file mode 100644
index 000000000000..a1ad52841eb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="270"
+ android:startColor="#ff000000"
+ android:endColor="#00000000"
+ android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index 59c0957d98cb..e52aa14abfa9 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -25,6 +25,6 @@
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
- android:paddingEnd="@dimen/battery_level_padding_start"
+ android:paddingStart="@dimen/battery_level_padding_start"
android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml
index 3d0ab3599bf1..b96f44750a0c 100644
--- a/packages/SystemUI/res/layout/output_chooser.xml
+++ b/packages/SystemUI/res/layout/output_chooser.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- extends FrameLayout -->
+<!-- extends LinearLayout -->
<com.android.systemui.volume.OutputChooserLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
@@ -23,8 +23,17 @@
android:minHeight="320dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:orientation="vertical"
android:padding="20dp" >
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:textAppearance="@style/TextAppearance.QS.DetailHeader"
+ android:layout_marginBottom="20dp" />
+
<com.android.systemui.qs.AutoSizingList
android:id="@android:id/list"
android:layout_width="match_parent"
@@ -42,9 +51,10 @@
android:orientation="vertical">
<TextView
- android:id="@android:id/title"
+ android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textDirection="locale"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml
index ed7df4b7f58a..c3ddbbe83dd7 100644
--- a/packages/SystemUI/res/layout/output_chooser_item.xml
+++ b/packages/SystemUI/res/layout/output_chooser_item.xml
@@ -30,6 +30,7 @@
android:layout_height="@dimen/qs_detail_item_icon_size"
android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
+ android:background="?android:selectableItemBackgroundBorderless"
android:tint="?android:attr/textColorPrimary"/>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 9ab8ac6346d3..b3b6a0c43a98 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,6 +20,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"
+ android:elevation="4dp"
android:orientation="vertical"
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 1fd239b15b30..0b9a7b226105 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -23,7 +23,8 @@
android:clickable="true"
android:orientation="vertical"
android:paddingBottom="8dp"
- android:visibility="invisible">
+ android:visibility="invisible"
+ android:elevation="4dp" >
<include
android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 3d09b743ca88..9f6a946dea2e 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -21,6 +21,7 @@
android:id="@+id/qs_footer"
android:layout_width="match_parent"
android:layout_height="48dp"
+ android:elevation="4dp"
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
@@ -105,17 +106,6 @@
android:visibility="invisible"/>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
- <com.android.systemui.statusbar.phone.ExpandableIndicator
- android:id="@+id/expand_indicator"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:clipToPadding="false"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:contentDescription="@string/accessibility_quick_settings_expand"
- android:padding="14dp" />
</LinearLayout>
</com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 5541f3de8e7c..5bcb7fd2685e 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -18,17 +18,46 @@
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/qs_background_primary"
- android:elevation="4dp"
android:clipToPadding="false"
- android:clipChildren="false">
+ android:clipChildren="false" >
+
+ <!-- Main QS background -->
+ <View
+ android:id="@+id/quick_settings_background"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:elevation="4dp"
+ android:background="@drawable/qs_background_primary" />
+
+ <!-- Black part behind the status bar -->
+ <View
+ android:id="@+id/quick_settings_status_bar_background"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_header_system_icons_area_height"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:background="#ff000000" />
+
+ <!-- Gradient view behind QS -->
+ <View
+ android:id="@+id/quick_settings_gradient_view"
+ android:layout_width="match_parent"
+ android:layout_height="126dp"
+ android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:background="@drawable/qs_bg_gradient" />
+
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
- android:layout_marginTop="28dp"
+ android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="48dp" />
+ android:layout_marginBottom="48dp"
+ android:elevation="4dp"
+ />
+
<include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
new file mode 100644
index 000000000000..e0f0ed994166
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/quick_qs_status_icons"
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:layout_marginBottom="22dp"
+ android:layout_below="@id/quick_status_bar_system_icons"
+ >
+
+</View>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index e8b418cd902b..dacc3f96220b 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -28,23 +28,21 @@
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
- android:paddingStart="0dp">
+ android:paddingStart="0dp"
+ android:elevation="4dp" >
<include layout="@layout/quick_status_bar_header_system_icons" />
+ <include layout="@layout/quick_qs_status_icons" />
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:layout_marginTop="31dp"
- android:layout_alignParentTop="true"
+ android:layout_below="@id/quick_qs_status_icons"
android:accessibilityTraversalAfter="@+id/date_time_group"
android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
android:focusable="true"
android:importantForAccessibility="yes" />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 739a2554e481..2c69501106a6 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/quick_status_bar_system_icons"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_header_system_icons_area_height"
android:layout_alignParentEnd="true"
@@ -40,6 +41,17 @@
systemui:showDark="false"
/>
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+ android:textSize="@dimen/qs_time_collapsed_size"
+ android:gravity="center_vertical"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
<android.widget.Space
android:id="@+id/space"
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
index 7762950b5a23..5074682f3808 100644
--- a/packages/SystemUI/res/layout/rotate_suggestion.xml
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -14,19 +14,13 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_width="@dimen/navigation_side_padding"
+<com.android.systemui.statusbar.policy.KeyButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/rotate_suggestion"
+ android:layout_width="@dimen/navigation_extra_key_width"
android:layout_height="match_parent"
- android:layout_weight="0"
- >
- <com.android.systemui.statusbar.policy.KeyButtonView
- android:id="@+id/rotate_suggestion"
- android:layout_width="@dimen/navigation_extra_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="2dp"
- android:visibility="invisible"
- android:scaleType="centerInside"
- />
- <!-- TODO android:contentDescription -->
-</FrameLayout>
+ android:layout_marginEnd="2dp"
+ android:visibility="invisible"
+ android:scaleType="centerInside"
+ android:contentDescription="@string/accessibility_rotate_button"
+/>
diff --git a/packages/SystemUI/res/layout/slice_permission_request.xml b/packages/SystemUI/res/layout/slice_permission_request.xml
new file mode 100644
index 000000000000..cdb2a91a73d2
--- /dev/null
+++ b/packages/SystemUI/res/layout/slice_permission_request.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<!-- Extends LinearLayout -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingStart="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/slice_permission_text_1" />
+
+ <TextView
+ android:id="@+id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:paddingBottom="16dp"
+ android:text="@string/slice_permission_text_2" />
+
+ <CheckBox
+ android:id="@+id/slice_permission_checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/slice_permission_checkbox" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 17b38cbce824..8c0b9bab35a0 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -54,30 +54,47 @@
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
+ <LinearLayout
android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:gravity="center_vertical|start"
- />
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ >
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:gravity="center_vertical|start"
+ />
+
+ <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+ PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/notification_icon_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal" />
- <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
- PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/notification_icon_area"
- android:layout_width="0dip"
+ </LinearLayout>
+
+ <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
+ <android.widget.Space
+ android:id="@+id/cutout_space_view"
+ android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal" />
+ android:gravity="center_horizontal|center_vertical"
+ />
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="match_parent"
+ android:layout_weight="1"
android:orientation="horizontal"
+ android:gravity="center_vertical|end"
>
<include layout="@layout/system_icons" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c5e5ee1f4af0..bef0830d5802 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,8 +43,6 @@
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:layout_marginStart="@dimen/notification_side_paddings"
- android:layout_marginEnd="@dimen/notification_side_paddings"
android:clipToPadding="false"
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index bfa92ad89a1e..1fafb2fc72d6 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -16,12 +16,13 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
- <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
- android:layout_width="wrap_content"
+ <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
+ android:layout_width="0dp"
+ android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0134086ae42e..4e17c71b0c93 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -215,7 +215,7 @@
<dimen name="close_handle_underlap">32dp</dimen>
<!-- Height of the status bar header bar -->
- <dimen name="status_bar_header_height">124dp</dimen>
+ <dimen name="status_bar_header_height">178dp</dimen>
<!-- Height of the status bar header bar in the car setting. -->
<dimen name="car_status_bar_header_height">128dp</dimen>
@@ -223,8 +223,9 @@
<!-- The bottom padding of the status bar header. -->
<dimen name="status_bar_header_padding_bottom">48dp</dimen>
- <!-- The height of the container that holds the system icons in the quick settings header. -->
- <dimen name="qs_header_system_icons_area_height">40dp</dimen>
+ <!-- The height of the container that holds the battery and time in the quick settings header.
+ -->
+ <dimen name="qs_header_system_icons_area_height">48dp</dimen>
<!-- The height of the container that holds the system icons in the quick settings header in the
car setting. -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index fed97c57ce09..edda613f2fbc 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -93,5 +93,8 @@
<item type="id" name="action_snooze_long"/>
<item type="id" name="action_snooze_longer"/>
<item type="id" name="action_snooze_assistant_suggestion_1"/>
+
+ <!-- For StatusBarIconContainer to tag its icon views -->
+ <item type="id" name="status_bar_view_state_tag" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6437903bd12a..ab83bcf23ba5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -228,6 +228,8 @@
<string name="accessibility_menu">Menu</string>
<!-- Content description of the accessibility button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_accessibility_button">Accessibility</string>
+ <!-- Content description of the rotate button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_rotate_button">Rotate screen</string>
<!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_recent">Overview</string>
<!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] -->
@@ -1702,12 +1704,14 @@
<item>Clipboard</item>
<item>Keycode</item>
<item>Keyboard switcher</item>
+ <item>Rotation suggestion</item>
<item>None</item>
</string-array>
<string-array name="nav_bar_button_values" translatable="false">
<item>clipboard</item>
<item>key</item>
<item>menu_ime</item>
+ <item>rotate</item>
<item>space</item>
</string-array>
@@ -2065,5 +2069,21 @@
<string name="touch_filtered_warning">Because an app is obscuring a permission request, Settings
can’t verify your response.</string>
+ <!-- Title of prompt requesting access to display slices [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_title">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices?</string>
+
+ <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_text_1"> - It can read information from <xliff:g id="app" example="Example App">%1$s</xliff:g></string>
+ <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_text_2"> - It can take actions inside <xliff:g id="app" example="Example App">%1$s</xliff:g></string>
+
+ <!-- Text on checkbox allowing the app to show slices from all apps [CHAR LIMIT=NONE] -->
+ <string name="slice_permission_checkbox">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show slices from any app</string>
+
+ <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
+ <string name="slice_permission_allow">Allow</string>
+
+ <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
+ <string name="slice_permission_deny">Deny</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4837fefd04b2..bcce6d1d2ee8 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -173,7 +173,7 @@
<style name="TextAppearance.StatusBar.Expanded.Date">
<item name="android:textSize">@dimen/qs_time_expanded_size</item>
<item name="android:textStyle">normal</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">#ffffffff</item>
<item name="android:fontFamily">sans-serif</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 13f30b2c27b9..7d159b745254 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -20,6 +20,7 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.RectEvaluator;
import android.annotation.FloatRange;
+import android.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
@@ -39,6 +40,7 @@ import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.ViewRootImpl;
import android.view.ViewStub;
import java.util.ArrayList;
@@ -294,17 +296,25 @@ public class Utilities {
}
/**
- * @return The next frame name for the specified surface.
+ * @return The next frame name for the specified surface or -1 if the surface is no longer
+ * valid.
*/
public static long getNextFrameNumber(Surface s) {
- return s.getNextFrameNumber();
+ return s != null && s.isValid()
+ ? s.getNextFrameNumber()
+ : -1;
+
}
/**
* @return The surface for the specified view.
*/
- public static Surface getSurface(View v) {
- return v.getViewRootImpl().mSurface;
+ public static @Nullable Surface getSurface(View v) {
+ ViewRootImpl viewRoot = v.getViewRootImpl();
+ if (viewRoot == null) {
+ return null;
+ }
+ return viewRoot.mSurface;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e58ad0589c8a..19afcf5e33a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -991,6 +991,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
maxChargingWattage > fastThreshold ? CHARGING_FAST :
CHARGING_REGULAR;
}
+
+ @Override
+ public String toString() {
+ return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
+ + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
+ }
}
public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
@@ -1624,18 +1630,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
return true;
}
- // change in battery level while plugged in
- if (nowPluggedIn && old.level != current.level) {
- return true;
- }
-
- // change in battery level while keyguard visible
- if (mKeyguardIsVisible && old.level != current.level) {
- return true;
- }
-
- // change where battery needs charging
- if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
+ // change in battery level
+ if (old.level != current.level) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2fe66a14a41d..8666b0c873e7 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -224,7 +224,6 @@ public class BatteryMeterView extends LinearLayout implements
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
updatePercentText();
addView(mBatteryPercentView,
- 0,
new ViewGroup.LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT));
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
new file mode 100644
index 000000000000..302face14c1a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.slice.SliceManager;
+import android.app.slice.SliceProvider;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+public class SlicePermissionActivity extends Activity implements OnClickListener,
+ OnDismissListener {
+
+ private static final String TAG = "SlicePermissionActivity";
+
+ private CheckBox mAllCheckbox;
+
+ private Uri mUri;
+ private String mCallingPkg;
+ private String mProviderPkg;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUri = getIntent().getParcelableExtra(SliceProvider.EXTRA_BIND_URI);
+ mCallingPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PKG);
+ mProviderPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PROVIDER_PKG);
+
+ try {
+ PackageManager pm = getPackageManager();
+ CharSequence app1 = pm.getApplicationInfo(mCallingPkg, 0).loadLabel(pm);
+ CharSequence app2 = pm.getApplicationInfo(mProviderPkg, 0).loadLabel(pm);
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.slice_permission_title, app1, app2))
+ .setView(R.layout.slice_permission_request)
+ .setNegativeButton(R.string.slice_permission_deny, this)
+ .setPositiveButton(R.string.slice_permission_allow, this)
+ .setOnDismissListener(this)
+ .show();
+ TextView t1 = dialog.getWindow().getDecorView().findViewById(R.id.text1);
+ t1.setText(getString(R.string.slice_permission_text_1, app2));
+ TextView t2 = dialog.getWindow().getDecorView().findViewById(R.id.text2);
+ t2.setText(getString(R.string.slice_permission_text_2, app2));
+ mAllCheckbox = dialog.getWindow().getDecorView().findViewById(
+ R.id.slice_permission_checkbox);
+ mAllCheckbox.setText(getString(R.string.slice_permission_checkbox, app1));
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Couldn't find package", e);
+ finish();
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ getSystemService(SliceManager.class).grantPermissionFromUser(mUri, mCallingPkg,
+ mAllCheckbox.isChecked());
+ }
+ finish();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index 931a99415615..69e347c9476d 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -463,4 +463,8 @@ public class DataCollector implements SensorEventListener {
public boolean isReportingEnabled() {
return mAllowReportRejectedTouch;
}
+
+ public void onFalsingSessionStarted() {
+ sessionEntrypoint();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index e4b405f580d4..ed659e2d16d5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -167,6 +167,9 @@ public class FalsingManager implements SensorEventListener {
if (mDataCollector.isEnabledFull()) {
registerSensors(COLLECTOR_SENSORS);
}
+ if (mDataCollector.isEnabled()) {
+ mDataCollector.onFalsingSessionStarted();
+ }
}
private void registerSensors(int [] sensors) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4b775a5a61e4..b8411e2963bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -608,6 +608,9 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha
public void onScanningStateChanged(boolean started) { }
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) { }
}
private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7f0acc254a7c..7320b861dbd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -45,8 +45,9 @@ public class QSContainerImpl extends FrameLayout {
protected float mQsExpansion;
private QSCustomizer mQSCustomizer;
private View mQSFooter;
- private float mFullElevation;
+ private View mBackground;
private float mRadius;
+ private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -60,12 +61,14 @@ public class QSContainerImpl extends FrameLayout {
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
- mFullElevation = mQSPanel.getElevation();
+ 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);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ setMargins();
}
@Override
@@ -131,6 +134,8 @@ public class QSContainerImpl extends FrameLayout {
mQSDetail.setBottom(getTop() + height);
// Pin QS Footer to the bottom of the panel.
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
+ mBackground.setTop(mQSPanel.getTop());
+ mBackground.setBottom(height);
ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
mRadius,
@@ -148,4 +153,19 @@ public class QSContainerImpl extends FrameLayout {
mQsExpansion = expansion;
updateExpansion();
}
+
+ private void setMargins() {
+ setMargins(mQSDetail);
+ setMargins(mBackground);
+ setMargins(mQSFooter);
+ setMargins(mQSPanel);
+ setMargins(mHeader);
+ setMargins(mQSCustomizer);
+ }
+
+ private void setMargins(View view) {
+ FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.rightMargin = mSideMargins;
+ lp.leftMargin = mSideMargins;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 927a49cb60f3..92475da697bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -61,18 +61,16 @@ import com.android.systemui.tuner.TunerService;
public class QSFooterImpl extends FrameLayout implements QSFooter,
OnClickListener, OnUserInfoChangedListener, EmergencyListener,
SignalCallback, CommandQueue.Callbacks {
- private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
-
private ActivityStarter mActivityStarter;
private UserInfoController mUserInfoController;
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
+ private View mCarrierText;
private boolean mQsDisabled;
private QSPanel mQsPanel;
private boolean mExpanded;
- protected ExpandableIndicator mExpandIndicator;
private boolean mListening;
@@ -100,18 +98,18 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
mQsPanel.showEdit(view)));
- mExpandIndicator = findViewById(R.id.expand_indicator);
mSettingsButton = findViewById(R.id.settings_button);
mSettingsContainer = findViewById(R.id.settings_button_container);
mSettingsButton.setOnClickListener(this);
+ mCarrierText = findViewById(R.id.qs_carrier_text);
+
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
// 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);
- ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
updateResources();
@@ -162,6 +160,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
return new TouchAnimator.Builder()
.addFloat(mEdit, "alpha", 0, 1)
.addFloat(mMultiUserSwitch, "alpha", 0, 1)
+ .addFloat(mCarrierText, "alpha", 0, 1)
+ .addFloat(mSettingsButton, "alpha", 0, 1)
.build();
}
@@ -185,8 +185,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
if (mSettingsAlpha != null) {
mSettingsAlpha.setPosition(headerExpansionFraction);
}
-
- mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
}
@Override
@@ -237,8 +235,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
- mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
-
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 398592ad1699..a97b35cba048 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -18,10 +18,12 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.app.StatusBarManager.DISABLE_NONE;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
+import android.provider.AlarmClock;
import android.support.annotation.VisibleForTesting;
import android.util.AttributeSet;
import android.view.View;
@@ -38,9 +40,11 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.SignalClusterView;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks {
+public class QuickStatusBarHeader extends RelativeLayout
+ implements CommandQueue.Callbacks, View.OnClickListener {
private ActivityStarter mActivityStarter;
@@ -53,6 +57,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue
protected QuickQSPanel mHeaderQsPanel;
protected QSTileHost mHost;
+ private View mDate;
+
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -63,21 +69,21 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue
Resources res = getResources();
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
+ mDate = findViewById(R.id.date);
+ mDate.setOnClickListener(this);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
updateResources();
- // Set the light/dark theming on the header status UI to match the current theme.
+ // Set light text on the header icons because they will always be on a black background
int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
- float intensity = colorForeground == Color.WHITE ? 0 : 1;
Rect tintArea = new Rect(0, 0, 0, 0);
-
- applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
- applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
+ applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
BatteryMeterView battery = findViewById(R.id.battery);
+ battery.setFillColor(Color.WHITE);
battery.setForceShowPercent(true);
mActivityStarter = Dependency.get(ActivityStarter.class);
@@ -146,6 +152,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue
super.onDetachedFromWindow();
}
+ @Override
+ public void onClick(View v) {
+ if (v == mDate) {
+ Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ AlarmClock.ACTION_SHOW_ALARMS), 0);
+ }
+ }
+
public void setListening(boolean listening) {
if (listening == mListening) {
return;
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 9e265e2295c3..4ceace34befe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -33,7 +33,6 @@ import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.quicksettings.Tile;
-import android.util.Log;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
@@ -55,7 +54,6 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeController.Callback;
import com.android.systemui.volume.ZenModePanel;
/** Quick settings tile: Do not disturb **/
@@ -134,8 +132,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
if (mState.value) {
mController.setZen(ZEN_MODE_OFF, null, TAG);
} else {
- int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
- mController.setZen(zen, null, TAG);
+ mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
}
}
@@ -159,9 +156,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
showDetail(true);
}
});
- int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
- Global.ZEN_MODE_ALARMS);
- mController.setZen(zen, null, TAG);
+ mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
} else {
showDetail(true);
}
@@ -313,9 +308,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
mController.setZen(ZEN_MODE_OFF, null, TAG);
mAuto = false;
} else {
- int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
- ZEN_MODE_ALARMS);
- mController.setZen(zen, null, TAG);
+ mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 6db46b5917b6..675aa8f73a09 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -293,12 +293,13 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
- // Create a share action for the notification. Note, we proxy the call to ShareReceiver
- // because RemoteViews currently forces an activity options on the PendingIntent being
- // launched, and since we don't want to trigger the share sheet in this case, we will
- // start the chooser activitiy directly in ShareReceiver.
+ // Create a share action for the notification. Note, we proxy the call to
+ // ScreenshotActionReceiver because RemoteViews currently forces an activity options
+ // on the PendingIntent being launched, and since we don't want to trigger the share
+ // sheet in this case, we start the chooser activity directly in
+ // ScreenshotActionReceiver.
PendingIntent shareAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.ShareReceiver.class)
+ new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
.putExtra(SHARING_INTENT, sharingIntent),
PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
@@ -306,15 +307,19 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
r.getString(com.android.internal.R.string.share), shareAction);
mNotificationBuilder.addAction(shareActionBuilder.build());
- // Create a delete action for the notification
- PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
- .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_delete,
- r.getString(com.android.internal.R.string.delete), deleteAction);
- mNotificationBuilder.addAction(deleteActionBuilder.build());
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ editIntent.setType("image/png");
+ editIntent.putExtra(Intent.EXTRA_STREAM, uri);
+
+ // Create a edit action for the notification the same way.
+ PendingIntent editAction = PendingIntent.getBroadcast(context, 1,
+ new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
+ .putExtra(SHARING_INTENT, editIntent),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_edit,
+ r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+ mNotificationBuilder.addAction(editActionBuilder.build());
mParams.imageUri = uri;
mParams.image = null;
@@ -879,9 +884,9 @@ class GlobalScreenshot {
}
/**
- * Receiver to proxy the share intent.
+ * Receiver to proxy the share or edit intent.
*/
- public static class ShareReceiver extends BroadcastReceiver {
+ public static class ScreenshotActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
@@ -903,7 +908,7 @@ class GlobalScreenshot {
}
/**
- * Removes the notification for a screenshot after a share target is chosen.
+ * Removes the notification for a screenshot after a share or edit target is chosen.
*/
public static class TargetChosenReceiver extends BroadcastReceiver {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 657b9534f3a8..c6abcf272722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -144,7 +144,7 @@ public class CommandQueue extends IStatusBar.Stub {
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
- default void onRotationProposal(int rotation) { }
+ default void onRotationProposal(int rotation, boolean isValid) { }
}
@VisibleForTesting
@@ -462,10 +462,10 @@ public class CommandQueue extends IStatusBar.Stub {
}
@Override
- public void onProposedRotationChanged(int rotation) {
+ public void onProposedRotationChanged(int rotation, boolean isValid) {
synchronized (mLock) {
mHandler.removeMessages(MSG_ROTATION_PROPOSAL);
- mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, 0,
+ mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, isValid ? 1 : 0,
null).sendToTarget();
}
}
@@ -668,7 +668,7 @@ public class CommandQueue extends IStatusBar.Stub {
break;
case MSG_ROTATION_PROPOSAL:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRotationProposal(msg.arg1);
+ mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 43047ed6a5c5..0a12be4eac7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -52,6 +52,8 @@ import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.text.NumberFormat;
/**
@@ -116,11 +118,9 @@ public class KeyguardIndicationController {
WakeLock wakeLock) {
mContext = context;
mIndicationArea = indicationArea;
- mTextView = (KeyguardIndicationTextView) indicationArea.findViewById(
- R.id.keyguard_indication_text);
+ mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
mInitialTextColor = mTextView != null ? mTextView.getCurrentTextColor() : Color.WHITE;
- mDisclosure = (KeyguardIndicationTextView) indicationArea.findViewById(
- R.id.keyguard_indication_enterprise_disclosure);
+ mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
mLockIcon = lockIcon;
mWakeLock = new SettableWakeLock(wakeLock);
@@ -416,6 +416,21 @@ public class KeyguardIndicationController {
updateDisclosure();
}
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("KeyguardIndicationController:");
+ pw.println(" mTransientTextColor: " + Integer.toHexString(mTransientTextColor));
+ pw.println(" mInitialTextColor: " + Integer.toHexString(mInitialTextColor));
+ pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
+ pw.println(" mPowerCharged: " + mPowerCharged);
+ pw.println(" mChargingSpeed: " + mChargingSpeed);
+ pw.println(" mChargingWattage: " + mChargingWattage);
+ pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
+ pw.println(" mDozing: " + mDozing);
+ pw.println(" mBatteryLevel: " + mBatteryLevel);
+ pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
+ pw.println(" computePowerIndication(): " + computePowerIndication());
+ }
+
protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
public static final int HIDE_DELAY_MS = 5000;
private int mLastSuccessiveErrorMessage = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 5b06874219e6..7360486ac7e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -616,8 +616,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
&& entry.row.getGuts() == mGutsManager.getExposedGuts();
entry.row.onDensityOrFontScaleChanged();
if (exposedGuts) {
- mGutsManager.setExposedGuts(entry.row.getGuts());
- mGutsManager.bindGuts(entry.row);
+ mGutsManager.onDensityOrFontScaleChanged(entry.row);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index c4024a57ceed..52776d7e8430 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -23,6 +23,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -187,6 +188,12 @@ public class NotificationGuts extends FrameLayout {
}
}
+ public void openControls(
+ int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) {
+ animateOpen(x, y, onAnimationEnd);
+ setExposed(true /* exposed */, needsFalsingProtection);
+ }
+
public void closeControls(boolean leavebehinds, boolean controls, int x, int y, boolean force) {
if (mGutsContent != null) {
if (mGutsContent.isLeavebehind() && leavebehinds) {
@@ -214,6 +221,27 @@ public class NotificationGuts extends FrameLayout {
}
}
+ private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
+ final double horz = Math.max(getWidth() - x, x);
+ final double vert = Math.max(getHeight() - y, y);
+ final float r = (float) Math.hypot(horz, vert);
+
+ final Animator a
+ = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (onAnimationEnd != null) {
+ onAnimationEnd.run();
+ }
+ }
+ });
+ a.start();
+ }
+
private void animateClose(int x, int y) {
if (x == -1 || y == -1) {
x = (getLeft() + getRight()) / 2;
@@ -279,7 +307,7 @@ public class NotificationGuts extends FrameLayout {
}
}
- public void setExposed(boolean exposed, boolean needsFalsingProtection) {
+ private void setExposed(boolean exposed, boolean needsFalsingProtection) {
final boolean wasExposed = mExposed;
mExposed = exposed;
mNeedsFalsingProtection = needsFalsingProtection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 441c18431894..9d8892da3c74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui.statusbar;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.content.Context;
@@ -32,17 +30,14 @@ import android.util.ArraySet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.View;
-import android.view.ViewAnimationUtils;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -112,6 +107,11 @@ public class NotificationGutsManager implements Dumpable {
mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
}
+ public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
+ setExposedGuts(row.getGuts());
+ bindGuts(row);
+ }
+
private void saveAndCloseNotificationMenu(
ExpandableNotificationRow row, NotificationGuts guts, View done) {
guts.resetFalsingCheck();
@@ -270,7 +270,7 @@ public class NotificationGutsManager implements Dumpable {
}
/**
- * Opens guts on the given ExpandableNotificationRow |v|.
+ * Opens guts on the given ExpandableNotificationRow |v|.
*
* @param v ExpandableNotificationRow to open guts on
* @param x x coordinate of origin of circular reveal
@@ -326,26 +326,15 @@ public class NotificationGutsManager implements Dumpable {
true /* removeControls */, -1 /* x */, -1 /* y */,
false /* resetMenu */);
guts.setVisibility(View.VISIBLE);
- final double horz = Math.max(guts.getWidth() - x, x);
- final double vert = Math.max(guts.getHeight() - y, y);
- final float r = (float) Math.hypot(horz, vert);
- final Animator a
- = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- a.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- // Move the notification view back over the menu
- row.resetTranslation();
- }
- });
- a.start();
+
final boolean needsFalsingProtection =
(mPresenter.isPresenterLocked() &&
!mAccessibilityManager.isTouchExplorationEnabled());
- guts.setExposed(true /* exposed */, needsFalsingProtection);
+ guts.openControls(x, y, needsFalsingProtection, () -> {
+ // Move the notification view back over the menu
+ row.resetTranslation();
+ });
+
row.closeRemoteInput();
mListContainer.onHeightChanged(row, true /* needsAnimation */);
mNotificationGutsExposed = guts;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 9b123cbea8d7..7284ee8b39bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -34,6 +34,7 @@ public class ButtonDispatcher {
private View.OnClickListener mClickListener;
private View.OnTouchListener mTouchListener;
private View.OnLongClickListener mLongClickListener;
+ private View.OnHoverListener mOnHoverListener;
private Boolean mLongClickable;
private Integer mAlpha;
private Float mDarkIntensity;
@@ -56,6 +57,7 @@ public class ButtonDispatcher {
view.setOnClickListener(mClickListener);
view.setOnTouchListener(mTouchListener);
view.setOnLongClickListener(mLongClickListener);
+ view.setOnHoverListener(mOnHoverListener);
if (mLongClickable != null) {
view.setLongClickable(mLongClickable);
}
@@ -174,6 +176,14 @@ public class ButtonDispatcher {
}
}
+ public void setOnHoverListener(View.OnHoverListener hoverListener) {
+ mOnHoverListener = hoverListener;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setOnHoverListener(mOnHoverListener);
+ }
+ }
+
public void setClickable(boolean clickable) {
abortCurrentGesture();
final int N = mViews.size();
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 70ec45e4cdb4..368b36bfd8e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -26,7 +26,6 @@ import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -111,7 +110,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
- private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000;
+ private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
protected NavigationBarView mNavigationBarView = null;
protected AssistManager mAssistManager;
@@ -120,6 +119,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
private int mNavigationIconHints = 0;
private int mNavigationBarMode;
+ private boolean mAccessibilityFeedbackEnabled;
private AccessibilityManager mAccessibilityManager;
private MagnificationContentObserver mMagnificationObserver;
private ContentResolver mContentResolver;
@@ -143,6 +143,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
public boolean mHomeBlockedThisTouch;
private int mLastRotationSuggestion;
+ private boolean mHoveringRotationSuggestion;
private RotationLockController mRotationLockController;
private TaskStackListenerImpl mTaskStackListener;
@@ -335,43 +336,77 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
}
@Override
- public void onRotationProposal(final int rotation) {
- // This method will only be called if rotation is valid but will include proposals for the
- // current system rotation
- Handler h = getView().getHandler();
+ public void onRotationProposal(final int rotation, boolean isValid) {
+ // This method will be called on rotation suggestion changes even if the proposed rotation
+ // is not valid for the top app. Use invalid rotation choices as a signal to remove the
+ // rotate button if shown.
+
+ if (!isValid) {
+ setRotateSuggestionButtonState(false);
+ return;
+ }
+
if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
// Use this as a signal to remove any current suggestions
- h.removeCallbacks(mRemoveRotationProposal);
+ getView().getHandler().removeCallbacks(mRemoveRotationProposal);
setRotateSuggestionButtonState(false);
} else {
mLastRotationSuggestion = rotation; // Remember rotation for click
setRotateSuggestionButtonState(true);
- h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
- h.postDelayed(mRemoveRotationProposal,
- ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout
+ rescheduleRotationTimeout(false);
}
}
+ private void rescheduleRotationTimeout(final boolean reasonHover) {
+ // May be called due to a new rotation proposal or a change in hover state
+ if (reasonHover) {
+ // Don't reschedule if a hide animator is running
+ if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+ return;
+ }
+ // Don't reschedule if not visible
+ if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) {
+ return;
+ }
+ }
+
+ Handler h = getView().getHandler();
+ h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
+ h.postDelayed(mRemoveRotationProposal,
+ computeRotationProposalTimeout()); // Schedule timeout
+ }
+
+ private int computeRotationProposalTimeout() {
+ if (mAccessibilityFeedbackEnabled) return 20000;
+ if (mHoveringRotationSuggestion) return 16000;
+ return 6000;
+ }
+
public void setRotateSuggestionButtonState(final boolean visible) {
setRotateSuggestionButtonState(visible, false);
}
public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
- boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
+ final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
// Rerun a show animation to indicate change but don't rerun a hide animation
if (!visible && !currentlyVisible) return;
- View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView();
+ View currentView = rotBtn.getCurrentView();
if (currentView == null) return;
- KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable();
+ KeyButtonDrawable kbd = rotBtn.getImageDrawable();
if (kbd == null) return;
- AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+ AnimatedVectorDrawable animIcon = null;
+ if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
+ animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+ }
+
if (visible) { // Appear and change
rotBtn.setVisibility(View.VISIBLE);
+ mNavigationBarView.notifyAccessibilitySubtreeChanged();
if (skipAnim) {
currentView.setAlpha(1f);
@@ -384,18 +419,22 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
0f, 1f);
- appearFade.setDuration(100);
+ appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
appearFade.setInterpolator(Interpolators.LINEAR);
mRotateShowAnimator = appearFade;
appearFade.start();
- // Run the rotate icon's animation
- animIcon.reset();
- animIcon.start();
+ // Run the rotate icon's animation if it has one
+ if (animIcon != null) {
+ animIcon.reset();
+ animIcon.start();
+ }
+
} else { // Hide
if (skipAnim) {
rotBtn.setVisibility(View.INVISIBLE);
+ mNavigationBarView.notifyAccessibilitySubtreeChanged();
return;
}
@@ -406,12 +445,13 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
0f);
- fadeOut.setDuration(100);
+ fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
fadeOut.setInterpolator(Interpolators.LINEAR);
fadeOut.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
rotBtn.setVisibility(View.INVISIBLE);
+ mNavigationBarView.notifyAccessibilitySubtreeChanged();
}
});
@@ -525,6 +565,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
+ rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
}
private boolean onHomeTouch(View v, MotionEvent event) {
@@ -700,6 +741,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
} catch (Settings.SettingNotFoundException e) {
}
+ boolean feedbackEnabled = false;
// AccessibilityManagerService resolves services for the current user since the local
// AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
final List<AccessibilityServiceInfo> services =
@@ -710,8 +752,15 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
requestingServices++;
}
+
+ if (info.feedbackType != 0 && info.feedbackType !=
+ AccessibilityServiceInfo.FEEDBACK_GENERIC) {
+ feedbackEnabled = true;
+ }
}
+ mAccessibilityFeedbackEnabled = feedbackEnabled;
+
final boolean showAccessibilityButton = requestingServices >= 1;
final boolean targetSelection = requestingServices >= 2;
mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
@@ -721,6 +770,14 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
}
+ private boolean onRotateSuggestionHover(View v, MotionEvent event) {
+ final int action = event.getActionMasked();
+ mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
+ || (action == MotionEvent.ACTION_HOVER_MOVE);
+ rescheduleRotationTimeout(true);
+ return false; // Must return false so a11y hover events are dispatched correctly.
+ }
+
// ----- Methods that StatusBar talks to (should be minimized) -----
public void setLightBarController(LightBarController lightBarController) {
@@ -768,18 +825,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
private final Stub mRotationWatcher = new Stub() {
@Override
- public void onRotationChanged(int rotation) throws RemoteException {
- // If the screen rotation changes while locked, update lock rotation to flow with
- // new screen rotation and hide any showing suggestions.
- if (mRotationLockController.isRotationLocked()) {
- mRotationLockController.setRotationLockedAtAngle(true, rotation);
- setRotateSuggestionButtonState(false, true);
- }
-
+ public void onRotationChanged(final int rotation) throws RemoteException {
// We need this to be scheduled as early as possible to beat the redrawing of
// window in response to the orientation change.
Handler h = getView().getHandler();
Message msg = Message.obtain(h, () -> {
+ // If the screen rotation changes while locked, update lock rotation to flow with
+ // new screen rotation and hide any showing suggestions.
+ if (mRotationLockController.isRotationLocked()) {
+ mRotationLockController.setRotationLockedAtAngle(true, rotation);
+ setRotateSuggestionButtonState(false, true);
+ }
+
if (mNavigationBarView != null
&& mNavigationBarView.needsReorient(rotation)) {
repositionNavigationBar();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 970d1de251d5..b18121255353 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,26 +16,34 @@
package com.android.systemui.statusbar.phone;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.EventLog;
+import android.view.DisplayCutout;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.DejankUtils;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.util.leak.RotationUtils;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
+ private static final int NO_VALUE = Integer.MIN_VALUE;
StatusBar mBar;
@@ -53,6 +61,11 @@ public class PhoneStatusBarView extends PanelBar {
}
};
private DarkReceiver mBattery;
+ private int mLastOrientation;
+ @Nullable
+ private View mCutoutSpace;
+ @Nullable
+ private DisplayCutout mDisplayCutout;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -76,6 +89,7 @@ public class PhoneStatusBarView extends PanelBar {
public void onFinishInflate() {
mBarTransitions.init();
mBattery = findViewById(R.id.battery);
+ mCutoutSpace = findViewById(R.id.cutout_space_view);
}
@Override
@@ -83,12 +97,51 @@ public class PhoneStatusBarView extends PanelBar {
super.onAttachedToWindow();
// Always have Battery meters in the status bar observe the dark/light modes.
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
+ if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+ postUpdateLayoutForCutout();
+ }
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
+ mDisplayCutout = null;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ // May trigger cutout space layout-ing
+ if (updateOrientationAndCutout(newConfig.orientation)) {
+ postUpdateLayoutForCutout();
+ }
+ }
+
+ /**
+ *
+ * @param newOrientation may pass NO_VALUE for no change
+ * @return boolean indicating if we need to update the cutout location / margins
+ */
+ private boolean updateOrientationAndCutout(int newOrientation) {
+ boolean changed = false;
+ if (newOrientation != NO_VALUE) {
+ if (mLastOrientation != newOrientation) {
+ changed = true;
+ mLastOrientation = newOrientation;
+ }
+ }
+
+ if (mDisplayCutout == null) {
+ DisplayCutout cutout = getRootWindowInsets().getDisplayCutout();
+ if (cutout != null) {
+ changed = true;
+ mDisplayCutout = cutout;
+ }
+ }
+
+ return changed;
}
@Override
@@ -214,4 +267,80 @@ public class PhoneStatusBarView extends PanelBar {
R.dimen.status_bar_height);
setLayoutParams(layoutParams);
}
+
+ private void updateLayoutForCutout() {
+ updateCutoutLocation();
+ updateSafeInsets();
+ }
+
+ private void postUpdateLayoutForCutout() {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ updateLayoutForCutout();
+ }
+ };
+ // Let the cutout emulation draw first
+ postDelayed(r, 0);
+ }
+
+ private void updateCutoutLocation() {
+ // Not all layouts have a cutout (e.g., Car)
+ if (mCutoutSpace == null) {
+ return;
+ }
+
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()
+ || mLastOrientation != ORIENTATION_PORTRAIT) {
+ mCutoutSpace.setVisibility(View.GONE);
+ return;
+ }
+
+ mCutoutSpace.setVisibility(View.VISIBLE);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
+ lp.width = mDisplayCutout.getBoundingRect().width();
+ lp.height = mDisplayCutout.getBoundingRect().height();
+ }
+
+ private void updateSafeInsets() {
+ // Depending on our rotation, we may have to work around a cutout in the middle of the view,
+ // or letterboxing from the right or left sides.
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ lp.leftMargin = 0;
+ lp.rightMargin = 0;
+ return;
+ }
+
+ int leftMargin = 0;
+ int rightMargin = 0;
+ switch (RotationUtils.getRotation(getContext())) {
+ /*
+ * Landscape: <-|
+ * Seascape: |->
+ */
+ case RotationUtils.ROTATION_LANDSCAPE:
+ leftMargin = getDisplayCutoutHeight();
+ break;
+ case RotationUtils.ROTATION_SEASCAPE:
+ rightMargin = getDisplayCutoutHeight();
+ break;
+ default:
+ break;
+ }
+
+ lp.leftMargin = leftMargin;
+ lp.rightMargin = rightMargin;
+ }
+
+ //TODO: Find a better way
+ private int getDisplayCutoutHeight() {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ return 0;
+ }
+
+ Rect r = mDisplayCutout.getBoundingRect();
+ return r.bottom - r.top;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 14329b564800..3b394ddd63cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -866,13 +866,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" ScrimController:");
- pw.print(" state:"); pw.println(mState);
- pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
+ pw.println(" ScrimController: ");
+ pw.print(" state: "); pw.println(mState);
+ pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
pw.print(" alpha="); pw.print(mCurrentInFrontAlpha);
pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint()));
- pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
+ pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
pw.print(" alpha="); pw.print(mCurrentBehindAlpha);
pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint()));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 6220fcbdf13b..1130b6de2544 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -32,6 +32,8 @@ import com.android.systemui.Interpolators;
public class SettingsButton extends AlphaOptimizedImageButton {
+ private static final boolean TUNER_ENABLE_AVAILABLE = false;
+
private static final long LONG_PRESS_LENGTH = 1000;
private static final long ACCEL_LENGTH = 750;
private static final long FULL_SPEED_LENGTH = 375;
@@ -59,7 +61,7 @@ public class SettingsButton extends AlphaOptimizedImageButton {
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- postDelayed(mLongPressCallback, LONG_PRESS_LENGTH);
+ if (TUNER_ENABLE_AVAILABLE) postDelayed(mLongPressCallback, LONG_PRESS_LENGTH);
break;
case MotionEvent.ACTION_UP:
if (mUpToSpeed) {
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 5e08ec3575fc..c30fb22630da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1704,7 +1704,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mReportRejectedTouch == null) {
return;
}
- mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD
+ mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing
&& mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
}
@@ -2663,6 +2663,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mFingerprintUnlockController.dump(pw);
}
+ if (mKeyguardIndicationController != null) {
+ mKeyguardIndicationController.dump(fd, pw, args);
+ }
+
if (mScrimController != null) {
mScrimController.dump(fd, pw, args);
}
@@ -4506,6 +4510,7 @@ public class StatusBar extends SystemUI implements DemoMode,
((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
}
updateDozingState();
+ updateReportRejectedTouchVisibility();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index bcda60ebc62c..07610ceff7b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -62,7 +62,7 @@ public interface StatusBarIconController {
}
/**
- * Version of ViewGroup that observers state from the DarkIconDispatcher.
+ * Version of ViewGroup that observes state from the DarkIconDispatcher.
*/
public static class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
new file mode 100644
index 000000000000..1897171b5e54
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+/**
+ * A container for Status bar system icons. Limits the number of system icons and handles overflow
+ * similar to NotificationIconController. Can be used to layout nested StatusIconContainers
+ *
+ * Children are expected to be of type StatusBarIconView.
+ */
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+
+import android.view.View;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.stack.ViewState;
+
+public class StatusIconContainer extends AlphaOptimizedLinearLayout {
+
+ private static final String TAG = "StatusIconContainer";
+ private static final int MAX_ICONS = 5;
+ private static final int MAX_DOTS = 3;
+
+ public StatusIconContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ float midY = getHeight() / 2.0f;
+
+ // Layout all child views so that we can move them around later
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
+ int top = (int) (midY - height / 2.0f);
+ child.layout(0, top, width, top + height);
+ }
+
+ resetViewStates();
+ calculateIconTranslations();
+ applyIconStates();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ final int count = getChildCount();
+ // Measure all children so that they report the correct width
+ for (int i = 0; i < count; i++) {
+ measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ ViewState vs = new ViewState();
+ child.setTag(R.id.status_bar_view_state_tag, vs);
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ child.setTag(R.id.status_bar_view_state_tag, null);
+ }
+
+ /**
+ * Layout is happening from end -> start
+ */
+ private void calculateIconTranslations() {
+ float translationX = getWidth();
+ float contentStart = getPaddingStart();
+ int childCount = getChildCount();
+ // Underflow === don't show content until that index
+ int firstUnderflowIndex = -1;
+ android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+
+ //TODO: Dots
+ for (int i = childCount - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (!(child instanceof StatusBarIconView)) {
+ continue;
+ }
+
+ ViewState childState = getViewStateFromChild(child);
+ if (childState == null ) {
+ continue;
+ }
+
+ // Rely on StatusBarIcon for truth about visibility
+ if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
+ childState.hidden = true;
+ continue;
+ }
+
+ childState.xTranslation = translationX - child.getWidth();
+
+ if (childState.xTranslation < contentStart) {
+ if (firstUnderflowIndex == -1) {
+ firstUnderflowIndex = i;
+ }
+ }
+
+ translationX -= child.getWidth();
+ }
+
+ if (firstUnderflowIndex != -1) {
+ for (int i = 0; i <= firstUnderflowIndex; i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs != null) {
+ vs.hidden = true;
+ }
+ }
+ }
+ }
+
+ private void applyIconStates() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs != null) {
+ vs.applyToView(child);
+ }
+ }
+ }
+
+ private void resetViewStates() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs == null) {
+ continue;
+ }
+
+ vs.initFrom(child);
+ vs.alpha = 1.0f;
+ if (child instanceof StatusBarIconView) {
+ vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
+ } else {
+ vs.hidden = false;
+ }
+ }
+ }
+
+ private static @Nullable ViewState getViewStateFromChild(View child) {
+ return (ViewState) child.getTag(R.id.status_bar_view_state_tag);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 3b15c2b8253f..fcf084b29327 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -276,6 +276,9 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
ActuallyCachedState state = mCachedState.get(device);
if (state == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 55050995d5f6..4ca33cd3f601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -394,7 +394,7 @@ public class NotificationChildrenContainer extends ViewGroup {
}
} else if (mOverflowNumber != null) {
removeView(mOverflowNumber);
- if (isShown()) {
+ if (isShown() && isAttachedToWindow()) {
final View removedOverflowNumber = mOverflowNumber;
addTransientView(removedOverflowNumber, getTransientViewCount());
CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8e584bc362eb..5a4478f072e0 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -58,7 +58,7 @@ public class TunerServiceImpl extends TunerService {
private static final String TUNER_VERSION = "sysui_tuner_version";
- private static final int CURRENT_TUNER_VERSION = 1;
+ private static final int CURRENT_TUNER_VERSION = 2;
private final Observer mObserver = new Observer();
// Map of Uris we listen on to their settings keys.
@@ -116,6 +116,9 @@ public class TunerServiceImpl extends TunerService {
TextUtils.join(",", iconBlacklist), mCurrentUser);
}
}
+ if (oldVersion < 2) {
+ setTunerEnabled(mContext, false);
+ }
setValue(TUNER_VERSION, newVersion);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java
new file mode 100644
index 000000000000..3423452c2a27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java
@@ -0,0 +1,51 @@
+/*
+ * 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.volume;
+
+import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
+
+import java.util.List;
+
+/**
+ * Wrapper for final class MediaRouter, for testing.
+ */
+public class MediaRouterWrapper {
+
+ private final MediaRouter mRouter;
+
+ public MediaRouterWrapper(MediaRouter router)
+ {
+ mRouter = router;
+ }
+
+ public void addCallback(MediaRouteSelector selector, MediaRouter.Callback callback, int flags) {
+ mRouter.addCallback(selector, callback, flags);
+ }
+
+ public void removeCallback(MediaRouter.Callback callback) {
+ mRouter.removeCallback(callback);
+ }
+
+ public void unselect(int reason) {
+ mRouter.unselect(reason);
+ }
+
+ public List<MediaRouter.RouteInfo> getRoutes() {
+ return mRouter.getRoutes();
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
index f8843a997d59..e0af9baf0bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ import android.os.SystemClock;
import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
+import android.telecom.TelecomManager;
import android.util.Log;
import android.util.Pair;
@@ -54,7 +55,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -65,15 +65,17 @@ public class OutputChooserDialog extends SystemUIDialog
private static final int MAX_DEVICES = 10;
private static final long UPDATE_DELAY_MS = 300L;
- static final int MSG_UPDATE_ITEMS = 1;
+ private static final int MSG_UPDATE_ITEMS = 1;
private final Context mContext;
- private final BluetoothController mController;
- private final WifiManager mWifiManager;
+ private final BluetoothController mBluetoothController;
+ private WifiManager mWifiManager;
private OutputChooserLayout mView;
- private final MediaRouter mRouter;
+ private final MediaRouterWrapper mRouter;
private final MediaRouterCallback mRouterCallback;
private long mLastUpdateTime;
+ private boolean mIsInCall;
+ protected boolean isAttached;
private final MediaRouteSelector mRouteSelector;
private Drawable mDefaultIcon;
@@ -81,12 +83,14 @@ public class OutputChooserDialog extends SystemUIDialog
private Drawable mSpeakerIcon;
private Drawable mSpeakerGroupIcon;
- public OutputChooserDialog(Context context) {
+ public OutputChooserDialog(Context context, MediaRouterWrapper router) {
super(context);
mContext = context;
- mController = Dependency.get(BluetoothController.class);
+ mBluetoothController = Dependency.get(BluetoothController.class);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mRouter = MediaRouter.getInstance(context);
+ TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ mIsInCall = tm.isInCall();
+ mRouter = router;
mRouterCallback = new MediaRouterCallback();
mRouteSelector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
@@ -96,27 +100,38 @@ public class OutputChooserDialog extends SystemUIDialog
context.registerReceiver(mReceiver, filter);
}
+ protected void setIsInCall(boolean inCall) {
+ mIsInCall = inCall;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.output_chooser);
setCanceledOnTouchOutside(true);
setOnDismissListener(this::onDismiss);
- setTitle(R.string.output_title);
mView = findViewById(R.id.output_chooser);
mView.setCallback(this);
+ if (mIsInCall) {
+ mView.setTitle(R.string.output_calls_title);
+ } else {
+ mView.setTitle(R.string.output_title);
+ }
+
mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast);
mTvIcon = mContext.getDrawable(R.drawable.ic_tv);
mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker);
mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group);
final boolean wifiOff = !mWifiManager.isWifiEnabled();
- final boolean btOff = !mController.isBluetoothEnabled();
- if (wifiOff || btOff) {
+ final boolean btOff = !mBluetoothController.isBluetoothEnabled();
+ if (wifiOff && btOff) {
mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff));
}
+ // time out after 5 seconds
+ mView.postDelayed(() -> updateItems(true), 5000);
}
protected void cleanUp() {}
@@ -131,15 +146,19 @@ public class OutputChooserDialog extends SystemUIDialog
public void onAttachedToWindow() {
super.onAttachedToWindow();
- mRouter.addCallback(mRouteSelector, mRouterCallback,
- MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
- mController.addCallback(mCallback);
+ if (!mIsInCall) {
+ mRouter.addCallback(mRouteSelector, mRouterCallback,
+ MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+ }
+ mBluetoothController.addCallback(mCallback);
+ isAttached = true;
}
@Override
public void onDetachedFromWindow() {
+ isAttached = false;
mRouter.removeCallback(mRouterCallback);
- mController.removeCallback(mCallback);
+ mBluetoothController.removeCallback(mCallback);
super.onDetachedFromWindow();
}
@@ -154,9 +173,8 @@ public class OutputChooserDialog extends SystemUIDialog
if (item == null || item.tag == null) return;
if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
- if (device != null && device.getMaxConnectionState()
- == BluetoothProfile.STATE_DISCONNECTED) {
- mController.connect(device);
+ if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) {
+ mBluetoothController.connect(device);
}
} else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag;
@@ -171,18 +189,16 @@ public class OutputChooserDialog extends SystemUIDialog
if (item == null || item.tag == null) return;
if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
- if (device != null) {
- mController.disconnect(device);
- }
+ mBluetoothController.disconnect(device);
} else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
mRouter.unselect(UNSELECT_REASON_DISCONNECTED);
}
}
- private void updateItems() {
+ private void updateItems(boolean timeout) {
if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) {
mHandler.removeMessages(MSG_UPDATE_ITEMS);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS),
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS, timeout),
mLastUpdateTime + UPDATE_DELAY_MS);
return;
}
@@ -194,14 +210,16 @@ public class OutputChooserDialog extends SystemUIDialog
addBluetoothDevices(items);
// Add remote displays
- addRemoteDisplayRoutes(items);
+ if (!mIsInCall) {
+ addRemoteDisplayRoutes(items);
+ }
- Collections.sort(items, ItemComparator.sInstance);
+ items.sort(ItemComparator.sInstance);
- if (items.size() == 0) {
+ if (items.size() == 0 && timeout) {
String emptyMessage = mContext.getString(R.string.output_none_found);
final boolean wifiOff = !mWifiManager.isWifiEnabled();
- final boolean btOff = !mController.isBluetoothEnabled();
+ final boolean btOff = !mBluetoothController.isBluetoothEnabled();
if (wifiOff || btOff) {
emptyMessage = getDisabledServicesMessage(wifiOff, btOff);
}
@@ -219,12 +237,12 @@ public class OutputChooserDialog extends SystemUIDialog
}
private void addBluetoothDevices(List<OutputChooserLayout.Item> items) {
- final Collection<CachedBluetoothDevice> devices = mController.getDevices();
+ final Collection<CachedBluetoothDevice> devices = mBluetoothController.getDevices();
if (devices != null) {
int connectedDevices = 0;
int count = 0;
for (CachedBluetoothDevice device : devices) {
- if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
+ if (mBluetoothController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
final int majorClass = device.getBtClass().getMajorDeviceClass();
if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO
&& majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) {
@@ -328,22 +346,22 @@ public class OutputChooserDialog extends SystemUIDialog
private final class MediaRouterCallback extends MediaRouter.Callback {
@Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
- updateItems();
+ updateItems(false);
}
@Override
public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
- updateItems();
+ updateItems(false);
}
@Override
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
- updateItems();
+ updateItems(false);
}
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
- dismiss();
+ updateItems(false);
}
}
@@ -361,12 +379,12 @@ public class OutputChooserDialog extends SystemUIDialog
private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
@Override
public void onBluetoothStateChange(boolean enabled) {
- updateItems();
+ updateItems(false);
}
@Override
public void onBluetoothDevicesChanged() {
- updateItems();
+ updateItems(false);
}
};
@@ -393,7 +411,7 @@ public class OutputChooserDialog extends SystemUIDialog
public void handleMessage(Message message) {
switch (message.what) {
case MSG_UPDATE_ITEMS:
- updateItems();
+ updateItems((Boolean) message.obj);
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
index 22ced60006b0..d4c6f897846e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
@@ -29,8 +29,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.FontSizeUtils;
@@ -40,11 +40,10 @@ import com.android.systemui.qs.AutoSizingList;
/**
* Limited height list of devices.
*/
-public class OutputChooserLayout extends FrameLayout {
+public class OutputChooserLayout extends LinearLayout {
private static final String TAG = "OutputChooserLayout";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final int mQsDetailIconOverlaySize;
private final Context mContext;
private final H mHandler = new H();
private final Adapter mAdapter = new Adapter();
@@ -55,6 +54,7 @@ public class OutputChooserLayout extends FrameLayout {
private AutoSizingList mItemList;
private View mEmpty;
private TextView mEmptyText;
+ private TextView mTitle;
private Item[] mItems;
@@ -62,8 +62,6 @@ public class OutputChooserLayout extends FrameLayout {
super(context, attrs);
mContext = context;
mTag = TAG;
- mQsDetailIconOverlaySize = (int) getResources().getDimension(
- R.dimen.qs_detail_icon_overlay_size);
}
@Override
@@ -74,7 +72,8 @@ public class OutputChooserLayout extends FrameLayout {
mItemList.setAdapter(mAdapter);
mEmpty = findViewById(android.R.id.empty);
mEmpty.setVisibility(GONE);
- mEmptyText = mEmpty.findViewById(android.R.id.title);
+ mEmptyText = mEmpty.findViewById(R.id.empty_text);
+ mTitle = findViewById(R.id.title);
}
@Override
@@ -84,17 +83,21 @@ public class OutputChooserLayout extends FrameLayout {
int count = mItemList.getChildCount();
for (int i = 0; i < count; i++) {
View item = mItemList.getChildAt(i);
- FontSizeUtils.updateFontSize(item, android.R.id.title,
+ FontSizeUtils.updateFontSize(item, R.id.empty_text,
R.dimen.qs_detail_item_primary_text_size);
FontSizeUtils.updateFontSize(item, android.R.id.summary,
R.dimen.qs_detail_item_secondary_text_size);
+ FontSizeUtils.updateFontSize(item, android.R.id.title,
+ R.dimen.qs_detail_header_text_size);
}
}
+ public void setTitle(int title) {
+ mTitle.setText(title);
+ }
+
public void setEmptyState(String text) {
- mEmpty.post(() -> {
- mEmptyText.setText(text);
- });
+ mEmptyText.setText(text);
}
@Override
@@ -176,11 +179,6 @@ public class OutputChooserLayout extends FrameLayout {
} else {
iv.setImageResource(item.iconResId);
}
- iv.getOverlay().clear();
- if (item.overlay != null) {
- item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
- iv.getOverlay().add(item.overlay);
- }
final TextView title = view.findViewById(android.R.id.title);
title.setText(item.line1);
final TextView summary = view.findViewById(android.R.id.summary);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 9f7c5a7fde22..e76bf572b1a1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -47,6 +47,7 @@ import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.support.v7.media.MediaRouter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -858,7 +859,8 @@ public class VolumeDialogImpl implements VolumeDialog {
if (mOutputChooserDialog != null) {
return;
}
- mOutputChooserDialog = new OutputChooserDialog(mContext) {
+ mOutputChooserDialog = new OutputChooserDialog(mContext,
+ new MediaRouterWrapper(MediaRouter.getInstance(mContext))) {
@Override
protected void cleanUp() {
synchronized (mOutputChooserLock) {
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e74736ab2fcc..859dc2f18dc6 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -46,6 +46,7 @@
<uses-permission android:name="android.permission.STATUS_BAR" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index fbcbd2096db7..4c7c1d1608fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -83,7 +83,7 @@ public abstract class SysuiTestCase {
return null;
}
- public Context getContext() {
+ public SysuiTestableContext getContext() {
return mContext;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
new file mode 100644
index 000000000000..8f38c2ca948d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationInflater;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+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.MockitoRule;
+import org.mockito.junit.MockitoJUnit;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationGutsManagerTest extends SysuiTestCase {
+ private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
+
+ private final String mPackageName = mContext.getPackageName();
+ private final int mUid = Binder.getCallingUid();
+
+ private NotificationChannel mTestNotificationChannel = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
+ private NotificationTestHelper mHelper;
+ private NotificationGutsManager mGutsManager;
+
+ @Rule public MockitoRule mockito = MockitoJUnit.rule();
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotificationStackScrollLayout mStackScroller;
+ @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
+ @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener;
+
+ @Before
+ public void setUp() {
+ mTestableLooper = TestableLooper.get(this);
+ mHandler = new Handler(mTestableLooper.getLooper());
+
+ mHelper = new NotificationTestHelper(mContext);
+
+ mGutsManager = new NotificationGutsManager(mContext);
+ mGutsManager.setUpWithPresenter(mPresenter, mEntryManager, mStackScroller,
+ mCheckSaveListener, mOnSettingsClickListener);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Test methods:
+
+ @Test
+ public void testOpenAndCloseGuts() {
+ NotificationGuts guts = spy(new NotificationGuts(mContext));
+ when(guts.post(any())).thenAnswer(invocation -> {
+ mHandler.post(((Runnable) invocation.getArguments()[0]));
+ return null;
+ });
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ ExpandableNotificationRow realRow = createTestNotificationRow();
+ NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+ ExpandableNotificationRow row = spy(realRow);
+ when(row.getWindowToken()).thenReturn(new Binder());
+ when(row.getGuts()).thenReturn(guts);
+
+ mGutsManager.openGuts(row, 0, 0, menuItem);
+ assertEquals(View.INVISIBLE, guts.getVisibility());
+ mTestableLooper.processAllMessages();
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ assertEquals(View.VISIBLE, guts.getVisibility());
+ mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+ verify(row, times(1)).setGutsView(any());
+ }
+
+ @Test
+ public void testChangeDensityOrFontScale() {
+ NotificationGuts guts = spy(new NotificationGuts(mContext));
+ when(guts.post(any())).thenAnswer(invocation -> {
+ mHandler.post(((Runnable) invocation.getArguments()[0]));
+ return null;
+ });
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ ExpandableNotificationRow realRow = createTestNotificationRow();
+ NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+ ExpandableNotificationRow row = spy(realRow);
+ when(row.getWindowToken()).thenReturn(new Binder());
+ when(row.getGuts()).thenReturn(guts);
+ doNothing().when(row).inflateGuts();
+
+ mGutsManager.openGuts(row, 0, 0, menuItem);
+ mTestableLooper.processAllMessages();
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+ row.onDensityOrFontScaleChanged();
+ mGutsManager.onDensityOrFontScaleChanged(row);
+ mTestableLooper.processAllMessages();
+
+ mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+ verify(row, times(2)).setGutsView(any());
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Utility methods:
+
+ private ExpandableNotificationRow createTestNotificationRow() {
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setColorized(true)
+ .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ try {
+ ExpandableNotificationRow row = mHelper.createRow(nb.build());
+ row.getEntry().channel = mTestNotificationChannel;
+ return row;
+ } catch (Exception e) {
+ fail();
+ return null;
+ }
+ }
+
+ private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
+ NotificationMenuRowPlugin menuRow = new NotificationMenuRow(mContext);
+ menuRow.createMenu(row, row.getStatusBarNotification());
+
+ NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
+ assertNotNull(menuItem);
+ return menuItem;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 66524cc2b193..2edcd01ed445 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -74,6 +74,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
}
@Test
@@ -87,11 +89,15 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
fragment.disable(0, 0, false);
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
new file mode 100644
index 000000000000..537d606365c4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.volume;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothProfile;
+import android.net.wifi.WifiManager;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.media.MediaRouter;
+import android.telecom.TelecomManager;
+import android.widget.TextView;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.BluetoothController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class OutputChooserDialogTest extends SysuiTestCase {
+
+ OutputChooserDialog mDialog;
+
+ @Mock
+ private BluetoothController mController;
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private TelecomManager mTelecomManager;
+
+ @Mock
+ private MediaRouterWrapper mRouter;
+
+
+ @Before
+ @UiThreadTest
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mController = mDependency.injectMockDependency(BluetoothController.class);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+ getContext().addMockSystemService(WifiManager.class, mWifiManager);
+ getContext().addMockSystemService(TelecomManager.class, mTelecomManager);
+
+ mDialog = new OutputChooserDialog(getContext(), mRouter);
+ }
+
+ @After
+ @UiThreadTest
+ public void tearDown() throws Exception {
+ mDialog.dismiss();
+ }
+
+ private void showDialog() {
+ mDialog.show();
+ }
+
+ @Test
+ @UiThreadTest
+ public void testClickMediaRouterItemConnectsMedia() {
+ showDialog();
+
+ OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+ item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER;
+ MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
+ when(info.isEnabled()).thenReturn(true);
+ item.tag = info;
+
+ mDialog.onDetailItemClick(item);
+ verify(info, times(1)).select();
+ verify(mController, never()).connect(any());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testClickBtItemConnectsBt() {
+ showDialog();
+
+ OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+ item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT;
+ CachedBluetoothDevice btDevice = mock(CachedBluetoothDevice.class);
+ when(btDevice.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED);
+ item.tag = btDevice;
+
+ mDialog.onDetailItemClick(item);
+ verify(mController, times(1)).connect(any());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testTitleNotInCall() {
+ showDialog();
+
+ assertTrue(((TextView) mDialog.findViewById(R.id.title))
+ .getText().toString().contains("Media"));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testTitleInCall() {
+ mDialog.setIsInCall(true);
+ showDialog();
+
+ assertTrue(((TextView) mDialog.findViewById(R.id.title))
+ .getText().toString().contains("Phone"));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testNoMediaScanIfInCall() {
+ mDialog.setIsInCall(true);
+ mDialog.onAttachedToWindow();
+
+ verify(mRouter, never()).addCallback(any(), any(), anyInt());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testMediaScanIfNotInCall() {
+ mDialog.setIsInCall(false);
+ mDialog.onAttachedToWindow();
+
+ verify(mRouter, times(1)).addCallback(any(), any(), anyInt());
+ }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f99ac5c37a66..9b67f8f32704 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5144,6 +5144,11 @@ message MetricsEvent {
// OS: P
APPLICATIONS_DIRECTORY_ACCESS_DETAIL = 1284;
+ // OPEN: Settings > Battery > Smart Battery > Restricted apps
+ // CATEGORY: SETTINGS
+ // OS: P
+ FUELGAUGE_RESTRICTED_APP_DETAILS = 1285;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
index 3b8d4bca0598..672518cc17ed 100644
--- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
@@ -21,6 +21,8 @@ import android.app.StatusBarManager;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -30,20 +32,34 @@ import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import java.util.function.Supplier;
+
/**
* Handle the back-end of AccessibilityService#performGlobalAction
*/
public class GlobalActionPerformer {
private final WindowManagerInternal mWindowManagerService;
private final Context mContext;
+ private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
mContext = context;
mWindowManagerService = windowManagerInternal;
+ mScreenshotHelperSupplier = null;
+ }
+
+ // Used to mock ScreenshotHelper
+ @VisibleForTesting
+ public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+ Supplier<ScreenshotHelper> screenshotHelperSupplier) {
+ this(context, windowManagerInternal);
+ mScreenshotHelperSupplier = screenshotHelperSupplier;
}
public boolean performGlobalAction(int action) {
@@ -79,6 +95,9 @@ public class GlobalActionPerformer {
case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
return lockScreen();
}
+ case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: {
+ return takeScreenshot();
+ }
}
return false;
} finally {
@@ -167,4 +186,12 @@ public class GlobalActionPerformer {
mWindowManagerService.lockNow();
return true;
}
+
+ private boolean takeScreenshot() {
+ ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null)
+ ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext);
+ screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ true, true, new Handler(Looper.getMainLooper()));
+ return true;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6d845f9a9d3a..978ed25378e1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -52,7 +52,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.service.autofill.AutofillFieldClassificationService.Scores;
import android.service.autofill.FillEventHistory;
import android.service.autofill.UserData;
import android.util.LocalLog;
@@ -646,37 +645,35 @@ public final class AutofillManagerService extends SystemService {
}
@Override
- public void getDefaultFieldClassificationAlgorithm(RemoteCallback callback)
- throws RemoteException {
+ public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- service.getDefaultFieldClassificationAlgorithm(getCallingUid(), callback);
+ return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
}
- callback.sendResult(null);
- }
+ return null;
+ }
}
}
@Override
- public void getAvailableFieldClassificationAlgorithms(RemoteCallback callback)
- throws RemoteException {
+ public String[] getAvailableFieldClassificationAlgorithms() throws RemoteException {
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- service.getAvailableFieldClassificationAlgorithms(getCallingUid(), callback);
+ return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
}
- callback.sendResult(null);
+ return null;
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a5bd59a9e77d..6bcfc4bf5a5e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1153,22 +1153,22 @@ final class AutofillManagerServiceImpl {
return mFieldClassificationStrategy;
}
- void getAvailableFieldClassificationAlgorithms(int callingUid, RemoteCallback callback) {
+ String[] getAvailableFieldClassificationAlgorithms(int callingUid) {
synchronized (mLock) {
if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
- return;
+ return null;
}
}
- mFieldClassificationStrategy.getAvailableAlgorithms(callback);
+ return mFieldClassificationStrategy.getAvailableAlgorithms();
}
- void getDefaultFieldClassificationAlgorithm(int callingUid, RemoteCallback callback) {
+ String getDefaultFieldClassificationAlgorithm(int callingUid) {
synchronized (mLock) {
if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
- return;
+ return null;
}
}
- mFieldClassificationStrategy.getDefaultAlgorithm(callback);
+ return mFieldClassificationStrategy.getDefaultAlgorithm();
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 44560879f028..4d69ef952f72 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -191,10 +191,7 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand {
if (scores == null) {
pw.println("no score");
} else {
- pw.print("algorithm: ");
- pw.print(scores.getAlgorithm());
- pw.print(" score: ");
- pw.println(scores.getScores()[0][0]);
+ pw.println(scores.scores[0][0]);
}
latch.countDown();
}));
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index 7228f1d2a8ea..da5220104e3c 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -15,12 +15,12 @@
*/
package com.android.server.autofill;
-import static android.view.autofill.AutofillManager.EXTRA_AVAILABLE_ALGORITHMS;
-import static android.view.autofill.AutofillManager.EXTRA_DEFAULT_ALGORITHM;
import static android.view.autofill.AutofillManager.FC_SERVICE_TIMEOUT;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
+import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM;
import android.Manifest;
import android.annotation.MainThread;
@@ -33,6 +33,7 @@ import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -49,6 +50,7 @@ import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -80,7 +82,8 @@ final class FieldClassificationStrategy {
mUserId = userId;
}
- private ComponentName getServiceComponentName() {
+ @Nullable
+ private ServiceInfo getServiceInfo() {
final String packageName =
mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
if (packageName == null) {
@@ -96,9 +99,15 @@ final class FieldClassificationStrategy {
Slog.w(TAG, "No valid components found.");
return null;
}
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ return resolveInfo.serviceInfo;
+ }
+
+ @Nullable
+ private ComponentName getServiceComponentName() {
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return null;
+ final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
if (!Manifest.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " does not require permission "
@@ -204,12 +213,40 @@ final class FieldClassificationStrategy {
}
}
- void getAvailableAlgorithms(RemoteCallback callback) {
- connectAndRun((service) -> service.getAvailableAlgorithms(callback));
+ /**
+ * Gets the name of all available algorithms.
+ */
+ @Nullable
+ String[] getAvailableAlgorithms() {
+ return getMetadataValue(SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS,
+ (res, id) -> res.getStringArray(id));
}
- void getDefaultAlgorithm(RemoteCallback callback) {
- connectAndRun((service) -> service.getDefaultAlgorithm(callback));
+ /**
+ * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
+ */
+ @Nullable
+ String getDefaultAlgorithm() {
+ return getMetadataValue(SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM, (res, id) -> res.getString(id));
+ }
+
+ @Nullable
+ private <T> T getMetadataValue(String field, MetadataParser<T> parser) {
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return null;
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ final Resources res;
+ try {
+ res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Error getting application resources for " + serviceInfo, e);
+ return null;
+ }
+
+ final int resourceId = serviceInfo.metaData.getInt(field);
+ return parser.get(res, resourceId);
}
//TODO(b/70291841): rename this method (and all others in the chain) to something like
@@ -237,43 +274,16 @@ final class FieldClassificationStrategy {
}
pw.println(impl.flattenToShortString());
- final CountDownLatch latch = new CountDownLatch(2);
-
- // Lock used to make sure lines don't overlap
- final Object lock = latch;
-
- connectAndRun((service) -> service.getAvailableAlgorithms(new RemoteCallback((bundle) -> {
- synchronized (lock) {
- pw.print(prefix); pw.print("Available algorithms: ");
- pw.println(bundle.getStringArrayList(EXTRA_AVAILABLE_ALGORITHMS));
- }
- latch.countDown();
- })));
-
- connectAndRun((service) -> service.getDefaultAlgorithm(new RemoteCallback((bundle) -> {
- synchronized (lock) {
- pw.print(prefix); pw.print("Default algorithm: ");
- pw.println(bundle.getString(EXTRA_DEFAULT_ALGORITHM));
- }
- latch.countDown();
- })));
-
- try {
- if (!latch.await(FC_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS)) {
- synchronized (lock) {
- pw.print(prefix); pw.print("timeout ("); pw.print(FC_SERVICE_TIMEOUT);
- pw.println("ms) waiting for service");
- }
- }
- } catch (InterruptedException e) {
- synchronized (lock) {
- pw.print(prefix); pw.println("interrupted while waiting for service");
- }
- Thread.currentThread().interrupt();
- }
+ pw.print(prefix); pw.print("Available algorithms: ");
+ pw.println(Arrays.toString(getAvailableAlgorithms()));
+ pw.print(prefix); pw.print("Default algorithm: "); pw.println(getDefaultAlgorithm());
}
- private interface Command {
+ private static interface Command {
void run(IAutofillFieldClassificationService service) throws RemoteException;
}
+
+ private static interface MetadataParser<T> {
+ T get(Resources res, int resId);
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a0e23a152224..63f83849db34 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1172,8 +1172,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.w(TAG, "No field classification score on " + result);
return;
}
- final float[][] scoresMatrix = scores.getScores();
-
int i = 0, j = 0;
try {
for (i = 0; i < viewsSize; i++) {
@@ -1182,8 +1180,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ArrayList<Match> matches = null;
for (j = 0; j < userValues.length; j++) {
String remoteId = remoteIds[j];
- final String actualAlgorithm = scores.getAlgorithm();
- final float score = scoresMatrix[i][j];
+ final float score = scores.scores[i][j];
if (score > 0) {
if (sVerbose) {
Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
@@ -1192,7 +1189,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (matches == null) {
matches = new ArrayList<>(userValues.length);
}
- matches.add(new Match(remoteId, score, actualAlgorithm));
+ matches.add(new Match(remoteId, score));
}
else if (sVerbose) {
Slog.v(TAG, "skipping score 0 at index " + j + " and id " + fieldId);
@@ -1205,7 +1202,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
} catch (ArrayIndexOutOfBoundsException e) {
Slog.wtf(TAG, "Error accessing FC score at " + i + " x " + j + ": "
- + Arrays.toString(scoresMatrix), e);
+ + Arrays.toString(scores.scores), e);
return;
}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 518891006b37..465bb09927a5 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -3464,16 +3464,21 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
- String callerLogString = "BMS.isAppEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- boolean eligible =
- AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager);
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.isAppEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ boolean eligible =
+ AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager);
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligible;
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
}
- return eligible;
}
@Override
@@ -3481,21 +3486,26 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
- String callerLogString = "BMS.filterAppsEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- List<String> eligibleApps = new LinkedList<>();
- for (String packageName : packages) {
- if (AppBackupUtils
- .appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager)) {
- eligibleApps.add(packageName);
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.filterAppsEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ List<String> eligibleApps = new LinkedList<>();
+ for (String packageName : packages) {
+ if (AppBackupUtils
+ .appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager)) {
+ eligibleApps.add(packageName);
+ }
}
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligibleApps.toArray(new String[eligibleApps.size()]);
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
}
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- }
- return eligibleApps.toArray(new String[eligibleApps.size()]);
}
@Override
@@ -3514,6 +3524,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
} else if ("agents".startsWith(arg)) {
dumpAgents(pw);
return;
+ } else if ("transportclients".equals(arg.toLowerCase())) {
+ mTransportManager.dump(pw);
+ return;
}
}
}
@@ -3576,6 +3589,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
}
+ mTransportManager.dump(pw);
+
pw.println("Pending init: " + mPendingInits.size());
for (String s : mPendingInits) {
pw.println(" " + s);
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 6d9231dcddfa..7e179e5d24f8 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -43,6 +43,7 @@ import com.android.server.backup.transport.TransportConnectionListener;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
+import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -633,6 +634,10 @@ public class TransportManager {
!Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held");
}
+ public void dump(PrintWriter pw) {
+ mTransportClientManager.dump(pw);
+ }
+
private static Predicate<ComponentName> fromPackageFilter(String packageName) {
return transportComponent -> packageName.equals(transportComponent.getPackageName());
}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index cc3af8c8c933..289dd140ee7f 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -114,14 +114,14 @@ public class PerformBackupTask implements BackupRestoreTask {
private RefactoredBackupManagerService backupManagerService;
private final Object mCancelLock = new Object();
- ArrayList<BackupRequest> mQueue;
- ArrayList<BackupRequest> mOriginalQueue;
- File mStateDir;
- @Nullable DataChangedJournal mJournal;
- BackupState mCurrentState;
- List<String> mPendingFullBackups;
- IBackupObserver mObserver;
- IBackupManagerMonitor mMonitor;
+ private ArrayList<BackupRequest> mQueue;
+ private ArrayList<BackupRequest> mOriginalQueue;
+ private File mStateDir;
+ @Nullable private DataChangedJournal mJournal;
+ private BackupState mCurrentState;
+ private List<String> mPendingFullBackups;
+ private IBackupObserver mObserver;
+ private IBackupManagerMonitor mMonitor;
private final TransportClient mTransportClient;
private final OnTaskFinishedListener mListener;
@@ -130,18 +130,18 @@ public class PerformBackupTask implements BackupRestoreTask {
private volatile int mEphemeralOpToken;
// carried information about the current in-flight operation
- IBackupAgent mAgentBinder;
- PackageInfo mCurrentPackage;
- File mSavedStateName;
- File mBackupDataName;
- File mNewStateName;
- ParcelFileDescriptor mSavedState;
- ParcelFileDescriptor mBackupData;
- ParcelFileDescriptor mNewState;
- int mStatus;
- boolean mFinished;
- final boolean mUserInitiated;
- final boolean mNonIncremental;
+ private IBackupAgent mAgentBinder;
+ private PackageInfo mCurrentPackage;
+ private File mSavedStateName;
+ private File mBackupDataName;
+ private File mNewStateName;
+ private ParcelFileDescriptor mSavedState;
+ private ParcelFileDescriptor mBackupData;
+ private ParcelFileDescriptor mNewState;
+ private int mStatus;
+ private boolean mFinished;
+ private final boolean mUserInitiated;
+ private final boolean mNonIncremental;
private volatile boolean mCancelAll;
@@ -241,7 +241,7 @@ public class PerformBackupTask implements BackupRestoreTask {
// We're starting a backup pass. Initialize the transport and send
// the PM metadata blob if we haven't already.
- void beginBackup() {
+ private void beginBackup() {
if (DEBUG_BACKUP_TRACE) {
backupManagerService.clearBackupTrace();
StringBuilder b = new StringBuilder(256);
@@ -369,7 +369,7 @@ public class PerformBackupTask implements BackupRestoreTask {
// Transport has been initialized and the PM metadata submitted successfully
// if that was warranted. Now we process the single next thing in the queue.
- void invokeNextAgent() {
+ private void invokeNextAgent() {
mStatus = BackupTransport.TRANSPORT_OK;
backupManagerService.addBackupTrace("invoke q=" + mQueue.size());
@@ -511,7 +511,7 @@ public class PerformBackupTask implements BackupRestoreTask {
}
}
- void finalizeBackup() {
+ private void finalizeBackup() {
backupManagerService.addBackupTrace("finishing");
// Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
@@ -617,14 +617,14 @@ public class PerformBackupTask implements BackupRestoreTask {
}
// Remove the PM metadata state. This will generate an init on the next pass.
- void clearMetadata() {
+ private void clearMetadata() {
final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
if (pmState.exists()) pmState.delete();
}
// Invoke an agent's doBackup() and start a timeout message spinning on the main
// handler in case it doesn't get back to us.
- int invokeAgentForBackup(String packageName, IBackupAgent agent) {
+ private int invokeAgentForBackup(String packageName, IBackupAgent agent) {
if (DEBUG) {
Slog.d(TAG, "invokeAgentForBackup on " + packageName);
}
@@ -711,7 +711,7 @@ public class PerformBackupTask implements BackupRestoreTask {
return BackupTransport.TRANSPORT_OK;
}
- public void failAgent(IBackupAgent agent, String message) {
+ private void failAgent(IBackupAgent agent, String message) {
try {
agent.fail(message);
} catch (Exception e) {
@@ -1059,7 +1059,7 @@ public class PerformBackupTask implements BackupRestoreTask {
}
}
- void revertAndEndBackup() {
+ private void revertAndEndBackup() {
if (MORE_DEBUG) {
Slog.i(TAG, "Reverting backup queue - restaging everything");
}
@@ -1085,14 +1085,14 @@ public class PerformBackupTask implements BackupRestoreTask {
}
- void errorCleanup() {
+ private void errorCleanup() {
mBackupDataName.delete();
mNewStateName.delete();
clearAgentState();
}
// Cleanup common to both success and failure cases
- void clearAgentState() {
+ private void clearAgentState() {
try {
if (mSavedState != null) mSavedState.close();
} catch (IOException e) {
@@ -1123,7 +1123,7 @@ public class PerformBackupTask implements BackupRestoreTask {
}
}
- void executeNextState(BackupState nextState) {
+ private void executeNextState(BackupState nextState) {
if (MORE_DEBUG) {
Slog.i(TAG, " => executing next step on "
+ this + " nextState=" + nextState);
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index 399f338d26b2..7b2e3df67942 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -16,6 +16,8 @@
package com.android.server.backup.transport;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
@@ -28,6 +30,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
+import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
@@ -41,6 +44,9 @@ import com.android.server.backup.TransportManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -65,6 +71,7 @@ import java.util.concurrent.ExecutionException;
*/
public class TransportClient {
private static final String TAG = "TransportClient";
+ private static final int LOG_BUFFER_SIZE = 5;
private final Context mContext;
private final Intent mBindIntent;
@@ -73,6 +80,10 @@ public class TransportClient {
private final Handler mListenerHandler;
private final String mPrefixForLog;
private final Object mStateLock = new Object();
+ private final Object mLogBufferLock = new Object();
+
+ @GuardedBy("mLogBufferLock")
+ private final List<String> mLogBuffer = new LinkedList<>();
@GuardedBy("mStateLock")
private final Map<TransportConnectionListener, String> mListeners = new ArrayMap<>();
@@ -112,7 +123,7 @@ public class TransportClient {
// For logging
String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", "");
- mPrefixForLog = classNameForLog + "#" + mIdentifier + ": ";
+ mPrefixForLog = classNameForLog + "#" + mIdentifier + ":";
}
public ComponentName getTransportComponent() {
@@ -229,7 +240,7 @@ public class TransportClient {
switch (mState) {
case State.UNUSABLE:
- log(Log.DEBUG, caller, "Async connect: UNUSABLE client");
+ log(Log.WARN, caller, "Async connect: UNUSABLE client");
notifyListener(listener, null, caller);
break;
case State.IDLE:
@@ -324,14 +335,14 @@ public class TransportClient {
IBackupTransport transport = mTransport;
if (transport != null) {
- log(Log.DEBUG, caller, "Sync connect: reusing transport");
+ log(Log.INFO, caller, "Sync connect: reusing transport");
return transport;
}
// If it's already UNUSABLE we return straight away, no need to go to main-thread
synchronized (mStateLock) {
if (mState == State.UNUSABLE) {
- log(Log.DEBUG, caller, "Sync connect: UNUSABLE client");
+ log(Log.WARN, caller, "Sync connect: UNUSABLE client");
return null;
}
}
@@ -403,13 +414,16 @@ public class TransportClient {
}
private void notifyListener(
- TransportConnectionListener listener, IBackupTransport transport, String caller) {
- log(Log.VERBOSE, caller, "Notifying listener of transport = " + transport);
+ TransportConnectionListener listener,
+ @Nullable IBackupTransport transport,
+ String caller) {
+ String transportString = (transport != null) ? "IBackupTransport" : "null";
+ log(Log.INFO, "Notifying [" + caller + "] transport = " + transportString);
mListenerHandler.post(() -> listener.onTransportConnectionResult(transport, this));
}
@GuardedBy("mStateLock")
- private void notifyListenersAndClearLocked(IBackupTransport transport) {
+ private void notifyListenersAndClearLocked(@Nullable IBackupTransport transport) {
for (Map.Entry<TransportConnectionListener, String> entry : mListeners.entrySet()) {
TransportConnectionListener listener = entry.getKey();
String caller = entry.getValue();
@@ -509,13 +523,30 @@ public class TransportClient {
}
private void log(int priority, String message) {
- TransportUtils.log(priority, TAG, message);
+ TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, null, message));
+ saveLogEntry(formatMessage(null, null, message));
}
- private void log(int priority, String caller, String msg) {
- TransportUtils.log(priority, TAG, mPrefixForLog, caller, msg);
- // TODO(brufino): Log in internal list for dump
- // CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+ private void log(int priority, String caller, String message) {
+ TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, caller, message));
+ saveLogEntry(formatMessage(null, caller, message));
+ }
+
+ private void saveLogEntry(String message) {
+ CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+ message = time + " " + message;
+ synchronized (mLogBufferLock) {
+ if (mLogBuffer.size() == LOG_BUFFER_SIZE) {
+ mLogBuffer.remove(mLogBuffer.size() - 1);
+ }
+ mLogBuffer.add(0, message);
+ }
+ }
+
+ List<String> getLogBuffer() {
+ synchronized (mLogBufferLock) {
+ return Collections.unmodifiableList(mLogBuffer);
+ }
}
@IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
index 1cbe74716b03..1132bce612b7 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
@@ -17,19 +17,20 @@
package com.android.server.backup.transport;
import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
-
import com.android.server.backup.TransportManager;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Manages the creation and disposal of {@link TransportClient}s. The only class that should use
* this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
- *
- * <p>TODO(brufino): Implement pool of TransportClients
*/
public class TransportClientManager {
private static final String TAG = "TransportClientManager";
@@ -37,6 +38,7 @@ public class TransportClientManager {
private final Context mContext;
private final Object mTransportClientsLock = new Object();
private int mTransportClientsCreated = 0;
+ private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
public TransportClientManager(Context context) {
mContext = context;
@@ -62,8 +64,10 @@ public class TransportClientManager {
bindIntent,
transportComponent,
Integer.toString(mTransportClientsCreated));
+ mTransportClientsCallerMap.put(transportClient, caller);
mTransportClientsCreated++;
- TransportUtils.log(Log.DEBUG, TAG, caller, "Retrieving " + transportClient);
+ TransportUtils.log(
+ Log.DEBUG, TAG, formatMessage(null, caller, "Retrieving " + transportClient));
return transportClient;
}
}
@@ -77,7 +81,25 @@ public class TransportClientManager {
* details.
*/
public void disposeOfTransportClient(TransportClient transportClient, String caller) {
- TransportUtils.log(Log.DEBUG, TAG, caller, "Disposing of " + transportClient);
transportClient.unbind(caller);
+ synchronized (mTransportClientsLock) {
+ TransportUtils.log(
+ Log.DEBUG, TAG, formatMessage(null, caller, "Disposing of " + transportClient));
+ mTransportClientsCallerMap.remove(transportClient);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("Transport clients created: " + mTransportClientsCreated);
+ synchronized (mTransportClientsLock) {
+ pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
+ for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) {
+ String caller = mTransportClientsCallerMap.get(transportClient);
+ pw.println(" " + transportClient + " [" + caller + "]");
+ for (String logEntry : transportClient.getLogBuffer()) {
+ pw.println(" " + logEntry);
+ }
+ }
+ }
}
}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
index 92bba9bf06f0..56b2d44ec420 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
@@ -41,21 +41,20 @@ public class TransportUtils {
}
static void log(int priority, String tag, String message) {
- log(priority, tag, null, message);
- }
-
- static void log(int priority, String tag, @Nullable String caller, String message) {
- log(priority, tag, "", caller, message);
+ if (Log.isLoggable(tag, priority)) {
+ Slog.println(priority, tag, message);
+ }
}
- static void log(
- int priority, String tag, String prefix, @Nullable String caller, String message) {
- if (Log.isLoggable(tag, priority)) {
- if (caller != null) {
- prefix += "[" + caller + "] ";
- }
- Slog.println(priority, tag, prefix + message);
+ static String formatMessage(@Nullable String prefix, @Nullable String caller, String message) {
+ StringBuilder string = new StringBuilder();
+ if (prefix != null) {
+ string.append(prefix).append(" ");
+ }
+ if (caller != null) {
+ string.append("[").append(caller).append("] ");
}
+ return string.append(message).toString();
}
private TransportUtils() {}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 4ffa5f1f38f9..f4675fd3316d 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1316,14 +1316,8 @@ public class AppOpsService extends IAppOpsService.Stub {
isPrivileged = (appInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
} else {
- if ("media".equals(packageName)) {
- pkgUid = Process.MEDIA_UID;
- isPrivileged = false;
- } else if ("audioserver".equals(packageName)) {
- pkgUid = Process.AUDIOSERVER_UID;
- isPrivileged = false;
- } else if ("cameraserver".equals(packageName)) {
- pkgUid = Process.CAMERASERVER_UID;
+ pkgUid = resolveUid(packageName);
+ if (pkgUid >= 0) {
isPrivileged = false;
}
}
@@ -1957,9 +1951,8 @@ public class AppOpsService extends IAppOpsService.Stub {
if (nonpackageUid != -1) {
packageName = null;
} else {
- if ("root".equals(packageName)) {
- packageUid = 0;
- } else {
+ packageUid = resolveUid(packageName);
+ if (packageUid < 0) {
packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
}
@@ -2052,6 +2045,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (ops == null || ops.size() <= 0) {
pw.println("No operations.");
+ if (shell.op > AppOpsManager.OP_NONE && shell.op < AppOpsManager._NUM_OP) {
+ pw.println("Default mode: " + AppOpsManager.modeToString(
+ AppOpsManager.opToDefaultMode(shell.op)));
+ }
return 0;
}
final long now = System.currentTimeMillis();
@@ -2061,24 +2058,7 @@ public class AppOpsService extends IAppOpsService.Stub {
AppOpsManager.OpEntry ent = entries.get(j);
pw.print(AppOpsManager.opToName(ent.getOp()));
pw.print(": ");
- switch (ent.getMode()) {
- case AppOpsManager.MODE_ALLOWED:
- pw.print("allow");
- break;
- case AppOpsManager.MODE_IGNORED:
- pw.print("ignore");
- break;
- case AppOpsManager.MODE_ERRORED:
- pw.print("deny");
- break;
- case AppOpsManager.MODE_DEFAULT:
- pw.print("default");
- break;
- default:
- pw.print("mode=");
- pw.print(ent.getMode());
- break;
- }
+ pw.print(AppOpsManager.modeToString(ent.getMode()));
if (ent.getTime() != 0) {
pw.print("; time=");
TimeUtils.formatDuration(now - ent.getTime(), pw);
@@ -2563,16 +2543,41 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private static String resolvePackageName(int uid, String packageName) {
- if (uid == 0) {
+ if (uid == Process.ROOT_UID) {
return "root";
} else if (uid == Process.SHELL_UID) {
return "com.android.shell";
+ } else if (uid == Process.MEDIA_UID) {
+ return "media";
+ } else if (uid == Process.AUDIOSERVER_UID) {
+ return "audioserver";
+ } else if (uid == Process.CAMERASERVER_UID) {
+ return "cameraserver";
} else if (uid == Process.SYSTEM_UID && packageName == null) {
return "android";
}
return packageName;
}
+ private static int resolveUid(String packageName) {
+ if (packageName == null) {
+ return -1;
+ }
+ switch (packageName) {
+ case "root":
+ return Process.ROOT_UID;
+ case "shell":
+ return Process.SHELL_UID;
+ case "media":
+ return Process.MEDIA_UID;
+ case "audioserver":
+ return Process.AUDIOSERVER_UID;
+ case "cameraserver":
+ return Process.CAMERASERVER_UID;
+ }
+ return -1;
+ }
+
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
try {
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index a75a3675f7f9..de113a6a53e0 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -26,6 +26,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -89,6 +91,9 @@ public class ForceAppStandbyTracker {
private final MyHandler mHandler;
+ @VisibleForTesting
+ FeatureFlagsObserver mFlagsObserver;
+
/**
* Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
*/
@@ -113,14 +118,36 @@ public class ForceAppStandbyTracker {
@GuardedBy("mLock")
boolean mStarted;
+ /**
+ * Only used for small battery use-case.
+ */
+ @GuardedBy("mLock")
+ boolean mIsPluggedIn;
+
@GuardedBy("mLock")
- boolean mForceAllAppsStandby; // True if device is in extreme battery saver mode
+ boolean mBatterySaverEnabled;
+ /**
+ * True if the forced app standby is currently enabled
+ */
@GuardedBy("mLock")
- boolean mForcedAppStandbyEnabled; // True if the forced app standby feature is enabled
+ boolean mForceAllAppsStandby;
- private class FeatureFlagObserver extends ContentObserver {
- FeatureFlagObserver() {
+ /**
+ * True if the forced app standby for small battery devices feature is enabled in settings
+ */
+ @GuardedBy("mLock")
+ boolean mForceAllAppStandbyForSmallBattery;
+
+ /**
+ * True if the forced app standby feature is enabled in settings
+ */
+ @GuardedBy("mLock")
+ boolean mForcedAppStandbyEnabled;
+
+ @VisibleForTesting
+ class FeatureFlagsObserver extends ContentObserver {
+ FeatureFlagsObserver() {
super(null);
}
@@ -128,6 +155,9 @@ public class ForceAppStandbyTracker {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
false, this);
+
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
}
boolean isForcedAppStandbyEnabled() {
@@ -135,20 +165,43 @@ public class ForceAppStandbyTracker {
Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
}
+ boolean isForcedAppStandbyForSmallBatteryEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
+ }
+
@Override
- public void onChange(boolean selfChange) {
- final boolean enabled = isForcedAppStandbyEnabled();
- synchronized (mLock) {
- if (mForcedAppStandbyEnabled == enabled) {
- return;
+ public void onChange(boolean selfChange, Uri uri) {
+ if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyEnabled();
+ synchronized (mLock) {
+ if (mForcedAppStandbyEnabled == enabled) {
+ return;
+ }
+ mForcedAppStandbyEnabled = enabled;
+ if (DEBUG) {
+ Slog.d(TAG,"Forced app standby feature flag changed: "
+ + mForcedAppStandbyEnabled);
+ }
}
- mForcedAppStandbyEnabled = enabled;
- if (DEBUG) {
- Slog.d(TAG,
- "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+ mHandler.notifyForcedAppStandbyFeatureFlagChanged();
+ } else if (Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
+ synchronized (mLock) {
+ if (mForceAllAppStandbyForSmallBattery == enabled) {
+ return;
+ }
+ mForceAllAppStandbyForSmallBattery = enabled;
+ if (DEBUG) {
+ Slog.d(TAG, "Forced app standby for small battery feature flag changed: "
+ + mForceAllAppStandbyForSmallBattery);
+ }
+ updateForceAllAppStandbyState();
}
+ } else {
+ Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri);
}
- mHandler.notifyFeatureFlagChanged();
}
}
@@ -289,9 +342,11 @@ public class ForceAppStandbyTracker {
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
- final FeatureFlagObserver flagObserver = new FeatureFlagObserver();
- flagObserver.register();
- mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled();
+ mFlagsObserver = new FeatureFlagsObserver();
+ mFlagsObserver.register();
+ mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
+ mForceAllAppStandbyForSmallBattery =
+ mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
try {
mIActivityManager.registerUidObserver(new UidObserver(),
@@ -306,16 +361,24 @@ public class ForceAppStandbyTracker {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new MyReceiver(), filter);
refreshForcedAppStandbyUidPackagesLocked();
mPowerManagerInternal.registerLowPowerModeObserver(
ServiceType.FORCE_ALL_APPS_STANDBY,
- (state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
+ (state) -> {
+ synchronized (mLock) {
+ mBatterySaverEnabled = state.batterySaverEnabled;
+ updateForceAllAppStandbyState();
+ }
+ });
- updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
- ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
+ mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState(
+ ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled;
+
+ updateForceAllAppStandbyState();
}
}
@@ -340,6 +403,11 @@ public class ForceAppStandbyTracker {
return LocalServices.getService(PowerManagerInternal.class);
}
+ @VisibleForTesting
+ boolean isSmallBatteryDevice() {
+ return ActivityManager.isSmallBatteryDevice();
+ }
+
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
@@ -369,18 +437,26 @@ public class ForceAppStandbyTracker {
}
}
- /**
- * Update {@link #mForceAllAppsStandby} and notifies the listeners.
- */
- void updateForceAllAppsStandby(boolean enable) {
+ private void updateForceAllAppStandbyState() {
synchronized (mLock) {
- if (enable == mForceAllAppsStandby) {
- return;
+ if (mForceAllAppStandbyForSmallBattery && isSmallBatteryDevice()) {
+ toggleForceAllAppsStandbyLocked(!mIsPluggedIn);
+ } else {
+ toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
}
- mForceAllAppsStandby = enable;
+ }
+ }
- mHandler.notifyForceAllAppsStandbyChanged();
+ /**
+ * Update {@link #mForceAllAppsStandby} and notifies the listeners.
+ */
+ private void toggleForceAllAppsStandbyLocked(boolean enable) {
+ if (enable == mForceAllAppsStandby) {
+ return;
}
+ mForceAllAppsStandby = enable;
+
+ mHandler.notifyForceAllAppsStandbyChanged();
}
private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
@@ -515,6 +591,11 @@ public class ForceAppStandbyTracker {
if (userId > 0) {
mHandler.doUserRemoved(userId);
}
+ } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ synchronized (mLock) {
+ mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+ }
+ updateForceAllAppStandbyState();
}
}
}
@@ -533,7 +614,7 @@ public class ForceAppStandbyTracker {
private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
private static final int MSG_FORCE_ALL_CHANGED = 6;
private static final int MSG_USER_REMOVED = 7;
- private static final int MSG_FEATURE_FLAG_CHANGED = 8;
+ private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
public MyHandler(Looper looper) {
super(looper);
@@ -563,8 +644,8 @@ public class ForceAppStandbyTracker {
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
- public void notifyFeatureFlagChanged() {
- obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget();
+ public void notifyForcedAppStandbyFeatureFlagChanged() {
+ obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
}
public void doUserRemoved(int userId) {
@@ -618,7 +699,7 @@ public class ForceAppStandbyTracker {
l.onForceAllAppsStandbyChanged(sender);
}
return;
- case MSG_FEATURE_FLAG_CHANGED:
+ case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
// Feature flag for forced app standby changed.
final boolean unblockAlarms;
synchronized (mLock) {
@@ -737,21 +818,23 @@ public class ForceAppStandbyTracker {
* @return whether alarms should be restricted for a UID package-name.
*/
public boolean areAlarmsRestricted(int uid, @NonNull String packageName) {
- return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false);
+ return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false,
+ /* exemptOnBatterySaver =*/ false);
}
/**
* @return whether jobs should be restricted for a UID package-name.
*/
public boolean areJobsRestricted(int uid, @NonNull String packageName) {
- return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true);
+ return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true,
+ /* exemptOnBatterySaver =*/ false);
}
/**
* @return whether force-app-standby is effective for a UID package-name.
*/
private boolean isRestricted(int uid, @NonNull String packageName,
- boolean useTempWhitelistToo) {
+ boolean useTempWhitelistToo, boolean exemptOnBatterySaver) {
if (isInForeground(uid)) {
return false;
}
@@ -765,12 +848,13 @@ public class ForceAppStandbyTracker {
ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
return false;
}
-
- if (mForceAllAppsStandby) {
+ if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) {
return true;
}
-
- return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName);
+ if (exemptOnBatterySaver) {
+ return false;
+ }
+ return mForceAllAppsStandby;
}
}
@@ -842,6 +926,18 @@ public class ForceAppStandbyTracker {
pw.println(isForceAllAppsStandbyEnabled());
pw.print(indent);
+ pw.print("Small Battery Device: ");
+ pw.println(isSmallBatteryDevice());
+
+ pw.print(indent);
+ pw.print("Force all apps standby for small battery device: ");
+ pw.println(mForceAllAppStandbyForSmallBattery);
+
+ pw.print(indent);
+ pw.print("Plugged In: ");
+ pw.println(mIsPluggedIn);
+
+ pw.print(indent);
pw.print("Foreground uids: [");
String sep = "";
@@ -880,6 +976,11 @@ public class ForceAppStandbyTracker {
final long token = proto.start(fieldId);
proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
+ proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE,
+ isSmallBatteryDevice());
+ proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
+ mForceAllAppStandbyForSmallBattery);
+ proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn);
for (int i = 0; i < mForegroundUids.size(); i++) {
if (mForegroundUids.valueAt(i)) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 46a35ec800ba..ef6bc437cb6c 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -148,7 +148,7 @@ public class IpSecService extends IIpSecService.Stub {
* resources.
*
* <p>References to the IResource object may be held by other RefcountedResource objects,
- * and as such, the kernel resources and quota may not be cleaned up.
+ * and as such, the underlying resources and quota may not be cleaned up.
*/
void invalidate() throws RemoteException;
@@ -298,7 +298,12 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- /* Very simple counting class that looks much like a counting semaphore */
+ /**
+ * Very simple counting class that looks much like a counting semaphore
+ *
+ * <p>This class is not thread-safe, and expects that that users of this class will ensure
+ * synchronization and thread safety by holding the IpSecService.this instance lock.
+ */
@VisibleForTesting
static class ResourceTracker {
private final int mMax;
@@ -341,26 +346,38 @@ public class IpSecService extends IIpSecService.Stub {
@VisibleForTesting
static final class UserRecord {
- /* Type names */
- public static final String TYPENAME_SPI = "SecurityParameterIndex";
- public static final String TYPENAME_TRANSFORM = "IpSecTransform";
- public static final String TYPENAME_ENCAP_SOCKET = "UdpEncapSocket";
-
/* Maximum number of each type of resource that a single UID may possess */
public static final int MAX_NUM_ENCAP_SOCKETS = 2;
public static final int MAX_NUM_TRANSFORMS = 4;
public static final int MAX_NUM_SPIS = 8;
+ /**
+ * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
+ * and explicit (user) reference management.
+ *
+ * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
+ *
+ * <p>Resources are removed from this array when the user releases their explicit reference
+ * by calling one of the releaseResource() methods.
+ */
final RefcountedResourceArray<SpiRecord> mSpiRecords =
- new RefcountedResourceArray<>(TYPENAME_SPI);
- final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
-
+ new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
final RefcountedResourceArray<TransformRecord> mTransformRecords =
- new RefcountedResourceArray<>(TYPENAME_TRANSFORM);
- final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
-
+ new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
- new RefcountedResourceArray<>(TYPENAME_ENCAP_SOCKET);
+ new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
+
+ /**
+ * Trackers for quotas for each of the OwnedResource types.
+ *
+ * <p>These trackers are separate from the resource arrays, since they are incremented and
+ * decremented at different points in time. Specifically, quota is only returned upon final
+ * resource deallocation (after all explicit and implicit references are released). Note
+ * that it is possible that calls to releaseResource() will not return the used quota if
+ * there are other resources that depend on (are parents of) the resource being released.
+ */
+ final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
+ final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
void removeSpiRecord(int resourceId) {
@@ -395,11 +412,15 @@ public class IpSecService extends IIpSecService.Stub {
}
}
+ /**
+ * This class is not thread-safe, and expects that that users of this class will ensure
+ * synchronization and thread safety by holding the IpSecService.this instance lock.
+ */
@VisibleForTesting
static final class UserResourceTracker {
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
- /** Never-fail getter that populates the list of UIDs as-needed */
+ /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
public UserRecord getUserRecord(int uid) {
checkCallerUid(uid);
@@ -428,18 +449,20 @@ public class IpSecService extends IIpSecService.Stub {
@VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
/**
- * The KernelResourceRecord class provides a facility to cleanly and reliably track system
+ * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
* resources. It relies on a provided resourceId that should uniquely identify the kernel
* resource. To use this class, the user should implement the invalidate() and
* freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
- * tracking arrays and kernel resources, respectively
+ * tracking arrays and kernel resources, respectively.
+ *
+ * <p>This class associates kernel resources with the UID that owns and controls them.
*/
- private abstract class KernelResourceRecord implements IResource {
+ private abstract class OwnedResourceRecord implements IResource {
final int pid;
final int uid;
protected final int mResourceId;
- KernelResourceRecord(int resourceId) {
+ OwnedResourceRecord(int resourceId) {
super();
if (resourceId == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
@@ -479,8 +502,6 @@ public class IpSecService extends IIpSecService.Stub {
}
};
- // TODO: Move this to right after RefcountedResource. With this here, Gerrit was showing many
- // more things as changed.
/**
* Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
*
@@ -534,7 +555,12 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- private final class TransformRecord extends KernelResourceRecord {
+ /**
+ * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
+ * created, the SpiRecord that originally tracked the SAs will reliquish the
+ * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
+ */
+ private final class TransformRecord extends OwnedResourceRecord {
private final IpSecConfig mConfig;
private final SpiRecord mSpi;
private final EncapSocketRecord mSocket;
@@ -603,7 +629,12 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- private final class SpiRecord extends KernelResourceRecord {
+ /**
+ * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
+ * responsibility for cleaning up underlying resources will be passed to the TransformRecord
+ * object
+ */
+ private final class SpiRecord extends OwnedResourceRecord {
private final String mSourceAddress;
private final String mDestinationAddress;
private int mSpi;
@@ -692,7 +723,14 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- private final class EncapSocketRecord extends KernelResourceRecord {
+ /**
+ * Tracks a UDP encap socket, and manages cleanup paths
+ *
+ * <p>While this class does not manage non-kernel resources, race conditions around socket
+ * binding require that the service creates the encap socket, binds it and applies the socket
+ * policy before handing it to a user.
+ */
+ private final class EncapSocketRecord extends OwnedResourceRecord {
private FileDescriptor mSocket;
private final int mPort;
@@ -1105,16 +1143,14 @@ public class IpSecService extends IIpSecService.Stub {
* receive data.
*/
@Override
- public synchronized IpSecTransformResponse createTransportModeTransform(
- IpSecConfig c, IBinder binder) throws RemoteException {
+ public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
+ throws RemoteException {
checkIpSecConfig(c);
- checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
+ checkNotNull(binder, "Null Binder passed to createTransform");
final int resourceId = mNextResourceId++;
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
- // Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI)
- List<RefcountedResource> dependencies = new ArrayList<>(2);
+ List<RefcountedResource> dependencies = new ArrayList<>();
if (!userRecord.mTransformQuotaTracker.isAvailable()) {
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
@@ -1186,7 +1222,7 @@ public class IpSecService extends IIpSecService.Stub {
* other reasons.
*/
@Override
- public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException {
+ public synchronized void deleteTransform(int resourceId) throws RemoteException {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
releaseResource(userRecord.mTransformRecords, resourceId);
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 80f8e519beb7..833def3e87b1 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -26,4 +26,7 @@ public interface PersistentDataBlockManagerInternal {
/** Retrieves handle to a lockscreen credential to be used for Factory Reset Protection. */
byte[] getFrpCredentialHandle();
+
+ /** Update the OEM unlock enabled bit, bypassing user restriction checks. */
+ void forceOemUnlockEnabled(boolean enabled);
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index c32a2d10b0bc..4298140d31a0 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -668,5 +668,13 @@ public class PersistentDataBlockService extends SystemService {
IoUtils.closeQuietly(inputStream);
}
}
+
+ @Override
+ public void forceOemUnlockEnabled(boolean enabled) {
+ synchronized (mLock) {
+ doSetOemUnlockEnabledLocked(enabled);
+ computeAndWriteDigestLocked();
+ }
+ }
};
}
diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java
new file mode 100644
index 000000000000..6c1ffdd32d18
--- /dev/null
+++ b/services/core/java/com/android/server/SystemUpdateManagerService.java
@@ -0,0 +1,255 @@
+/*
+ * 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;
+
+import static android.os.SystemUpdateManager.KEY_STATUS;
+import static android.os.SystemUpdateManager.STATUS_IDLE;
+import static android.os.SystemUpdateManager.STATUS_UNKNOWN;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ISystemUpdateManager;
+import android.os.PersistableBundle;
+import android.os.SystemUpdateManager;
+import android.provider.Settings;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class SystemUpdateManagerService extends ISystemUpdateManager.Stub {
+
+ private static final String TAG = "SystemUpdateManagerService";
+
+ private static final int UID_UNKNOWN = -1;
+
+ private static final String INFO_FILE = "system-update-info.xml";
+ private static final int INFO_FILE_VERSION = 0;
+ private static final String TAG_INFO = "info";
+ private static final String KEY_VERSION = "version";
+ private static final String KEY_UID = "uid";
+ private static final String KEY_BOOT_COUNT = "boot-count";
+ private static final String KEY_INFO_BUNDLE = "info-bundle";
+
+ private final Context mContext;
+ private final AtomicFile mFile;
+ private final Object mLock = new Object();
+ private int mLastUid = UID_UNKNOWN;
+ private int mLastStatus = STATUS_UNKNOWN;
+
+ public SystemUpdateManagerService(Context context) {
+ mContext = context;
+ mFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), INFO_FILE));
+
+ // Populate mLastUid and mLastStatus.
+ synchronized (mLock) {
+ loadSystemUpdateInfoLocked();
+ }
+ }
+
+ @Override
+ public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.RECOVERY, TAG);
+
+ int status = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN);
+ if (status == STATUS_UNKNOWN) {
+ Slog.w(TAG, "Invalid status info. Ignored");
+ return;
+ }
+
+ // There could be multiple updater apps running on a device. But only one at most should
+ // be active (i.e. with a pending update), with the rest reporting idle status. We will
+ // only accept the reported status if any of the following conditions holds:
+ // a) none has been reported before;
+ // b) the current on-file status was last reported by the same caller;
+ // c) an active update is being reported.
+ int uid = Binder.getCallingUid();
+ if (mLastUid == UID_UNKNOWN || mLastUid == uid || status != STATUS_IDLE) {
+ synchronized (mLock) {
+ saveSystemUpdateInfoLocked(infoBundle, uid);
+ }
+ } else {
+ Slog.i(TAG, "Inactive updater reporting IDLE status. Ignored");
+ }
+ }
+
+ @Override
+ public Bundle retrieveSystemUpdateInfo() {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_SYSTEM_UPDATE_INFO)
+ == PackageManager.PERMISSION_DENIED
+ && mContext.checkCallingOrSelfPermission(Manifest.permission.RECOVERY)
+ == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException("Can't read system update info. Requiring "
+ + "READ_SYSTEM_UPDATE_INFO or RECOVERY permission.");
+ }
+
+ synchronized (mLock) {
+ return loadSystemUpdateInfoLocked();
+ }
+ }
+
+ // Reads and validates the info file. Returns the loaded info bundle on success; or a default
+ // info bundle with UNKNOWN status.
+ private Bundle loadSystemUpdateInfoLocked() {
+ PersistableBundle loadedBundle = null;
+ try (FileInputStream fis = mFile.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, StandardCharsets.UTF_8.name());
+ loadedBundle = readInfoFileLocked(parser);
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "No existing info file " + mFile.getBaseFile());
+ } catch (XmlPullParserException e) {
+ Slog.e(TAG, "Failed to parse the info file:", e);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read the info file:", e);
+ }
+
+ // Validate the loaded bundle.
+ if (loadedBundle == null) {
+ return removeInfoFileAndGetDefaultInfoBundleLocked();
+ }
+
+ int version = loadedBundle.getInt(KEY_VERSION, -1);
+ if (version == -1) {
+ Slog.w(TAG, "Invalid info file (invalid version). Ignored");
+ return removeInfoFileAndGetDefaultInfoBundleLocked();
+ }
+
+ int lastUid = loadedBundle.getInt(KEY_UID, -1);
+ if (lastUid == -1) {
+ Slog.w(TAG, "Invalid info file (invalid UID). Ignored");
+ return removeInfoFileAndGetDefaultInfoBundleLocked();
+ }
+
+ int lastBootCount = loadedBundle.getInt(KEY_BOOT_COUNT, -1);
+ if (lastBootCount == -1 || lastBootCount != getBootCount()) {
+ Slog.w(TAG, "Outdated info file. Ignored");
+ return removeInfoFileAndGetDefaultInfoBundleLocked();
+ }
+
+ PersistableBundle infoBundle = loadedBundle.getPersistableBundle(KEY_INFO_BUNDLE);
+ if (infoBundle == null) {
+ Slog.w(TAG, "Invalid info file (missing info). Ignored");
+ return removeInfoFileAndGetDefaultInfoBundleLocked();
+ }
+
+ int lastStatus = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN);
+ if (lastStatus == STATUS_UNKNOWN) {
+ Slog.w(TAG, "Invalid info file (invalid status). Ignored");
+ return removeInfoFileAndGetDefaultInfoBundleLocked();
+ }
+
+ // Everything looks good upon reaching this point.
+ mLastStatus = lastStatus;
+ mLastUid = lastUid;
+ return new Bundle(infoBundle);
+ }
+
+ private void saveSystemUpdateInfoLocked(PersistableBundle infoBundle, int uid) {
+ // Wrap the incoming bundle with extra info (e.g. version, uid, boot count). We use nested
+ // PersistableBundle to avoid manually parsing XML attributes when loading the info back.
+ PersistableBundle outBundle = new PersistableBundle();
+ outBundle.putPersistableBundle(KEY_INFO_BUNDLE, infoBundle);
+ outBundle.putInt(KEY_VERSION, INFO_FILE_VERSION);
+ outBundle.putInt(KEY_UID, uid);
+ outBundle.putInt(KEY_BOOT_COUNT, getBootCount());
+
+ // Only update the info on success.
+ if (writeInfoFileLocked(outBundle)) {
+ mLastUid = uid;
+ mLastStatus = infoBundle.getInt(KEY_STATUS);
+ }
+ }
+
+ // Performs I/O work only, without validating the loaded info.
+ @Nullable
+ private PersistableBundle readInfoFileLocked(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && TAG_INFO.equals(parser.getName())) {
+ return PersistableBundle.restoreFromXml(parser);
+ }
+ }
+ return null;
+ }
+
+ private boolean writeInfoFileLocked(PersistableBundle outBundle) {
+ FileOutputStream fos = null;
+ try {
+ fos = mFile.startWrite();
+
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+
+ out.startTag(null, TAG_INFO);
+ outBundle.saveToXml(out);
+ out.endTag(null, TAG_INFO);
+
+ out.endDocument();
+ mFile.finishWrite(fos);
+ return true;
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Failed to save the info file:", e);
+ if (fos != null) {
+ mFile.failWrite(fos);
+ }
+ }
+ return false;
+ }
+
+ private Bundle removeInfoFileAndGetDefaultInfoBundleLocked() {
+ if (mFile.exists()) {
+ Slog.i(TAG, "Removing info file");
+ mFile.delete();
+ }
+
+ mLastStatus = STATUS_UNKNOWN;
+ mLastUid = UID_UNKNOWN;
+ Bundle infoBundle = new Bundle();
+ infoBundle.putInt(KEY_STATUS, STATUS_UNKNOWN);
+ return infoBundle;
+ }
+
+ private int getBootCount() {
+ return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index e38148c7bd42..db21ef1f9b5c 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -676,6 +676,15 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
}
+ public void dumpStacks(PrintWriter pw) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ pw.print(mStacks.get(i).mStackId);
+ if (i > 0) {
+ pw.print(",");
+ }
+ }
+ }
+
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 29d33ce777f5..254d49b0bf27 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -27,9 +27,9 @@ import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
@@ -120,6 +120,7 @@ import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICAT
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -198,6 +199,7 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -277,8 +279,8 @@ import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
@@ -356,18 +358,18 @@ import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
-import android.util.LongSparseArray;
-import android.util.StatsLog;
-import android.util.TimingsTraceLog;
import android.util.DebugUtils;
import android.util.EventLog;
import android.util.Log;
+import android.util.LongSparseArray;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.StatsLog;
import android.util.TimeUtils;
+import android.util.TimingsTraceLog;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
@@ -440,6 +442,12 @@ import com.android.server.utils.PriorityDump;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
+
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -479,11 +487,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -8601,6 +8604,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
return false;
}
+
+ @Override
+ public int getPackageUid(String packageName, int flags) {
+ try {
+ return mActivityManagerService.mContext.getPackageManager()
+ .getPackageUid(packageName, flags);
+ } catch (NameNotFoundException nnfe) {
+ return -1;
+ }
+ }
}
class IntentFirewallInterface implements IntentFirewall.AMSInterface {
@@ -10404,10 +10417,9 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "getTaskDescriptionIcon");
- }
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
+
final File passedIconFile = new File(filePath);
final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
passedIconFile.getName());
@@ -21470,6 +21482,17 @@ public class ActivityManagerService extends IActivityManager.Stub
private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
final Rect newStackBounds = new Rect();
final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+ // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
+ if (stack == null) {
+ final StringWriter writer = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(writer);
+ mStackSupervisor.dumpDisplays(printWriter);
+ printWriter.flush();
+
+ Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
+ }
+
stack.getBoundsForNewConfiguration(newStackBounds);
mStackSupervisor.resizeStackLocked(
stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
@@ -25149,6 +25172,10 @@ public class ActivityManagerService extends IActivityManager.Stub
public int getMaxRunningUsers() {
return mUserController.mMaxRunningUsers;
}
+
+ public boolean isCallerRecents(int callingUid) {
+ return getRecentTasks().isCallerRecents(callingUid);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8168cba213f5..3d8863ee8c01 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3752,6 +3752,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
+ public void dumpDisplays(PrintWriter pw) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.valueAt(i);
+ pw.print("[id:" + display.mDisplayId + " stacks:");
+ display.dumpStacks(pw);
+ pw.print("]");
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 89102748796a..26d65bc7414c 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -745,7 +745,7 @@ class AppErrors {
mContext.getContentResolver(),
Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
0,
- UserHandle.USER_CURRENT) != 0;
+ mService.mUserController.getCurrentUserId()) != 0;
final boolean crashSilenced = mAppsNotReportingCrashes != null &&
mAppsNotReportingCrashes.contains(proc.info.packageName);
if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 1fcaeef72dba..927b72ceb37e 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -99,7 +99,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
// Keep the last WiFi stats so we can compute a delta.
@GuardedBy("mWorkerLock")
private WifiActivityEnergyInfo mLastInfo =
- new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
+ new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0);
BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
mContext = context;
@@ -374,6 +374,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
+ final long lastScanMs = mLastInfo.mControllerScanTimeMs;
final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
final long lastTxMs = mLastInfo.mControllerTxTimeMs;
final long lastRxMs = mLastInfo.mControllerRxTimeMs;
@@ -388,14 +389,16 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
final long txTimeMs = latest.mControllerTxTimeMs - lastTxMs;
final long rxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
+ final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
- if (txTimeMs < 0 || rxTimeMs < 0) {
+ if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0) {
// The stats were reset by the WiFi system (which is why our delta is negative).
// Returns the unaltered stats.
delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
+ delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
} else {
final long totalActiveTimeMs = txTimeMs + rxTimeMs;
@@ -433,6 +436,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
// These times seem to be the most reliable.
delta.mControllerTxTimeMs = txTimeMs;
delta.mControllerRxTimeMs = rxTimeMs;
+ delta.mControllerScanTimeMs = scanTimeMs;
// WiFi calculates the idle time as a difference from the on time and the various
// Rx + Tx times. There seems to be some missing time there because this sometimes
// becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 81e8eb0d4136..04b49ba3db33 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,8 @@ import android.os.UserManagerInternal;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
import android.os.health.HealthStatsParceler;
import android.os.health.HealthStatsWriter;
import android.os.health.UidHealthStats;
@@ -594,6 +596,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
+ public void noteGpsSignalQuality(int signalLevel) {
+ synchronized (mStats) {
+ mStats.noteGpsSignalQualityLocked(signalLevel);
+ }
+ }
+
public void noteScreenState(int state) {
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
@@ -1448,6 +1456,26 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
/**
+ * Gets a snapshot of Wifi stats
+ * @hide
+ */
+ public WifiBatteryStats getWifiBatteryStats() {
+ synchronized (mStats) {
+ return mStats.getWifiBatteryStats();
+ }
+ }
+
+ /**
+ * Gets a snapshot of Gps stats
+ * @hide
+ */
+ public GpsBatteryStats getGpsBatteryStats() {
+ synchronized (mStats) {
+ return mStats.getGpsBatteryStats();
+ }
+ }
+
+ /**
* Gets a snapshot of the system health for a particular uid.
*/
@Override
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 1a47aa5cd777..5ada484e9e32 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1431,7 +1431,13 @@ class UserController implements Handler.Callback {
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
- if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
+ if (mInjector.isCallerRecents(callingUid)
+ && callingUserId == getCurrentUserId()
+ && isSameProfileGroup(callingUserId, targetUserId)) {
+ // If the caller is Recents and it is running in the current user, we then allow it
+ // to access its profiles.
+ allow = true;
+ } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
// If the caller has this permission, they always pass go. And collect $200.
allow = true;
@@ -2149,5 +2155,9 @@ class UserController implements Handler.Callback {
mService.mLockTaskController.clearLockedTasks(reason);
}
}
+
+ protected boolean isCallerRecents(int callingUid) {
+ return mService.getRecentTasks().isCallerRecents(callingUid);
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 799f2a92bc33..a7147206bda2 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1047,9 +1047,11 @@ public class AudioService extends IAudioService.Stub
private void checkMuteAffectedStreams() {
// any stream with a min level > 0 is not muteable by definition
+ // STREAM_VOICE_CALL can be muted by applications that has the the MODIFY_PHONE_STATE permission.
for (int i = 0; i < mStreamStates.length; i++) {
final VolumeStreamState vss = mStreamStates[i];
- if (vss.mIndexMin > 0) {
+ if (vss.mIndexMin > 0 &&
+ vss.mStreamType != AudioSystem.STREAM_VOICE_CALL) {
mMuteAffectedStreams &= ~(1 << vss.mStreamType);
}
}
@@ -1412,6 +1414,18 @@ public class AudioService extends IAudioService.Stub
return;
}
+ // If adjust is mute and the stream is STREAM_VOICE_CALL, make sure
+ // that the calling app have the MODIFY_PHONE_STATE permission.
+ if (isMuteAdjust &&
+ streamType == AudioSystem.STREAM_VOICE_CALL &&
+ mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
// use stream type alias here so that streams with same alias have the same behavior,
// including with regard to silent mode control (e.g the use of STREAM_RING below and in
// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
@@ -1712,6 +1726,15 @@ public class AudioService extends IAudioService.Stub
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
return;
}
+ if ((streamType == AudioManager.STREAM_VOICE_CALL) &&
+ (index == 0) &&
+ (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED)) {
+ Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
+ + " MODIFY_PHONE_STATE callingPackage=" + callingPackage);
+ return;
+ }
mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
@@ -4132,22 +4155,30 @@ public class AudioService extends IAudioService.Stub
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
{
+ return setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ device, state, profile, false /* suppressNoisyIntent */);
+ }
+
+ public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
+ int state, int profile, boolean suppressNoisyIntent)
+ {
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
return 0;
}
return setBluetoothA2dpDeviceConnectionStateInt(
- device, state, profile, AudioSystem.DEVICE_NONE);
+ device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE);
}
public int setBluetoothA2dpDeviceConnectionStateInt(
- BluetoothDevice device, int state, int profile, int musicDevice)
+ BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
+ int musicDevice)
{
int delay;
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
throw new IllegalArgumentException("invalid profile " + profile);
}
synchronized (mConnectedDevices) {
- if (profile == BluetoothProfile.A2DP) {
+ if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
intState, musicDevice);
@@ -4503,27 +4534,30 @@ public class AudioService extends IAudioService.Stub
if (mStreamType == srcStream.mStreamType) {
return;
}
- synchronized (VolumeStreamState.class) {
- int srcStreamType = srcStream.getStreamType();
- // apply default device volume from source stream to all devices first in case
- // some devices are present in this stream state but not in source stream state
- int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
- index = rescaleIndex(index, srcStreamType, mStreamType);
- for (int i = 0; i < mIndexMap.size(); i++) {
- mIndexMap.put(mIndexMap.keyAt(i), index);
- }
- // Now apply actual volume for devices in source stream state
- SparseIntArray srcMap = srcStream.mIndexMap;
- for (int i = 0; i < srcMap.size(); i++) {
- int device = srcMap.keyAt(i);
- index = srcMap.valueAt(i);
+ synchronized (mSettingsLock) {
+ synchronized (VolumeStreamState.class) {
+ int srcStreamType = srcStream.getStreamType();
+ // apply default device volume from source stream to all devices first in case
+ // some devices are present in this stream state but not in source stream state
+ int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
index = rescaleIndex(index, srcStreamType, mStreamType);
-
- setIndex(index, device, caller);
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ mIndexMap.put(mIndexMap.keyAt(i), index);
+ }
+ // Now apply actual volume for devices in source stream state
+ SparseIntArray srcMap = srcStream.mIndexMap;
+ for (int i = 0; i < srcMap.size(); i++) {
+ int device = srcMap.keyAt(i);
+ index = srcMap.valueAt(i);
+ index = rescaleIndex(index, srcStreamType, mStreamType);
+
+ setIndex(index, device, caller);
+ }
}
}
}
+ @GuardedBy("mSettingsLock")
public void setAllIndexesToMax() {
synchronized (VolumeStreamState.class) {
for (int i = 0; i < mIndexMap.size(); i++) {
@@ -5397,7 +5431,7 @@ public class AudioService extends IAudioService.Stub
// consistent with audio policy manager state
setBluetoothA2dpDeviceConnectionStateInt(
btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
- musicDevice);
+ false /* suppressNoisyIntent */, musicDevice);
}
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index e093c9df7c4b..1ae7d20fb3fe 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -222,7 +222,7 @@ class TunerSession extends ITuner.Stub {
MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
MutableBoolean flagState = new MutableBoolean(false);
try {
- mHwSession.getConfigFlag(flag, (int result, boolean value) -> {
+ mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> {
halResult.value = result;
flagState.value = value;
});
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index bd1dbf9c46e8..fa5fdf587b8d 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -63,6 +63,7 @@ import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -823,6 +824,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
}
startTrackingJobLocked(jobStatus, toCancel);
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ uId, null, jobStatus.getBatteryName(), 2);
// 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/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 6dc5403acc99..48d275cc483f 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -827,7 +827,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
return isEnabled();
}
};
- mGnssMetrics = new GnssMetrics();
+ mGnssMetrics = new GnssMetrics(mBatteryStats);
}
/**
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index e458f485c9ab..971ac8b922ef 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -66,4 +66,9 @@ public abstract class NetworkPolicyManagerInternal {
* given network.
*/
public abstract long getSubscriptionOpportunisticQuota(Network network, int quotaType);
+
+ /**
+ * Informs that admin data is loaded and available.
+ */
+ public abstract void onAdminDataAvailable();
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 96b06c2c7f66..e406d51ac823 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -199,6 +199,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
@@ -333,6 +334,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
+ /**
+ * Indicates the maximum wait time for admin data to be available;
+ */
+ private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+
private static final int MSG_RULES_CHANGED = 1;
private static final int MSG_METERED_IFACES_CHANGED = 2;
private static final int MSG_LIMIT_REACHED = 5;
@@ -384,6 +390,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final boolean mSuppressDefaultPolicy;
+ private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+
/** Defined network policies. */
@GuardedBy("mNetworkPoliciesSecondLock")
final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
@@ -672,6 +680,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mSystemReady = true;
+ waitForAdminData();
+
// read policy from disk
readPolicyAL();
@@ -4590,6 +4600,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network));
}
}
+
+ @Override
+ public void onAdminDataAvailable() {
+ mAdminDataAvailableLatch.countDown();
+ }
}
private int parseSubId(NetworkState state) {
@@ -4617,6 +4632,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return ArrayUtils.isEmpty(plans) ? null : plans[0];
}
+ /**
+ * This will only ever be called once - during device boot.
+ */
+ private void waitForAdminData() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+ ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
+ WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
+ }
+ }
+
private static boolean hasRule(int uidRules, int rule) {
return (uidRules & rule) != 0;
}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index fbf11fce08eb..7387ad4f90ff 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -161,7 +161,7 @@ class WatchlistConfig {
public boolean containsDomain(String domain) {
final CrcShaDigests domainDigests = mDomainDigests;
if (domainDigests == null) {
- Slog.wtf(TAG, "domainDigests should not be null");
+ // mDomainDigests is not initialized
return false;
}
// First it does a quick CRC32 check.
@@ -177,7 +177,7 @@ class WatchlistConfig {
public boolean containsIp(String ip) {
final CrcShaDigests ipDigests = mIpDigests;
if (ipDigests == null) {
- Slog.wtf(TAG, "ipDigests should not be null");
+ // mIpDigests is not initialized
return false;
}
// First it does a quick CRC32 check.
@@ -234,12 +234,20 @@ class WatchlistConfig {
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Domain CRC32 digest list:");
- mDomainDigests.crc32Digests.dump(fd, pw, args);
+ if (mDomainDigests != null) {
+ mDomainDigests.crc32Digests.dump(fd, pw, args);
+ }
pw.println("Domain SHA256 digest list:");
- mDomainDigests.sha256Digests.dump(fd, pw, args);
+ if (mDomainDigests != null) {
+ mDomainDigests.sha256Digests.dump(fd, pw, args);
+ }
pw.println("Ip CRC32 digest list:");
- mIpDigests.crc32Digests.dump(fd, pw, args);
+ if (mIpDigests != null) {
+ mIpDigests.crc32Digests.dump(fd, pw, args);
+ }
pw.println("Ip SHA256 digest list:");
- mIpDigests.sha256Digests.dump(fd, pw, args);
+ if (mIpDigests != null) {
+ mIpDigests.sha256Digests.dump(fd, pw, args);
+ }
}
}
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 2a2ff06b7a5a..a6200bf433e4 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -31,10 +31,10 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.service.oemlock.IOemLockService;
-import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Slog;
import com.android.server.LocalServices;
+import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemService;
import com.android.server.pm.UserRestrictionsUtils;
@@ -217,13 +217,12 @@ public class OemLockService extends SystemService {
* is used to erase FRP information on a unlockable device.
*/
private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) {
- final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
- mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+ final PersistentDataBlockManagerInternal pdbmi
+ = LocalServices.getService(PersistentDataBlockManagerInternal.class);
// if mOemLock is PersistentDataBlockLock, then the bit should have already been set
- if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock)
- && pdbm.getOemUnlockEnabled() != allowed) {
+ if (pdbmi != null && !(mOemLock instanceof PersistentDataBlockLock)) {
Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed);
- pdbm.setOemUnlockEnabled(allowed);
+ pdbmi.forceOemUnlockEnabled(allowed);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index cc448daa2619..a6ff4f7e5f70 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -344,9 +344,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isSelfUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+ final boolean isUpdatePermissionGranted =
+ (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
+ mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+ final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
final boolean isPermissionGranted = isInstallPermissionGranted
- || (isSelfUpdatePermissionGranted
- && mPm.getPackageUid(mPackageName, 0, userId) == mInstallerUid);
+ || (isUpdatePermissionGranted && targetPackageUid != -1)
+ || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
final boolean forcePermissionPrompt =
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2585cf3d7fa5..faf6114237cd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -48,7 +48,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_NEWER_SDK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE;
@@ -119,6 +118,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.ResourcesManager;
@@ -993,6 +993,7 @@ public class PackageManagerService extends IPackageManager.Stub
private List<String> mKeepUninstalledPackages;
private UserManagerInternal mUserManagerInternal;
+ private ActivityManagerInternal mActivityManagerInternal;
private DeviceIdleController.LocalService mDeviceIdleController;
@@ -4576,6 +4577,14 @@ Slog.e("TODD",
return mUserManagerInternal;
}
+ private ActivityManagerInternal getActivityManagerInternal() {
+ if (mActivityManagerInternal == null) {
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ }
+ return mActivityManagerInternal;
+ }
+
+
private DeviceIdleController.LocalService getDeviceIdleController() {
if (mDeviceIdleController == null) {
mDeviceIdleController =
@@ -4736,8 +4745,12 @@ Slog.e("TODD",
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, component);
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
- false /* requireFullPermission */, false /* checkShell */, "get activity info");
+
+ if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ false /* requireFullPermission */, false /* checkShell */, "get activity info");
+ }
+
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -4759,6 +4772,22 @@ Slog.e("TODD",
return null;
}
+ private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
+ if (!getActivityManagerInternal().isCallerRecents(callingUid)) {
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (ActivityManager.getCurrentUser() != callingUserId) {
+ return false;
+ }
+ return sUserManager.isSameProfileGroup(callingUserId, targetUserId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public boolean activitySupportsIntent(ComponentName component, Intent intent,
String resolvedType) {
@@ -16495,16 +16524,6 @@ Slog.e("TODD",
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- // App targetSdkVersion is below min supported version
- if (!forceSdk && pkg.applicationInfo.isTargetingDeprecatedSdkVersion()) {
- Slog.w(TAG, "App " + pkg.packageName + " targets deprecated sdk");
-
- res.setError(INSTALL_FAILED_NEWER_SDK,
- "App is targeting deprecated sdk (targetSdkVersion should be at least "
- + Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT + ").");
- return;
- }
-
// Instant apps have several additional install-time checks.
if (instantApp) {
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a33f0716c4cb..47cd81326932 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -236,6 +236,8 @@ class PackageManagerShellCommand extends ShellCommand {
return runHasFeature();
case "set-harmful-app-warning":
return runSetHarmfulAppWarning();
+ case "get-harmful-app-warning":
+ return runGetHarmfulAppWarning();
default: {
String nextArg = getNextArg();
if (nextArg == null) {
@@ -2125,6 +2127,31 @@ class PackageManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runGetHarmfulAppWarning() throws RemoteException {
+ int userId = UserHandle.USER_CURRENT;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+
+ userId = translateUserId(userId, false /*allowAll*/, "runGetHarmfulAppWarning");
+
+ final String packageName = getNextArgRequired();
+ final CharSequence warning = mInterface.getHarmfulAppWarning(packageName, userId);
+ if (!TextUtils.isEmpty(warning)) {
+ getOutPrintWriter().println(warning);
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
private static String checkAbiArgument(String abi) {
if (TextUtils.isEmpty(abi)) {
throw new IllegalArgumentException("Missing ABI argument");
@@ -2684,6 +2711,9 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println("");
pw.println(" set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]");
pw.println(" Mark the app as harmful with the given warning message.");
+ pw.println("");
+ pw.println(" get-harmful-app-warning [--user <USER_ID>] <PACKAGE>");
+ pw.println(" Return the harmful app warning message for the given app, if present");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 63449976587e..0f394a4eed65 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -66,7 +66,6 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
@@ -163,13 +162,10 @@ import android.app.StatusBarManager;
import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -202,7 +198,6 @@ import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
@@ -1030,8 +1025,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void run() {
// send interaction hint to improve redraw performance
mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
- if (showRotationChoice(mCurrentAppOrientation, mRotation)) {
- sendProposedRotationChangeToStatusBarInternal(mRotation);
+ if (isRotationChoiceEnabled()) {
+ final boolean isValid = isValidRotationChoice(mCurrentAppOrientation,
+ mRotation);
+ sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
} else {
updateRotation(false);
}
@@ -4355,6 +4352,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
final int fl = PolicyControl.getWindowFlags(null, attrs);
+ final int pfl = attrs.privateFlags;
final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
final int displayRotation = displayFrames.mRotation;
@@ -4377,8 +4375,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
- == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+ final boolean layoutInScreenAndInsetDecor =
+ (fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
+ == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR);
+ final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
+
+ if (layoutInScreenAndInsetDecor && !screenDecor) {
Rect frame;
int availRight, availBottom;
if (canHideNavigationBar() &&
@@ -4498,7 +4500,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
mInputConsumer = null;
}
- } else if (mInputConsumer == null) {
+ } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
INPUT_CONSUMER_NAVIGATION,
(channel, looper) -> new HideNavInputEventReceiver(channel, looper));
@@ -6193,10 +6195,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/**
* Notify the StatusBar that system rotation suggestion has changed.
*/
- private void sendProposedRotationChangeToStatusBarInternal(int rotation) {
+ private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
- statusBar.onProposedRotationChanged(rotation);
+ statusBar.onProposedRotationChanged(rotation, isValid);
}
}
@@ -7142,15 +7144,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mOrientationListener.setCurrentRotation(rotation);
}
- public boolean showRotationChoice(int orientation, final int preferredRotation) {
+ public boolean isRotationChoiceEnabled() {
// Rotation choice is only shown when the user is in locked mode.
if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
- // We should only show a rotation choice if:
- // 1. The rotation isn't forced by the lid, dock, demo, hdmi, vr, etc mode
- // 2. The user choice won't be ignored due to screen orientation settings
+ // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
+ // demo, hdmi, vr, etc mode
- // Determine if the rotation currently forced
+ // Determine if the rotation is currently forced
if (mForceDefaultOrientation) {
return false; // Rotation is forced to default orientation
@@ -7183,7 +7184,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return false;
}
- // Determine if the orientation will ignore user choice
+ // Rotation isn't forced, enable choice
+ return true;
+ }
+
+ public boolean isValidRotationChoice(int orientation, final int preferredRotation) {
+ // Determine if the given app orientation can be chosen and, if so, if it is compatible
+ // with the provided rotation choice
+
switch (orientation) {
case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index cf930f5cc6b7..5811714c73c4 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -21,7 +21,10 @@ import android.app.slice.SliceSpec;
import android.content.ContentProviderClient;
import android.net.Uri;
import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -48,9 +51,13 @@ public class PinnedSliceState {
@GuardedBy("mLock")
private final ArraySet<String> mPinnedPkgs = new ArraySet<>();
@GuardedBy("mLock")
- private final ArraySet<ISliceListener> mListeners = new ArraySet<>();
+ private final ArrayMap<IBinder, ISliceListener> mListeners = new ArrayMap<>();
@GuardedBy("mLock")
private SliceSpec[] mSupportedSpecs = null;
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, String> mPkgMap = new ArrayMap<>();
+
+ private final DeathRecipient mDeathRecipient = this::handleRecheckListeners;
public PinnedSliceState(SliceManagerService service, Uri uri) {
mService = service;
@@ -102,20 +109,29 @@ public class PinnedSliceState {
mService.getHandler().post(this::handleBind);
}
- public void addSliceListener(ISliceListener listener, SliceSpec[] specs) {
+ public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs) {
synchronized (mLock) {
- if (mListeners.add(listener) && mListeners.size() == 1) {
+ if (mListeners.size() == 0) {
mService.listen(mUri);
}
+ try {
+ listener.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ }
+ mListeners.put(listener.asBinder(), listener);
+ mPkgMap.put(listener.asBinder(), pkg);
mergeSpecs(specs);
}
}
public boolean removeSliceListener(ISliceListener listener) {
synchronized (mLock) {
- if (mListeners.remove(listener) && mListeners.size() == 0) {
+ listener.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ mPkgMap.remove(listener.asBinder());
+ if (mListeners.containsKey(listener.asBinder()) && mListeners.size() == 1) {
mService.unlisten(mUri);
}
+ mListeners.remove(listener.asBinder());
}
return !isPinned();
}
@@ -154,38 +170,68 @@ public class PinnedSliceState {
return client;
}
+ private void handleRecheckListeners() {
+ if (!isPinned()) return;
+ synchronized (mLock) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ ISliceListener l = mListeners.valueAt(i);
+ if (!l.asBinder().isBinderAlive()) {
+ mListeners.removeAt(i);
+ }
+ }
+ if (!isPinned()) {
+ // All the listeners died, remove from pinned state.
+ mService.removePinnedSlice(mUri);
+ }
+ }
+ }
+
private void handleBind() {
- Slice s;
+ Slice cachedSlice = doBind(null);
+ synchronized (mLock) {
+ if (!isPinned()) return;
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ ISliceListener l = mListeners.valueAt(i);
+ Slice s = cachedSlice;
+ if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) {
+ s = doBind(mPkgMap.get(l));
+ }
+ if (s == null) {
+ mListeners.removeAt(i);
+ continue;
+ }
+ try {
+ l.onSliceUpdated(s);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to notify slice " + mUri, e);
+ mListeners.removeAt(i);
+ continue;
+ }
+ }
+ if (!isPinned()) {
+ // All the listeners died, remove from pinned state.
+ mService.removePinnedSlice(mUri);
+ }
+ }
+ }
+
+ private Slice doBind(String overridePkg) {
try (ContentProviderClient client = getClient()) {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(Arrays.asList(mSupportedSpecs)));
+ extras.putString(SliceProvider.EXTRA_OVERRIDE_PKG, overridePkg);
final Bundle res;
try {
res = client.call(SliceProvider.METHOD_SLICE, null, extras);
} catch (RemoteException e) {
Log.e(TAG, "Unable to bind slice " + mUri, e);
- return;
+ return null;
}
- if (res == null) return;
+ if (res == null) return null;
Bundle.setDefusable(res, true);
- s = res.getParcelable(SliceProvider.EXTRA_SLICE);
- }
- synchronized (mLock) {
- mListeners.removeIf(l -> {
- try {
- l.onSliceUpdated(s);
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to notify slice " + mUri, e);
- return true;
- }
- });
- if (!isPinned()) {
- // All the listeners died, remove from pinned state.
- mService.removePinnedSlice(mUri);
- }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
}
}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 2d9e772a6b0c..c1915801b61b 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -16,27 +16,35 @@
package com.android.server.slice;
+import static android.content.ContentProvider.getUriWithoutUserId;
import static android.content.ContentProvider.getUserIdFromUri;
import static android.content.ContentProvider.maybeAddUserId;
import android.Manifest.permission;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.ContentProviderHolder;
+import android.app.IActivityManager;
import android.app.slice.ISliceListener;
import android.app.slice.ISliceManager;
+import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -63,6 +71,8 @@ public class SliceManagerService extends ISliceManager.Stub {
@GuardedBy("mLock")
private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
private final Handler mHandler;
private final ContentObserver mObserver;
@@ -81,9 +91,9 @@ public class SliceManagerService extends ISliceManager.Stub {
mObserver = new ContentObserver(mHandler) {
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, int userId) {
try {
- getPinnedSlice(uri).onChange();
+ getPinnedSlice(maybeAddUserId(uri, userId)).onChange();
} catch (IllegalStateException e) {
Log.e(TAG, "Received change for unpinned slice " + uri, e);
}
@@ -111,7 +121,7 @@ public class SliceManagerService extends ISliceManager.Stub {
verifyCaller(pkg);
uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
enforceAccess(pkg, uri);
- getOrCreatePinnedSlice(uri).addSliceListener(listener, specs);
+ getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs);
}
@Override
@@ -156,8 +166,45 @@ public class SliceManagerService extends ISliceManager.Stub {
return getPinnedSlice(uri).getSpecs();
}
+ @Override
+ public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
+ if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return SliceManager.PERMISSION_GRANTED;
+ }
+ if (hasFullSliceAccess(pkg, uid)) {
+ return SliceManager.PERMISSION_GRANTED;
+ }
+ synchronized (mLock) {
+ if (mUserGrants.contains(new SliceGrant(uri, pkg))) {
+ return SliceManager.PERMISSION_USER_GRANTED;
+ }
+ }
+ return SliceManager.PERMISSION_DENIED;
+ }
+
+ @Override
+ public void grantPermissionFromUser(Uri uri, String pkg, String callingPkg, boolean allSlices) {
+ verifyCaller(callingPkg);
+ getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
+ "Slice granting requires MANAGE_SLICE_PERMISSIONS");
+ if (allSlices) {
+ // TODO: Manage full access grants.
+ } else {
+ synchronized (mLock) {
+ mUserGrants.add(new SliceGrant(uri, pkg));
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.getContentResolver().notifyChange(uri, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
/// ----- internal code -----
- void removePinnedSlice(Uri uri) {
+ protected void removePinnedSlice(Uri uri) {
synchronized (mLock) {
mPinnedSlicesByUri.remove(uri).destroy();
}
@@ -186,7 +233,7 @@ public class SliceManagerService extends ISliceManager.Stub {
}
@VisibleForTesting
- PinnedSliceState createPinnedSlice(Uri uri) {
+ protected PinnedSliceState createPinnedSlice(Uri uri) {
return new PinnedSliceState(this, uri);
}
@@ -202,12 +249,45 @@ public class SliceManagerService extends ISliceManager.Stub {
return mHandler;
}
- private void enforceAccess(String pkg, Uri uri) {
- getContext().enforceUriPermission(uri, permission.BIND_SLICE,
- permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
+ private void enforceAccess(String pkg, Uri uri) throws RemoteException {
int user = Binder.getCallingUserHandle().getIdentifier();
+ // Check for default launcher/assistant.
+ if (!hasFullSliceAccess(pkg, Binder.getCallingUid())) {
+ try {
+ // Also allow things with uri access.
+ getContext().enforceUriPermission(uri, Binder.getCallingPid(),
+ Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires permission to the Uri");
+ } catch (SecurityException e) {
+ // Last fallback (if the calling app owns the authority, then it can have access).
+ long ident = Binder.clearCallingIdentity();
+ try {
+ IBinder token = new Binder();
+ IActivityManager activityManager = ActivityManager.getService();
+ ContentProviderHolder holder = null;
+ String providerName = getUriWithoutUserId(uri).getAuthority();
+ try {
+ holder = activityManager.getContentProviderExternal(
+ providerName, getUserIdFromUri(uri, user), token);
+ if (holder == null || holder.info == null
+ || !Objects.equals(holder.info.packageName, pkg)) {
+ // No more fallbacks, no access.
+ throw e;
+ }
+ } finally {
+ if (holder != null && holder.provider != null) {
+ activityManager.removeContentProviderExternal(providerName, token);
+ }
+ }
+ } finally {
+ // I know, the double finally seems ugly, but seems safest for the identity.
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ // Lastly check for any multi-userness. Any return statements above here will break this
+ // important check.
if (getUserIdFromUri(uri, user) != user) {
getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
"Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
@@ -230,8 +310,14 @@ public class SliceManagerService extends ISliceManager.Stub {
}
private boolean hasFullSliceAccess(String pkg, int userId) {
- return isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
- || isGrantedFullAccess(pkg, userId);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
+ || isGrantedFullAccess(pkg, userId);
+ return ret;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private boolean isAssistant(String pkg, int userId) {
@@ -259,13 +345,14 @@ public class SliceManagerService extends ISliceManager.Stub {
private boolean isDefaultHomeApp(String pkg, int userId) {
String defaultHome = getDefaultHome(userId);
- return Objects.equals(pkg, defaultHome);
+
+ return pkg != null && Objects.equals(pkg, defaultHome);
}
// Based on getDefaultHome in ShortcutService.
// TODO: Unify if possible
@VisibleForTesting
- String getDefaultHome(int userId) {
+ protected String getDefaultHome(int userId) {
final long token = Binder.clearCallingIdentity();
try {
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
@@ -301,7 +388,7 @@ public class SliceManagerService extends ISliceManager.Stub {
lastPriority = ri.priority;
}
}
- return detected.getPackageName();
+ return detected != null ? detected.getPackageName() : null;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -349,4 +436,26 @@ public class SliceManagerService extends ISliceManager.Stub {
mService.onStopUser(userHandle);
}
}
+
+ private class SliceGrant {
+ private final Uri mUri;
+ private final String mPkg;
+
+ public SliceGrant(Uri uri, String pkg) {
+ mUri = uri;
+ mPkg = pkg;
+ }
+
+ @Override
+ public int hashCode() {
+ return mUri.hashCode() + mPkg.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SliceGrant)) return false;
+ SliceGrant other = (SliceGrant) obj;
+ return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2f5e2f8665f9..4cb5e089bd83 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -28,6 +28,7 @@ import android.content.pm.UserInfo;
import android.net.NetworkStats;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.SystemClock;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.os.BatteryStatsInternal;
@@ -621,6 +622,20 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
break;
}
+ case StatsLog.CPU_SUSPEND_TIME: {
+ List<StatsLogEventWrapper> ret = new ArrayList();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.elapsedRealtime());
+ ret.add(e);
+ break;
+ }
+ case StatsLog.CPU_IDLE_TIME: {
+ List<StatsLogEventWrapper> ret = new ArrayList();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.uptimeMillis());
+ ret.add(e);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b5d0c60efae8..95006ffb2ed6 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -95,7 +95,7 @@ public interface StatusBarManagerInternal {
*
* @param rotation rotation suggestion
*/
- void onProposedRotationChanged(int rotation);
+ void onProposedRotationChanged(int rotation, boolean isValid);
public interface GlobalActionsListener {
/**
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 79d3dbbe8c83..c58c208dea81 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -408,10 +408,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
- public void onProposedRotationChanged(int rotation) {
+ public void onProposedRotationChanged(int rotation, boolean isValid) {
if (mBar != null){
try {
- mBar.onProposedRotationChanged(rotation);
+ mBar.onProposedRotationChanged(rotation, isValid);
} catch (RemoteException ex) {}
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b474c62ba427..ce3f512deccf 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -463,10 +463,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
}
- // Update the client visibility if we are not running an animation. Otherwise, we'll
- // update client visibility state in onAnimationFinished.
- if (!visible && !delayed) {
- setClientHidden(true);
+ // If we're becoming visible, immediately change client visibility as well although it
+ // usually gets changed in AppWindowContainerController.setVisibility already. However,
+ // there seem to be some edge cases where we change our visibility but client visibility
+ // never gets updated.
+ // If we're becoming invisible, update the client visibility if we are not running an
+ // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+ if (visible || !delayed) {
+ setClientHidden(!visible);
}
// If we are hidden but there is no delay needed we immediately
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fba404ed6f0e..6dc384a8831e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -49,6 +49,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -3180,6 +3181,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
SurfaceControl mAppAnimationLayer = null;
+ /**
+ * Given that the split-screen divider does not have an AppWindowToken, it
+ * will have to live inside of a "NonAppWindowContainer", in particular
+ * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order
+ * it will need to be interleaved with some of our children, appearing on top of
+ * both docked stacks but underneath any assistant stacks.
+ *
+ * To solve this problem we have this anchor control, which will always exist so
+ * we can always assign it the correct value in our {@link #assignChildLayers}.
+ * Likewise since it always exists, {@link AboveAppWindowContainers} can always
+ * assign the divider a layer relative to it. This way we prevent linking lifecycle
+ * events between the two containers.
+ */
+ SurfaceControl mSplitScreenDividerAnchor = null;
+
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
private TaskStack mHomeStack = null;
@@ -3496,37 +3512,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
@Override
void assignChildLayers(SurfaceControl.Transaction t) {
- int layer = 0;
- // We allow stacks to change visual order from the AM specified order due to
- // Z-boosting during animations. However we must take care to ensure TaskStacks
- // which are marked as alwaysOnTop remain that way.
- for (int i = 0; i < mChildren.size(); i++) {
- final TaskStack s = mChildren.get(i);
- s.assignChildLayers();
- if (!s.needsZBoost() && !s.isAlwaysOnTop()) {
- s.assignLayer(t, layer++);
+ final int HOME_STACK_STATE = 0;
+ final int NORMAL_STACK_STATE = 1;
+ final int ALWAYS_ON_TOP_STATE = 2;
+
+ int layer = 0;
+ for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+ for (int i = 0; i < mChildren.size(); i++) {
+ final TaskStack s = mChildren.get(i);
+ if (state == HOME_STACK_STATE && s.isActivityTypeHome()) {
+ s.assignLayer(t, layer++);
+ } else if (state == NORMAL_STACK_STATE && !s.isActivityTypeHome()
+ && !s.isAlwaysOnTop()) {
+ s.assignLayer(t, layer++);
+ if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
+ t.setLayer(mSplitScreenDividerAnchor, layer++);
+ }
+ } else if (state == ALWAYS_ON_TOP_STATE && s.isAlwaysOnTop()) {
+ s.assignLayer(t, layer++);
+ }
}
- }
- for (int i = 0; i < mChildren.size(); i++) {
- final TaskStack s = mChildren.get(i);
- if (s.needsZBoost() && !s.isAlwaysOnTop()) {
- s.assignLayer(t, layer++);
+ // The appropriate place for App-Transitions to occur is right
+ // above all other animations but still below things in the Picture-and-Picture
+ // windowing mode.
+ if (state == NORMAL_STACK_STATE && mAppAnimationLayer != null) {
+ t.setLayer(mAppAnimationLayer, layer++);
}
}
for (int i = 0; i < mChildren.size(); i++) {
final TaskStack s = mChildren.get(i);
- if (s.isAlwaysOnTop()) {
- s.assignLayer(t, layer++);
- }
+ s.assignChildLayers(t);
}
- // The appropriate place for App-Transitions to occur is right
- // above all other animations but still below things in the Picture-and-Picture
- // windowing mode.
- if (mAppAnimationLayer != null) {
- t.setLayer(mAppAnimationLayer, layer++);
- }
}
@Override
@@ -3534,6 +3552,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mAppAnimationLayer;
}
+ SurfaceControl getSplitScreenDividerAnchor() {
+ return mSplitScreenDividerAnchor;
+ }
+
@Override
void onParentSet() {
super.onParentSet();
@@ -3541,11 +3563,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mAppAnimationLayer = makeChildSurface(null)
.setName("animationLayer")
.build();
- getPendingTransaction().show(mAppAnimationLayer);
+ mSplitScreenDividerAnchor = makeChildSurface(null)
+ .setName("splitScreenDividerAnchor")
+ .build();
+ getPendingTransaction()
+ .show(mAppAnimationLayer)
+ .show(mSplitScreenDividerAnchor);
scheduleAnimation();
} else {
mAppAnimationLayer.destroy();
mAppAnimationLayer = null;
+ mSplitScreenDividerAnchor.destroy();
+ mSplitScreenDividerAnchor = null;
}
}
}
@@ -3560,6 +3589,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
&& imeContainer.getSurfaceControl() != null;
for (int j = 0; j < mChildren.size(); ++j) {
final WindowToken wt = mChildren.get(j);
+
+ // See {@link mSplitScreenDividerAnchor}
+ if (wt.windowType == TYPE_DOCK_DIVIDER) {
+ wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1);
+ continue;
+ }
wt.assignLayer(t, j);
wt.assignChildLayers(t);
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 688b4ffcee4b..8515dcb69970 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -99,11 +99,15 @@ class RemoteAnimationController {
}
private RemoteAnimationTarget[] createAnimations() {
- final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()];
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
- result[i] = mPendingAnimations.get(i).createRemoteAppAnimation();
+ final RemoteAnimationTarget target =
+ mPendingAnimations.get(i).createRemoteAppAnimation();
+ if (target != null) {
+ targets.add(target);
+ }
}
- return result;
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
}
private void onAnimationFinished() {
@@ -145,9 +149,17 @@ class RemoteAnimationController {
}
RemoteAnimationTarget createRemoteAppAnimation() {
- return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(),
+ final Task task = mAppWindowToken.getTask();
+ final WindowState mainWindow = mAppWindowToken.findMainWindow();
+ if (task == null) {
+ return null;
+ }
+ if (mainWindow == null) {
+ return null;
+ }
+ return new RemoteAnimationTarget(task.mTaskId, getMode(),
mCapturedLeash, !mAppWindowToken.fillsParent(),
- mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect,
+ mainWindow.mWinAnimator.mLastClipRect,
mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b18c1a0ed504..0bc58e024f8a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -55,6 +55,17 @@ cc_library_static {
"frameworks/native/services",
"system/gatekeeper/include",
],
+
+ product_variables: {
+ arc: {
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ srcs: [
+ "com_android_server_ArcVideoService.cpp",
+ ],
+ }
+ }
}
cc_defaults {
@@ -119,4 +130,17 @@ cc_defaults {
"android.hardware.broadcastradio@common-utils-1x-lib",
"libscrypt_static",
],
+
+ product_variables: {
+ arc: {
+ // TODO: remove the suffix "_bp" after finishing migration to Android.bp.
+ shared_libs: [
+ "libarcbridge",
+ "libarcbridgeservice",
+ "libarcvideobridge",
+ "libchrome",
+ "libmojo_bp",
+ ],
+ }
+ }
}
diff --git a/services/core/jni/com_android_server_ArcVideoService.cpp b/services/core/jni/com_android_server_ArcVideoService.cpp
new file mode 100644
index 000000000000..7df8276085ee
--- /dev/null
+++ b/services/core/jni/com_android_server_ArcVideoService.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ArcVideoService"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <media/arcvideobridge/IArcVideoBridge.h>
+#include <utils/Log.h>
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+#include <mojo/edk/embedder/embedder.h>
+#include <mojo/public/cpp/bindings/binding.h>
+
+#include <arc/ArcBridgeSupport.h>
+#include <arc/ArcService.h>
+#include <arc/Future.h>
+#include <arc/IArcBridgeService.h>
+#include <arc/MojoProcessSupport.h>
+#include <video.mojom.h>
+
+namespace {
+
+// [MinVersion] of OnVideoInstanceReady method in arc_bridge.mojom.
+constexpr int kMinimumArcBridgeHostVersion = 6;
+
+void onCaptureResult(arc::Future<arc::MojoBootstrapResult>* future, uint32_t version,
+ mojo::ScopedHandle handle, const std::string& token) {
+ mojo::edk::ScopedPlatformHandle scoped_platform_handle;
+ MojoResult result =
+ mojo::edk::PassWrappedPlatformHandle(handle.release().value(), &scoped_platform_handle);
+ if (result != MOJO_RESULT_OK) {
+ ALOGE("Received invalid file descriptor.");
+ future->set(arc::MojoBootstrapResult());
+ return;
+ }
+
+ base::ScopedFD fd(scoped_platform_handle.release().handle);
+ future->set(arc::MojoBootstrapResult(std::move(fd), token, version));
+}
+
+} // namespace
+
+namespace arc {
+
+class VideoService : public mojom::VideoInstance,
+ public ArcService,
+ public android::BnArcVideoBridge {
+public:
+ explicit VideoService(MojoProcessSupport* mojoProcessSupport)
+ : mMojoProcessSupport(mojoProcessSupport), mBinding(this) {
+ mMojoProcessSupport->arc_bridge_support().requestArcBridgeProxyAsync(
+ this, kMinimumArcBridgeHostVersion);
+ }
+
+ ~VideoService() override { mMojoProcessSupport->disconnect(&mBinding, &mHostPtr); }
+
+ // VideoInstance overrides:
+ void InitDeprecated(mojom::VideoHostPtr hostPtr) override {
+ Init(std::move(hostPtr), base::Bind(&base::DoNothing));
+ }
+
+ void Init(mojom::VideoHostPtr hostPtr, const InitCallback& callback) override {
+ ALOGV("Init");
+ mHostPtr = std::move(hostPtr);
+ // A method must be called while we are still in a Mojo thread so the
+ // proxy can perform lazy initialization and be able to be called from
+ // non-Mojo threads later.
+ // This also caches the version number so it can be obtained by calling
+ // .version().
+ mHostPtr.QueryVersion(base::Bind(
+ [](const InitCallback& callback, uint32_t version) {
+ ALOGI("VideoService ready (version=%d)", version);
+ callback.Run();
+ },
+ callback));
+ ALOGV("Init done");
+ }
+
+ // ArcService overrides:
+ void ready(mojom::ArcBridgeHostPtr* bridgeHost) override {
+ (*bridgeHost)->OnVideoInstanceReady(mBinding.CreateInterfacePtrAndBind());
+ }
+
+ void versionMismatch(uint32_t version) override {
+ ALOGE("ArcBridgeHost version %d, does not support video (version %d)\n", version,
+ kMinimumArcBridgeHostVersion);
+ }
+
+ // BnArcVideoBridge overrides:
+ MojoBootstrapResult bootstrapVideoAcceleratorFactory() override {
+ ALOGV("VideoService::bootstrapVideoAcceleratorFactory");
+
+ Future<MojoBootstrapResult> future;
+ mMojoProcessSupport->mojo_thread().getTaskRunner()->PostTask(
+ FROM_HERE, base::Bind(&VideoService::bootstrapVideoAcceleratorFactoryOnMojoThread,
+ base::Unretained(this), &future));
+ return future.get();
+ }
+
+ int32_t hostVersion() override {
+ ALOGV("VideoService::hostVersion");
+ return mHostPtr.version();
+ }
+
+private:
+ void bootstrapVideoAcceleratorFactoryOnMojoThread(Future<MojoBootstrapResult>* future) {
+ if (!mHostPtr) {
+ ALOGE("mHostPtr is not ready yet");
+ future->set(MojoBootstrapResult());
+ return;
+ }
+ mHostPtr->OnBootstrapVideoAcceleratorFactory(
+ base::Bind(&onCaptureResult, base::Unretained(future), mHostPtr.version()));
+ }
+
+ // Outlives VideoService.
+ MojoProcessSupport* const mMojoProcessSupport;
+ mojo::Binding<mojom::VideoInstance> mBinding;
+ mojom::VideoHostPtr mHostPtr;
+};
+
+} // namespace arc
+
+namespace android {
+
+int register_android_server_ArcVideoService() {
+ defaultServiceManager()->addService(
+ String16("android.os.IArcVideoBridge"),
+ new arc::VideoService(arc::MojoProcessSupport::getLeakyInstance()));
+ return 0;
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 071b6b808ff0..07ddb0561f92 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -52,6 +52,9 @@ int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_server_GraphicsStatsService(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
+#ifdef USE_ARC
+int register_android_server_ArcVideoService();
+#endif
};
using namespace android;
@@ -97,6 +100,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_GraphicsStatsService(env);
register_android_hardware_display_DisplayViewport(env);
register_android_server_net_NetworkStatsService(env);
-
+#ifdef USE_ARC
+ register_android_server_ArcVideoService();
+#endif
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 99275e88a242..7a0b1bf46fcf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -39,12 +39,6 @@ import java.util.List;
*/
abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
/**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when the service is started.
- *
- * @see {@link SystemService#onStart}.
- */
- abstract void handleStart();
- /**
* To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
*
* @see {@link SystemService#onBootPhase}.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 77b87b6394b4..6bee9d67f215 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -198,8 +198,10 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
import com.google.android.collect.Sets;
@@ -511,7 +513,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void onStart() {
publishBinderService(Context.DEVICE_POLICY_SERVICE, mService);
- mService.handleStart();
}
@Override
@@ -1743,6 +1744,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return LocalServices.getService(UsageStatsManagerInternal.class);
}
+ NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
+ return LocalServices.getService(NetworkPolicyManagerInternal.class);
+ }
+
NotificationManager getNotificationManager() {
return mContext.getSystemService(NotificationManager.class);
}
@@ -1996,6 +2001,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException {
return KeyChain.bindAsUser(mContext, user);
}
+
+ void postOnSystemServerInitThreadPool(Runnable runnable) {
+ SystemServerInitThreadPool.get().submit(runnable, LOG_TAG);
+ }
}
/**
@@ -3239,6 +3248,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
switch (phase) {
case SystemService.PHASE_LOCK_SETTINGS_READY:
onLockSettingsReady();
+ loadAdminDataAsync();
break;
case SystemService.PHASE_BOOT_COMPLETED:
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
@@ -3307,11 +3317,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- void handleStart() {
- pushActiveAdminPackages();
- }
-
- @Override
void handleStartUser(int userId) {
updateScreenCaptureDisabledInWindowManager(userId,
getScreenCaptureDisabled(null, userId));
@@ -3493,6 +3498,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private void loadAdminDataAsync() {
+ mInjector.postOnSystemServerInitThreadPool(() -> {
+ pushActiveAdminPackages();
+ mUsageStatsManagerInternal.onAdminDataAvailable();
+ mInjector.getNetworkPolicyManagerInternal().onAdminDataAvailable();
+ });
+ }
+
private void pushActiveAdminPackages() {
synchronized (this) {
final List<UserInfo> users = mUserManager.getUsers();
@@ -10216,7 +10229,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = UserHandle.getUserId(uid);
Intent intent = null;
if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
- DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+ DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction) ||
+ DevicePolicyManager.POLICY_MANDATORY_BACKUPS.equals(restriction)) {
synchronized(this) {
final DevicePolicyData policy = getUserData(userId);
final int N = policy.mAdminList.size();
@@ -10225,7 +10239,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if ((admin.disableCamera &&
DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
(admin.disableScreenCapture && DevicePolicyManager
- .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
+ .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) ||
+ (admin.mandatoryBackupTransport != null && DevicePolicyManager
+ .POLICY_MANDATORY_BACKUPS.equals(restriction))) {
intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
break;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e660c50fcbc1..94a356e65878 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1172,6 +1172,15 @@ public final class SystemServer {
}
traceEnd();
+ traceBeginAndSlog("StartSystemUpdateManagerService");
+ try {
+ ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE,
+ new SystemUpdateManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting SystemUpdateManagerService", e);
+ }
+ traceEnd();
+
traceBeginAndSlog("StartUpdateLockService");
try {
ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index 429dd8fd1d3d..de54e52d4d0a 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -42,6 +42,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager.ServiceType;
@@ -51,6 +52,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
@@ -105,6 +107,9 @@ public class ForceAppStandbyTrackerTest {
PowerManagerInternal injectPowerManagerInternal() {
return mMockPowerManagerInternal;
}
+
+ @Override
+ boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
}
private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -144,6 +149,7 @@ public class ForceAppStandbyTrackerTest {
private FakeSettingsProvider mFakeSettingsProvider;
private boolean mPowerSaveMode;
+ private boolean mIsSmallBatteryDevice;
private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
@@ -232,6 +238,7 @@ public class ForceAppStandbyTrackerTest {
assertNotNull(mAppOpsCallback);
assertNotNull(mPowerSaveObserver);
assertNotNull(mReceiver);
+ assertNotNull(instance.mFlagsObserver);
}
private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -852,6 +859,56 @@ public class ForceAppStandbyTrackerTest {
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
}
+ @Test
+ public void testSmallBatteryAndPluggedIn() throws Exception {
+ // This is a small battery device
+ mIsSmallBatteryDevice = true;
+
+ final ForceAppStandbyTrackerTestable instance = newInstance();
+ callStart(instance);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // Setting/experiment for all app standby for small battery is enabled
+ Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1);
+ instance.mFlagsObserver.onChange(true,
+ Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED));
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery is plugged in, force app standby is disabled
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+ mReceiver.onReceive(mMockContext, intent);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery stops plugged in, force app standby is enabled
+ mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+ }
+
+ @Test
+ public void testNotSmallBatteryAndPluggedIn() throws Exception {
+ // Not a small battery device, so plugged in status should not affect forced app standby
+ mIsSmallBatteryDevice = false;
+
+ final ForceAppStandbyTrackerTestable instance = newInstance();
+ callStart(instance);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ mPowerSaveMode = true;
+ mPowerSaveObserver.accept(getPowerSaveState());
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery is plugged in, force app standby is unaffected
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+ mReceiver.onReceive(mMockContext, intent);
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery stops plugged in, force app standby is unaffected
+ mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+ }
+
static int[] array(int... appIds) {
Arrays.sort(appIds);
return appIds;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
index 1213e814b3fa..e72e4601bbe8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
@@ -16,13 +16,18 @@
package com.android.server.accessibility;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
import android.app.StatusBarManager;
import android.content.Context;
+import android.os.Handler;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
@@ -39,6 +44,7 @@ public class GlobalActionPerformerTest {
@Mock Context mMockContext;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock StatusBarManager mMockStatusBarManager;
+ @Mock ScreenshotHelper mMockScreenshotHelper;
@Before
public void setup() {
@@ -48,7 +54,8 @@ public class GlobalActionPerformerTest {
.thenReturn(mMockStatusBarManager);
mGlobalActionPerformer =
- new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal);
+ new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal,
+ () -> mMockScreenshotHelper);
}
@Test
@@ -70,4 +77,13 @@ public class GlobalActionPerformerTest {
mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
verify(mMockWindowManagerInternal).showGlobalActions();
}
+
+ @Test
+ public void testScreenshot_requestsFromScreenshotHelper() {
+ mGlobalActionPerformer.performGlobalAction(
+ AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
+ verify(mMockScreenshotHelper).takeScreenshot(
+ eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
+ anyBoolean(), any(Handler.class));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5134f523a8ff..06f138ba898b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -45,6 +45,7 @@ import android.view.IWindowManager;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.net.NetworkPolicyManagerInternal;
import java.io.File;
import java.io.IOException;
@@ -159,6 +160,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
}
@Override
+ NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
+ return services.networkPolicyManagerInternal;
+ }
+
+ @Override
PackageManagerInternal getPackageManagerInternal() {
return services.packageManagerInternal;
}
@@ -438,5 +444,10 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
KeyChain.KeyChainConnection keyChainBindAsUser(UserHandle user) {
return services.keyChainConnection;
}
+
+ @Override
+ void postOnSystemServerInitThreadPool(Runnable runnable) {
+ runnable.run();
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 1df0ff23f847..725ede81754a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -186,6 +186,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
initializeDpms();
Mockito.reset(getServices().usageStatsManagerInternal);
+ Mockito.reset(getServices().networkPolicyManagerInternal);
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
@@ -211,7 +212,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
- dpms.handleStart();
dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
@@ -283,7 +283,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertNull(LocalServices.getService(DevicePolicyManagerInternal.class));
}
- public void testHandleStart() throws Exception {
+ public void testLoadAdminData() throws Exception {
// Device owner in SYSTEM_USER
setDeviceOwner();
// Profile owner in CALLER_USER_HANDLE
@@ -307,6 +307,23 @@ public class DevicePolicyManagerTest extends DpmTestBase {
MockUtils.checkAdminApps(admin2.getPackageName(),
adminAnotherPackage.getPackageName()),
eq(DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).onAdminDataAvailable();
+ verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable();
+ }
+
+ public void testLoadAdminData_noAdmins() throws Exception {
+ final int ANOTHER_USER_ID = 15;
+ getServices().addUser(ANOTHER_USER_ID, 0);
+
+ initializeDpms();
+
+ // Verify
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, ANOTHER_USER_ID);
+ verify(getServices().usageStatsManagerInternal).onAdminDataAvailable();
+ verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable();
}
/**
@@ -2119,8 +2136,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
- // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not
- // user restrictions
+ // Try with POLICY_DISABLE_CAMERA, POLICY_DISABLE_SCREEN_CAPTURE and
+ // POLICY_MANDATORY_BACKUPS, which are not user restrictions
// Camera is not disabled
intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
@@ -2144,6 +2161,19 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+ // Backups are not mandatory
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
+ assertNull(intent);
+
+ // Backups are mandatory
+ ComponentName transportComponent = ComponentName.unflattenFromString(
+ "android/com.android.internal.backup.LocalTransport");
+ dpm.setMandatoryBackupTransport(admin1, transportComponent);
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
+ assertNotNull(intent);
+ assertEquals(DevicePolicyManager.POLICY_MANDATORY_BACKUPS,
+ intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
// Same checks for different user
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
// Camera should be disabled by device owner
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 268d424734e2..0343a52e05da 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -58,6 +58,7 @@ import android.view.IWindowManager;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.net.NetworkPolicyManagerInternal;
import java.io.File;
import java.io.IOException;
@@ -76,6 +77,7 @@ public class MockSystemServices {
public final UserManager userManager;
public final UserManagerInternal userManagerInternal;
public final UsageStatsManagerInternal usageStatsManagerInternal;
+ public final NetworkPolicyManagerInternal networkPolicyManagerInternal;
public final PackageManagerInternal packageManagerInternal;
public final UserManagerForMock userManagerForMock;
public final PowerManagerForMock powerManager;
@@ -113,6 +115,8 @@ public class MockSystemServices {
userManager = mock(UserManager.class);
userManagerInternal = mock(UserManagerInternal.class);
usageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
+ networkPolicyManagerInternal = mock(NetworkPolicyManagerInternal.class);
+
userManagerForMock = mock(UserManagerForMock.class);
packageManagerInternal = mock(PackageManagerInternal.class);
powerManager = mock(PowerManagerForMock.class);
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
index a31b46ce5534..999dce51bd9c 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
@@ -77,9 +77,9 @@ public class PrivacyUtilsTests {
assertEquals(6, result.size());
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"));
- assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
- assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
- assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
+ assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
+ assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
+ assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"));
}
@@ -87,7 +87,7 @@ public class PrivacyUtilsTests {
public void testPrivacyUtils_createInsecureDPEncoderForTest() throws Exception {
DifferentialPrivacyEncoder encoder = PrivacyUtils.createInsecureDPEncoderForTest("foo");
assertEquals(
- "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, "
+ "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, "
+ "ProbabilityQ: 1.000",
encoder.getConfig().toString());
assertTrue(encoder.isInsecureEncoderForTest());
@@ -97,7 +97,7 @@ public class PrivacyUtilsTests {
public void testPrivacyUtils_createSecureDPEncoderTest() throws Exception {
DifferentialPrivacyEncoder encoder = PrivacyUtils.createSecureDPEncoder(TEST_SECRET, "foo");
assertEquals(
- "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, "
+ "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, "
+ "ProbabilityQ: 1.000",
encoder.getConfig().toString());
assertFalse(encoder.isInsecureEncoderForTest());
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
index 851d2c62e87e..654acc20328d 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
@@ -33,12 +33,15 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.PrintWriter;
import java.util.Arrays;
+
/**
* runtest frameworks-services -c com.android.server.net.watchlist.WatchlistConfigTests
*/
@@ -117,6 +120,15 @@ public class WatchlistConfigTests {
assertEquals(TEST_XML_1_HASH, HexDump.toHexString(config.getWatchlistConfigHash()));
}
+ @Test
+ public void testWatchlistConfig_testDumpDoesNotCrash() throws Exception {
+ WatchlistConfig config = new WatchlistConfig(new File("/not_exist_path.xml"));
+ ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
+ PrintWriter pw = new PrintWriter(bs);
+ // Make sure dump still works even watchlist does not exist
+ config.dump(null, pw, null);
+ }
+
private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile)
throws IOException {
writeToFile(outFile, readAsset(context, xmlAsset));
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index b6c370eb6b08..293f9afeea22 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -27,14 +27,19 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.DisplayCutout;
import android.view.WindowManager;
import org.junit.Before;
@@ -262,4 +267,23 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
}
+ @Test
+ public void insetHint_screenDecorWindow() {
+ addDisplayCutout();
+ mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ final Rect content = new Rect();
+ final Rect stable = new Rect();
+ final Rect outsets = new Rect();
+ final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
+ mPolicy.getInsetHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, content,
+ stable, outsets, cutout);
+
+ assertThat(content, equalTo(new Rect()));
+ assertThat(stable, equalTo(new Rect()));
+ assertThat(outsets, equalTo(new Rect()));
+ assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT));
+ }
} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index 6468763440a5..5f44fb675330 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -17,14 +17,17 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -74,11 +77,11 @@ public class ZOrderingTests extends WindowTestsBase {
return super.setRelativeLayer(sc, relativeTo, layer);
}
- int getLayer(SurfaceControl sc) {
+ private int getLayer(SurfaceControl sc) {
return mLayersForControl.getOrDefault(sc, 0);
}
- SurfaceControl getRelativeLayer(SurfaceControl sc) {
+ private SurfaceControl getRelativeLayer(SurfaceControl sc) {
return mRelativeLayersForControl.get(sc);
}
};
@@ -146,8 +149,9 @@ public class ZOrderingTests extends WindowTestsBase {
return p;
}
- void assertZOrderGreaterThan(LayerRecordingTransaction t,
- SurfaceControl left, SurfaceControl right) throws Exception {
+
+ void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left,
+ SurfaceControl right) throws Exception {
final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
@@ -171,9 +175,12 @@ public class ZOrderingTests extends WindowTestsBase {
}
}
- void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
- WindowState left, WindowState right) throws Exception {
- assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+ void assertWindowHigher(WindowState left, WindowState right) throws Exception {
+ assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl());
+ }
+
+ WindowState createWindow(String name) {
+ return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name);
}
@Test
@@ -184,38 +191,37 @@ public class ZOrderingTests extends WindowTestsBase {
// The Ime has an higher base layer than app windows and lower base layer than system
// windows, so it should be above app windows and below system windows if there isn't an IME
// target.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTarget = createWindow("imeAppTarget");
sWm.mInputMethodTarget = imeAppTarget;
+
mDisplayContent.assignChildLayers(mTransaction);
// Ime should be above all app windows and below system windows if it is targeting an app
// window.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, imeAppTarget);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTarget = createWindow("imeAppTarget");
final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
"imeAppTargetChildAboveWindow");
@@ -228,41 +234,38 @@ public class ZOrderingTests extends WindowTestsBase {
// Ime should be above all app windows except for child windows that are z-ordered above it
// and below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, imeAppTarget);
+ assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
- final WindowState appBelowImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState appAboveImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+ final WindowState appBelowImeTarget = createWindow("appBelowImeTarget");
+ final WindowState imeAppTarget = createWindow("imeAppTarget");
+ final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
sWm.mInputMethodTarget = imeAppTarget;
mDisplayContent.assignChildLayers(mTransaction);
// Ime should be above all app windows except for non-fullscreen app window above it and
// below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
- assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mImeWindow, imeAppTarget);
+ assertWindowHigher(mImeWindow, appBelowImeTarget);
+ assertWindowHigher(appAboveImeTarget, mImeWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
@@ -276,20 +279,20 @@ public class ZOrderingTests extends WindowTestsBase {
// The IME target base layer is higher than all window except for the nav bar window, so the
// IME should be above all windows except for the nav bar.
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowHigher(mImeWindow, imeSystemOverlayTarget);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mImeWindow, mDockedDividerWindow);
// The IME has a higher base layer than the status bar so we may expect it to go
// above the status bar once they are both in the Non-App layer, as past versions of this
// test enforced. However this seems like the wrong behavior unless the status bar is the
// IME target.
- assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+ assertWindowHigher(mNavBarWindow, mImeWindow);
+ assertWindowHigher(mStatusBarWindow, mImeWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
@@ -297,17 +300,18 @@ public class ZOrderingTests extends WindowTestsBase {
sWm.mInputMethodTarget = mStatusBarWindow;
mDisplayContent.assignChildLayers(mTransaction);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+ assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+ assertWindowHigher(mImeWindow, mAppWindow);
+ assertWindowHigher(mImeWindow, mDockedDividerWindow);
+ assertWindowHigher(mImeWindow, mStatusBarWindow);
// And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ assertWindowHigher(mImeDialogWindow, mImeWindow);
}
@Test
public void testStackLayers() throws Exception {
+ final WindowState anyWindow1 = createWindow("anyWindow");
final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
"pinnedStackWindow");
@@ -317,12 +321,22 @@ public class ZOrderingTests extends WindowTestsBase {
final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
mDisplayContent, "assistantStackWindow");
+ final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION,
+ mDisplayContent, "homeActivityWindow");
+ final WindowState anyWindow2 = createWindow("anyWindow2");
mDisplayContent.assignChildLayers(mTransaction);
- assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
- assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+ assertWindowHigher(dockedStackWindow, homeActivityWindow);
+ assertWindowHigher(assistantStackWindow, homeActivityWindow);
+ assertWindowHigher(pinnedStackWindow, homeActivityWindow);
+ assertWindowHigher(anyWindow1, homeActivityWindow);
+ assertWindowHigher(anyWindow2, homeActivityWindow);
+ assertWindowHigher(pinnedStackWindow, anyWindow1);
+ assertWindowHigher(pinnedStackWindow, anyWindow2);
+ assertWindowHigher(pinnedStackWindow, dockedStackWindow);
+ assertWindowHigher(pinnedStackWindow, assistantStackWindow);
}
@Test
@@ -337,9 +351,9 @@ public class ZOrderingTests extends WindowTestsBase {
// Ime should be above all app windows and below system windows if it is targeting an app
// window.
- assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow);
- assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow);
- assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel);
+ assertWindowHigher(navBarPanel, mNavBarWindow);
+ assertWindowHigher(statusBarPanel, mStatusBarWindow);
+ assertWindowHigher(statusBarSubPanel, statusBarPanel);
}
@Test
@@ -347,8 +361,7 @@ public class ZOrderingTests extends WindowTestsBase {
// TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
// then we can drop all negative layering on the windowing side.
- final WindowState anyWindow =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow");
+ final WindowState anyWindow = createWindow("anyWindow");
final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
"TypeApplicationMediaChild");
final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
@@ -356,7 +369,29 @@ public class ZOrderingTests extends WindowTestsBase {
mDisplayContent.assignChildLayers(mTransaction);
- assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild);
- assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child);
+ assertWindowHigher(anyWindow, mediaOverlayChild);
+ assertWindowHigher(mediaOverlayChild, child);
+ }
+
+ @Test
+ public void testDockedDividerPosition() throws Exception {
+ final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+ "pinnedStackWindow");
+ final WindowState splitScreenWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "splitScreenWindow");
+ final WindowState splitScreenSecondaryWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+ TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
+ final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+ mDisplayContent, "assistantStackWindow");
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowHigher(mDockedDividerWindow, splitScreenWindow);
+ assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow);
+ assertWindowHigher(pinnedStackWindow, mDockedDividerWindow);
}
}
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index f022dcf376a6..347557291427 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index ce328c29f01c..d3bb80485dfb 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -21,8 +22,11 @@ import android.app.slice.SliceSpec;
import android.content.ContentProvider;
import android.content.IContentProvider;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -34,6 +38,7 @@ import com.android.server.UiServiceTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -147,9 +152,10 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
@Test
public void testListenerPin() {
ISliceListener listener = mock(ISliceListener.class);
+ when(listener.asBinder()).thenReturn(new Binder());
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
assertTrue(mPinnedSliceManager.isPinned());
assertTrue(mPinnedSliceManager.removeSliceListener(listener));
@@ -159,12 +165,16 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
@Test
public void testMultiListenerPin() {
ISliceListener listener = mock(ISliceListener.class);
+ Binder value = new Binder();
+ when(listener.asBinder()).thenReturn(value);
ISliceListener listener2 = mock(ISliceListener.class);
+ Binder value2 = new Binder();
+ when(listener2.asBinder()).thenReturn(value2);
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
assertTrue(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener2, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener2, mContext.getPackageName(), FIRST_SPECS);
assertFalse(mPinnedSliceManager.removeSliceListener(listener));
assertTrue(mPinnedSliceManager.removeSliceListener(listener2));
@@ -172,11 +182,33 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
}
@Test
+ public void testListenerDeath() throws RemoteException {
+ ISliceListener listener = mock(ISliceListener.class);
+ IBinder binder = mock(IBinder.class);
+ when(binder.isBinderAlive()).thenReturn(true);
+ when(listener.asBinder()).thenReturn(binder);
+ assertFalse(mPinnedSliceManager.isPinned());
+
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
+ assertTrue(mPinnedSliceManager.isPinned());
+
+ ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class);
+ verify(binder).linkToDeath(arg.capture(), anyInt());
+
+ when(binder.isBinderAlive()).thenReturn(false);
+ arg.getValue().binderDied();
+
+ verify(mSliceService).removePinnedSlice(eq(TEST_URI));
+ assertFalse(mPinnedSliceManager.isPinned());
+ }
+
+ @Test
public void testPkgListenerPin() {
ISliceListener listener = mock(ISliceListener.class);
+ when(listener.asBinder()).thenReturn(new Binder());
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
assertTrue(mPinnedSliceManager.isPinned());
mPinnedSliceManager.pin("pkg", FIRST_SPECS);
@@ -191,6 +223,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
clearInvocations(mIContentProvider);
ISliceListener listener = mock(ISliceListener.class);
+ when(listener.asBinder()).thenReturn(new Binder());
Slice s = new Slice.Builder(TEST_URI).build();
Bundle b = new Bundle();
b.putParcelable(SliceProvider.EXTRA_SLICE, s);
@@ -199,7 +232,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
assertFalse(mPinnedSliceManager.isPinned());
- mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
mPinnedSliceManager.onChange();
TestableLooper.get(this).processAllMessages();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index ff3d58652aed..6782188c0f34 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -78,6 +78,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
@@ -90,6 +91,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
/**
* Manages the standby state of an app, listening to various events.
@@ -128,6 +130,11 @@ public class AppStandbyController {
// Expiration time for predicted bucket
private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+ /**
+ * Indicates the maximum wait time for admin data to be available;
+ */
+ private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+
// To name the lock for stack traces
static class Lock {}
@@ -153,6 +160,8 @@ public class AppStandbyController {
@GuardedBy("mActiveAdminApps")
private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
+ private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+
// Messages for the handler
static final int MSG_INFORM_LISTENERS = 3;
static final int MSG_FORCE_IDLE_STATE = 4;
@@ -895,6 +904,20 @@ public class AppStandbyController {
}
}
+ public void onAdminDataAvailable() {
+ mAdminDataAvailableLatch.countDown();
+ }
+
+ /**
+ * This will only ever be called once - during device boot.
+ */
+ private void waitForAdminData() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+ ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
+ WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
+ }
+ }
+
Set<String> getActiveAdminAppsForTest(int userId) {
synchronized (mActiveAdminApps) {
return mActiveAdminApps.get(userId);
@@ -1224,6 +1247,7 @@ public class AppStandbyController {
case MSG_ONE_TIME_CHECK_IDLE_STATES:
mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ waitForAdminData();
checkIdleStates(UserHandle.USER_ALL);
break;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 78cc81f2bab6..979feaa5a165 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1030,5 +1030,10 @@ public class UsageStatsService extends SystemService implements
public void setActiveAdminApps(Set<String> packageNames, int userId) {
mAppStandby.setActiveAdminApps(packageNames, userId);
}
+
+ @Override
+ public void onAdminDataAvailable() {
+ mAppStandby.onAdminDataAvailable();
+ }
}
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 8c7d6b30ecdc..d17bdc85eb22 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -419,7 +419,6 @@ public final class Call {
/**
* Indicates the call used Assisted Dialing.
* See also {@link Connection#PROPERTY_ASSISTED_DIALING_USED}
- * @hide
*/
public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index aaef8d3d3856..75224434bc1c 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -35,6 +35,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -401,7 +402,6 @@ public abstract class Connection extends Conferenceable {
/**
* Set by the framework to indicate that a connection is using assisted dialing.
- * @hide
*/
public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
@@ -2538,6 +2538,19 @@ public abstract class Connection extends Conferenceable {
}
/**
+ * Adds a parcelable extra to this {@code Connection}.
+ *
+ * @param key The extra key.
+ * @param value The value.
+ * @hide
+ */
+ public final void putExtra(@NonNull String key, @NonNull Parcelable value) {
+ Bundle newExtras = new Bundle();
+ newExtras.putParcelable(key, value);
+ putExtras(newExtras);
+ }
+
+ /**
* Removes extras from this {@code Connection}.
*
* @param keys The keys of the extras to remove.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d292db3efc82..91d5da30935b 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -602,12 +602,17 @@ public class TelecomManager {
/**
* The boolean indicated by this extra controls whether or not a call is eligible to undergo
* assisted dialing. This extra is stored under {@link #EXTRA_OUTGOING_CALL_EXTRAS}.
- * @hide
*/
public static final String EXTRA_USE_ASSISTED_DIALING =
"android.telecom.extra.USE_ASSISTED_DIALING";
/**
+ * The bundle indicated by this extra store information related to the assisted dialing action.
+ */
+ public static final String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO =
+ "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO";
+
+ /**
* The following 4 constants define how properties such as phone numbers and names are
* displayed to the user.
*/
diff --git a/telecomm/java/android/telecom/TransformationInfo.java b/telecomm/java/android/telecom/TransformationInfo.java
new file mode 100755
index 000000000000..3e848c6f2f80
--- /dev/null
+++ b/telecomm/java/android/telecom/TransformationInfo.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A container class to hold information related to the Assisted Dialing operation. All member
+ * variables must be set when constructing a new instance of this class.
+ */
+public final class TransformationInfo implements Parcelable {
+ private String mOriginalNumber;
+ private String mTransformedNumber;
+ private String mUserHomeCountryCode;
+ private String mUserRoamingCountryCode;
+ private int mTransformedNumberCountryCallingCode;
+
+ public TransformationInfo(String originalNumber,
+ String transformedNumber,
+ String userHomeCountryCode,
+ String userRoamingCountryCode,
+ int transformedNumberCountryCallingCode) {
+ String missing = "";
+ if (originalNumber == null) {
+ missing += " mOriginalNumber";
+ }
+ if (transformedNumber == null) {
+ missing += " mTransformedNumber";
+ }
+ if (userHomeCountryCode == null) {
+ missing += " mUserHomeCountryCode";
+ }
+ if (userRoamingCountryCode == null) {
+ missing += " mUserRoamingCountryCode";
+ }
+
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ this.mOriginalNumber = originalNumber;
+ this.mTransformedNumber = transformedNumber;
+ this.mUserHomeCountryCode = userHomeCountryCode;
+ this.mUserRoamingCountryCode = userRoamingCountryCode;
+ this.mTransformedNumberCountryCallingCode = transformedNumberCountryCallingCode;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mOriginalNumber);
+ out.writeString(mTransformedNumber);
+ out.writeString(mUserHomeCountryCode);
+ out.writeString(mUserRoamingCountryCode);
+ out.writeInt(mTransformedNumberCountryCallingCode);
+ }
+
+ public static final Parcelable.Creator<TransformationInfo> CREATOR
+ = new Parcelable.Creator<TransformationInfo>() {
+ public TransformationInfo createFromParcel(Parcel in) {
+ return new TransformationInfo(in);
+ }
+
+ public TransformationInfo[] newArray(int size) {
+ return new TransformationInfo[size];
+ }
+ };
+
+ private TransformationInfo(Parcel in) {
+ mOriginalNumber = in.readString();
+ mTransformedNumber = in.readString();
+ mUserHomeCountryCode = in.readString();
+ mUserRoamingCountryCode = in.readString();
+ mTransformedNumberCountryCallingCode = in.readInt();
+ }
+
+ /**
+ * The original number that underwent Assisted Dialing.
+ */
+ public String getOriginalNumber() {
+ return mOriginalNumber;
+ }
+
+ /**
+ * The number after it underwent Assisted Dialing.
+ */
+ public String getTransformedNumber() {
+ return mTransformedNumber;
+ }
+
+ /**
+ * The user's home country code that was used when attempting to transform the number.
+ */
+ public String getUserHomeCountryCode() {
+ return mUserHomeCountryCode;
+ }
+
+ /**
+ * The users's roaming country code that was used when attempting to transform the number.
+ */
+ public String getUserRoamingCountryCode() {
+ return mUserRoamingCountryCode;
+ }
+
+ /**
+ * The country calling code that was used in the transformation.
+ */
+ public int getTransformedNumberCountryCallingCode() {
+ return mTransformedNumberCountryCallingCode;
+ }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ce0b551311b0..649d4783cc7a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1644,6 +1644,13 @@ public class CarrierConfigManager {
"roaming_operator_string_array";
/**
+ * Controls whether Assisted Dialing is enabled and the preference is shown. This feature
+ * transforms numbers when the user is roaming.
+ */
+ public static final String KEY_ASSISTED_DIALING_ENABLED_BOOL =
+ "assisted_dialing_enabled_bool";
+
+ /**
* URL from which the proto containing the public key of the Carrier used for
* IMSI encryption will be downloaded.
* @hide
@@ -2040,6 +2047,7 @@ public class CarrierConfigManager {
false);
sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_ASSISTED_DIALING_ENABLED_BOOL, true);
sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4e1c15fa1251..38408fe3d0fd 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -126,14 +126,31 @@ public class SubscriptionInfo implements Parcelable {
private UiccAccessRule[] mAccessRules;
/**
+ * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
+ * for an eUICC card.
+ */
+ private String mCardId;
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, int mcc, int mnc, String countryIso) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+ null /* accessRules */, null /* accessRules */);
+ }
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
- Bitmap icon, int mcc, int mnc, String countryIso) {
+ Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] accessRules) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
- null /* accessRules */);
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */);
}
/**
@@ -142,7 +159,7 @@ public class SubscriptionInfo implements Parcelable {
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules) {
+ @Nullable UiccAccessRule[] accessRules, String cardId) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -158,6 +175,7 @@ public class SubscriptionInfo implements Parcelable {
this.mCountryIso = countryIso;
this.mIsEmbedded = isEmbedded;
this.mAccessRules = accessRules;
+ this.mCardId = cardId;
}
/**
@@ -387,6 +405,14 @@ public class SubscriptionInfo implements Parcelable {
return mAccessRules;
}
+ /**
+ * @return the ID of the SIM card which contains the subscription.
+ * @hide
+ */
+ public String getCardId() {
+ return this.mCardId;
+ }
+
public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
@Override
public SubscriptionInfo createFromParcel(Parcel source) {
@@ -405,10 +431,11 @@ public class SubscriptionInfo implements Parcelable {
Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
boolean isEmbedded = source.readBoolean();
UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+ String cardId = source.readString();
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules);
+ isEmbedded, accessRules, cardId);
}
@Override
@@ -434,6 +461,7 @@ public class SubscriptionInfo implements Parcelable {
mIconBitmap.writeToParcel(dest, flags);
dest.writeBoolean(mIsEmbedded);
dest.writeTypedArray(mAccessRules, flags);
+ dest.writeString(mCardId);
}
@Override
@@ -459,11 +487,13 @@ public class SubscriptionInfo implements Parcelable {
@Override
public String toString() {
String iccIdToPrint = givePrintableIccid(mIccId);
+ String cardIdToPrint = givePrintableIccid(mCardId);
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
+ " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ " mnc " + mMnc + " isEmbedded " + mIsEmbedded
- + " accessRules " + Arrays.toString(mAccessRules) + "}";
+ + " accessRules " + Arrays.toString(mAccessRules)
+ + " cardId=" + cardIdToPrint + "}";
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 14060935f4ff..57f4cf28d90d 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -284,6 +284,14 @@ public class SubscriptionManager {
public static final String IS_EMBEDDED = "is_embedded";
/**
+ * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
+ * current enabled profile on the card, while for eUICC card it is the EID of the card.
+ * <P>Type: TEXT (String)</P>
+ * @hide
+ */
+ public static final String CARD_ID = "card_id";
+
+ /**
* TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
* {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
* <p>TYPE: BLOB
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index de9e691eadcf..17f809d3863a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1024,8 +1024,8 @@ public class TelephonyManager {
/**
* An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
- * the updated carrier id {@link TelephonyManager#getSubscriptionCarrierId()} of the current
- * subscription.
+ * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+ * the current subscription.
* <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
* the carrier cannot be identified.
*/
@@ -6900,14 +6900,19 @@ public class TelephonyManager {
/**
* Returns carrier id of the current subscription.
- * <p>To recognize a carrier (including MVNO) as a first class identity, assign each carrier
- * with a canonical integer a.k.a carrier id.
+ * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
+ * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an
+ * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+ * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ *
+ * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
+ * as an Android platform-wide identifier for carriers.
*
* @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
* subscription is unavailable or the carrier cannot be identified.
* @throws IllegalStateException if telephony service is unavailable.
*/
- public int getSubscriptionCarrierId() {
+ public int getAndroidCarrierIdForSubscription() {
try {
ITelephony service = getITelephony();
return service.getSubscriptionCarrierId(getSubId());
@@ -6923,17 +6928,18 @@ public class TelephonyManager {
/**
* Returns carrier name of the current subscription.
- * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId()},
- * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
- * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
- * Carrier name is not a canonical identity, use {@link #getSubscriptionCarrierId()} instead.
+ * <p>Carrier name is a user-facing name of carrier id
+ * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+ * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
+ * should have a single carrier name. Carrier name is not a canonical identity,
+ * use {@link #getAndroidCarrierIdForSubscription()} instead.
* <p>The returned carrier name is unlocalized.
*
* @return Carrier name of the current subscription. Return {@code null} if the subscription is
* unavailable or the carrier cannot be identified.
* @throws IllegalStateException if telephony service is unavailable.
*/
- public String getSubscriptionCarrierName() {
+ public CharSequence getAndroidCarrierNameForSubscription() {
try {
ITelephony service = getITelephony();
return service.getSubscriptionCarrierName(getSubId());
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 2ab8d4fb900e..73a05af8e56e 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -21,22 +21,23 @@ import android.annotation.StringDef;
import android.content.ContentValues;
import android.database.Cursor;
import android.hardware.radio.V1_0.ApnTypes;
-import android.net.NetworkUtils;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony;
import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
import java.net.MalformedURLException;
-import java.net.UnknownHostException;
import java.net.URL;
-import java.net.InetAddress;
-import java.util.Arrays;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -67,8 +68,8 @@ public class ApnSetting implements Parcelable {
private final int mMtu;
private final boolean mCarrierEnabled;
- private final int mBearer;
- private final int mBearerBitmask;
+
+ private final int mNetworkTypeBitmask;
private final int mProfileId;
@@ -103,34 +104,6 @@ public class ApnSetting implements Parcelable {
}
/**
- * Radio Access Technology info.
- * To check what values can hold, refer to ServiceState.java.
- * This should be spread to other technologies,
- * but currently only used for LTE(14) and EHRPD(13).
- *
- * @return the bearer info of the APN
- * @hide
- */
- public int getBearer() {
- return mBearer;
- }
-
- /**
- * Returns the radio access technology bitmask for this APN.
- *
- * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
- * technologies in ServiceState.
- * This should be spread to other technologies,
- * but currently only used for LTE(14) and EHRPD(13).
- *
- * @return the radio access technology bitmask
- * @hide
- */
- public int getBearerBitmask() {
- return mBearerBitmask;
- }
-
- /**
* Returns the profile id to which the APN saved in modem.
*
* @return the profile id of the APN
@@ -411,6 +384,20 @@ public class ApnSetting implements Parcelable {
return mCarrierEnabled;
}
+ /**
+ * Returns a bitmask describing the Radio Technologies(Network Types) which this APN may use.
+ *
+ * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+ *
+ * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+ * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+ *
+ * @return a bitmask describing the Radio Technologies(Network Types)
+ */
+ public int getNetworkTypeBitmask() {
+ return mNetworkTypeBitmask;
+ }
+
/** @hide */
@StringDef({
MVNO_TYPE_SPN,
@@ -452,8 +439,7 @@ public class ApnSetting implements Parcelable {
this.mRoamingProtocol = builder.mRoamingProtocol;
this.mMtu = builder.mMtu;
this.mCarrierEnabled = builder.mCarrierEnabled;
- this.mBearer = builder.mBearer;
- this.mBearerBitmask = builder.mBearerBitmask;
+ this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
this.mProfileId = builder.mProfileId;
this.mModemCognitive = builder.mModemCognitive;
this.mMaxConns = builder.mMaxConns;
@@ -467,8 +453,8 @@ public class ApnSetting implements Parcelable {
public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
int mmsPort, String user, String password, int authType, List<String> types,
- String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
- int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
+ String protocol, String roamingProtocol, boolean carrierEnabled,
+ int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
return new Builder()
.setId(id)
@@ -487,8 +473,7 @@ public class ApnSetting implements Parcelable {
.setProtocol(protocol)
.setRoamingProtocol(roamingProtocol)
.setCarrierEnabled(carrierEnabled)
- .setBearer(bearer)
- .setBearerBitmask(bearerBitmask)
+ .setNetworkTypeBitmask(networkTypeBitmask)
.setProfileId(profileId)
.setModemCognitive(modemCognitive)
.setMaxConns(maxConns)
@@ -504,6 +489,14 @@ public class ApnSetting implements Parcelable {
public static ApnSetting makeApnSetting(Cursor cursor) {
String[] types = parseTypes(
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+ int networkTypeBitmask = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
+ if (networkTypeBitmask == 0) {
+ final int bearerBitmask = cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.BEARER_BITMASK));
+ networkTypeBitmask =
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
+ }
return makeApnSetting(
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
@@ -529,9 +522,7 @@ public class ApnSetting implements Parcelable {
Telephony.Carriers.ROAMING_PROTOCOL)),
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.CARRIER_ENABLED)) == 1,
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
- cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.BEARER_BITMASK)),
+ networkTypeBitmask,
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.MODEM_COGNITIVE)) == 1,
@@ -551,7 +542,7 @@ public class ApnSetting implements Parcelable {
return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
- apn.mCarrierEnabled, apn.mBearer, apn.mBearerBitmask, apn.mProfileId,
+ apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
apn.mMvnoType, apn.mMvnoMatchData);
}
@@ -559,7 +550,7 @@ public class ApnSetting implements Parcelable {
/** @hide */
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("[ApnSettingV3] ")
+ sb.append("[ApnSettingV4] ")
.append(mEntryName)
.append(", ").append(mId)
.append(", ").append(mOperatorNumeric)
@@ -579,8 +570,6 @@ public class ApnSetting implements Parcelable {
sb.append(", ").append(mProtocol);
sb.append(", ").append(mRoamingProtocol);
sb.append(", ").append(mCarrierEnabled);
- sb.append(", ").append(mBearer);
- sb.append(", ").append(mBearerBitmask);
sb.append(", ").append(mProfileId);
sb.append(", ").append(mModemCognitive);
sb.append(", ").append(mMaxConns);
@@ -590,6 +579,7 @@ public class ApnSetting implements Parcelable {
sb.append(", ").append(mMvnoType);
sb.append(", ").append(mMvnoMatchData);
sb.append(", ").append(mPermanentFailed);
+ sb.append(", ").append(mNetworkTypeBitmask);
return sb.toString();
}
@@ -678,8 +668,6 @@ public class ApnSetting implements Parcelable {
&& Objects.equals(mProtocol, other.mProtocol)
&& Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
&& Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
- && Objects.equals(mBearer, other.mBearer)
- && Objects.equals(mBearerBitmask, other.mBearerBitmask)
&& Objects.equals(mProfileId, other.mProfileId)
&& Objects.equals(mModemCognitive, other.mModemCognitive)
&& Objects.equals(mMaxConns, other.mMaxConns)
@@ -687,13 +675,14 @@ public class ApnSetting implements Parcelable {
&& Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
&& Objects.equals(mMtu, other.mMtu)
&& Objects.equals(mMvnoType, other.mMvnoType)
- && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+ && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+ && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask);
}
/**
* Compare two APN settings
*
- * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+ * Note: This method does not compare 'mId', 'mNetworkTypeBitmask'. We only use this for
* determining if tearing a data call is needed when conditions change. See
* cleanUpConnectionsOnUpdatedApns in DcTracker.
*
@@ -752,13 +741,13 @@ public class ApnSetting implements Parcelable {
&& xorEquals(this.mProtocol, other.mProtocol)
&& xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
&& Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
- && Objects.equals(this.mBearerBitmask, other.mBearerBitmask)
&& Objects.equals(this.mProfileId, other.mProfileId)
&& Objects.equals(this.mMvnoType, other.mMvnoType)
&& Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
&& xorEqualsURL(this.mMmsc, other.mMmsc)
&& xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
- && xorEqualsPort(this.mMmsPort, other.mMmsPort));
+ && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+ && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
}
// Equal or one is not specified.
@@ -808,53 +797,33 @@ public class ApnSetting implements Parcelable {
return TextUtils.join(",", types);
}
+ private String nullToEmpty(String stringValue) {
+ return stringValue == null ? "" : stringValue;
+ }
+
/** @hide */
// Called by DPM.
public ContentValues toContentValues() {
ContentValues apnValue = new ContentValues();
- if (mOperatorNumeric != null) {
- apnValue.put(Telephony.Carriers.NUMERIC, mOperatorNumeric);
- }
- if (mEntryName != null) {
- apnValue.put(Telephony.Carriers.NAME, mEntryName);
- }
- if (mApnName != null) {
- apnValue.put(Telephony.Carriers.APN, mApnName);
- }
- if (mProxy != null) {
- apnValue.put(Telephony.Carriers.PROXY, inetAddressToString(mProxy));
- }
+ apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
+ apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
+ apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
+ apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
- if (mMmsc != null) {
- apnValue.put(Telephony.Carriers.MMSC, URLToString(mMmsc));
- }
+ apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
- if (mMmsProxy != null) {
- apnValue.put(Telephony.Carriers.MMSPROXY, inetAddressToString(mMmsProxy));
- }
- if (mUser != null) {
- apnValue.put(Telephony.Carriers.USER, mUser);
- }
- if (mPassword != null) {
- apnValue.put(Telephony.Carriers.PASSWORD, mPassword);
- }
+ apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
+ ? "" : inetAddressToString(mMmsProxy));
+ apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
+ apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
String apnType = deParseTypes(mTypes);
- if (apnType != null) {
- apnValue.put(Telephony.Carriers.TYPE, apnType);
- }
- if (mProtocol != null) {
- apnValue.put(Telephony.Carriers.PROTOCOL, mProtocol);
- }
- if (mRoamingProtocol != null) {
- apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, mRoamingProtocol);
- }
+ apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
+ apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
+ apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
- // networkTypeBit.
- apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask);
- if (mMvnoType != null) {
- apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType);
- }
+ apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+ apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
return apnValue;
}
@@ -905,8 +874,16 @@ public class ApnSetting implements Parcelable {
if (inetAddress == null) {
return null;
}
- return TextUtils.isEmpty(inetAddress.getHostName())
- ? inetAddress.getHostAddress() : inetAddress.getHostName();
+ final String inetAddressString = inetAddress.toString();
+ if (TextUtils.isEmpty(inetAddressString)) {
+ return null;
+ }
+ final String hostName = inetAddressString.substring(0, inetAddressString.indexOf("/"));
+ final String address = inetAddressString.substring(inetAddressString.indexOf("/") + 1);
+ if (TextUtils.isEmpty(hostName) && TextUtils.isEmpty(address)) {
+ return null;
+ }
+ return TextUtils.isEmpty(hostName) ? address : hostName;
}
private static int portFromString(String strPort) {
@@ -952,16 +929,33 @@ public class ApnSetting implements Parcelable {
dest.writeString(mRoamingProtocol);
dest.writeInt(mCarrierEnabled ? 1: 0);
dest.writeString(mMvnoType);
+ dest.writeInt(mNetworkTypeBitmask);
}
private static ApnSetting readFromParcel(Parcel in) {
- return makeApnSetting(in.readInt(), in.readString(), in.readString(), in.readString(),
- (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
- in.readInt(), (URL)in.readValue(URL.class.getClassLoader()),
- (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
- in.readInt(), in.readString(), in.readString(), in.readInt(),
- Arrays.asList(in.readStringArray()), in.readString(), in.readString(),
- in.readInt() > 0, 0, 0, 0, false, 0, 0, 0, 0, in.readString(), null);
+ final int id = in.readInt();
+ final String operatorNumeric = in.readString();
+ final String entryName = in.readString();
+ final String apnName = in.readString();
+ final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+ final int port = in.readInt();
+ final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+ final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+ final int mmsPort = in.readInt();
+ final String user = in.readString();
+ final String password = in.readString();
+ final int authType = in.readInt();
+ final List<String> types = Arrays.asList(in.readStringArray());
+ final String protocol = in.readString();
+ final String roamingProtocol = in.readString();
+ final boolean carrierEnabled = in.readInt() > 0;
+ final String mvnoType = in.readString();
+ final int networkTypeBitmask = in.readInt();
+
+ return makeApnSetting(id, operatorNumeric, entryName, apnName,
+ proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
+ roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+ 0, 0, 0, 0, mvnoType, null);
}
public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -1061,9 +1055,8 @@ public class ApnSetting implements Parcelable {
private String mProtocol;
private String mRoamingProtocol;
private int mMtu;
+ private int mNetworkTypeBitmask;
private boolean mCarrierEnabled;
- private int mBearer;
- private int mBearerBitmask;
private int mProfileId;
private boolean mModemCognitive;
private int mMaxConns;
@@ -1078,35 +1071,23 @@ public class ApnSetting implements Parcelable {
public Builder() {}
/**
- * Set the MTU size of the mobile interface to which the APN connected.
- *
- * @param mtu the MTU size to set for the APN
- * @hide
- */
- public Builder setMtu(int mtu) {
- this.mMtu = mtu;
- return this;
- }
-
- /**
- * Sets bearer info.
+ * Sets the unique database id for this entry.
*
- * @param bearer the bearer info to set for the APN
- * @hide
+ * @param id the unique database id to set for this entry
*/
- public Builder setBearer(int bearer) {
- this.mBearer = bearer;
+ private Builder setId(int id) {
+ this.mId = id;
return this;
}
/**
- * Sets the radio access technology bitmask for this APN.
+ * Set the MTU size of the mobile interface to which the APN connected.
*
- * @param bearerBitmask the radio access technology bitmask to set for this APN
+ * @param mtu the MTU size to set for the APN
* @hide
*/
- public Builder setBearerBitmask(int bearerBitmask) {
- this.mBearerBitmask = bearerBitmask;
+ public Builder setMtu(int mtu) {
+ this.mMtu = mtu;
return this;
}
@@ -1298,16 +1279,6 @@ public class ApnSetting implements Parcelable {
}
/**
- * Sets the unique database id for this entry.
- *
- * @param id the unique database id to set for this entry
- */
- public Builder setId(int id) {
- this.mId = id;
- return this;
- }
-
- /**
* Set the numeric operator ID for the APN.
*
* @param operatorNumeric the numeric operator ID to set for this entry
@@ -1341,7 +1312,7 @@ public class ApnSetting implements Parcelable {
}
/**
- * Sets the current status of APN.
+ * Sets the current status for this APN.
*
* @param carrierEnabled the current status to set for this APN
*/
@@ -1351,6 +1322,16 @@ public class ApnSetting implements Parcelable {
}
/**
+ * Sets Radio Technology (Network Type) info for this APN.
+ *
+ * @param networkTypeBitmask the Radio Technology (Network Type) info
+ */
+ public Builder setNetworkTypeBitmask(int networkTypeBitmask) {
+ this.mNetworkTypeBitmask = networkTypeBitmask;
+ return this;
+ }
+
+ /**
* Sets the MVNO match type for this APN.
*
* Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 253432755179..d14670725e8e 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -71,8 +71,18 @@ public class EuiccManager {
* TODO(b/35851809): Make this a SystemApi.
*/
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_OTA_STATUS_CHANGED
- = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+ public static final String ACTION_OTA_STATUS_CHANGED =
+ "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+
+ /**
+ * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not
+ * completed.
+ *
+ * TODO(b/35851809): Make this a public API.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_NOTIFY_CARRIER_SETUP =
+ "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP";
/**
* Intent action to provision an embedded subscription.
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 1bd1af5e3c22..994f3ccd7560 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -31,32 +31,34 @@ LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
+LOCAL_JNI_SHARED_LIBRARIES := \
+ android.hidl.token@1.0 \
libbacktrace \
libbase \
libbinder \
libc++ \
+ libcrypto \
libcutils \
+ libframeworksnettestsjni \
+ libhidl-gen-utils \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
liblog \
liblzma \
libnativehelper \
libnetdaidl \
+ libpackagelistparser \
+ libpcre2 \
+ libselinux \
libui \
libunwind \
libutils \
+ libvintf \
libvndksupport \
- libcrypto \
- libhidl-gen-utils \
- libhidlbase \
- libhidltransport \
- libpackagelistparser \
- libpcre2 \
- libselinux \
libtinyxml2 \
- libvintf \
- libhwbinder \
libunwindstack \
- android.hidl.token@1.0
+ libutilscallstack
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 4fbb228e6e53..d9d4eeba900f 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -204,13 +204,13 @@ public class IpSecServiceParameterizedTest {
}
@Test
- public void testCreateTransportModeTransform() throws Exception {
+ public void testCreateTransform() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verify(mMockNetd)
@@ -236,14 +236,14 @@ public class IpSecServiceParameterizedTest {
}
@Test
- public void testCreateTransportModeTransformAead() throws Exception {
+ public void testCreateTransformAead() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verify(mMockNetd)
@@ -269,14 +269,14 @@ public class IpSecServiceParameterizedTest {
}
@Test
- public void testDeleteTransportModeTransform() throws Exception {
+ public void testDeleteTransform() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
+ mIpSecService.createTransform(ipSecConfig, new Binder());
+ mIpSecService.deleteTransform(createTransformResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
@@ -302,7 +302,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -334,7 +334,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
int resourceId = createTransformResp.resourceId;
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 3eba881df427..a375b600ca60 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -166,6 +166,7 @@ public class IpSecServiceTest {
mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
udpEncapResp.fileDescriptor.close();
+ // Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
@@ -179,10 +180,8 @@ public class IpSecServiceTest {
@Test
public void testUdpEncapsulationSocketBinderDeath() throws Exception {
- int localport = findUnusedPort();
-
IpSecUdpEncapResponse udpEncapResp =
- mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
+ mIpSecService.openUdpEncapsulationSocket(0, new Binder());
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -192,6 +191,7 @@ public class IpSecServiceTest {
refcountedRecord.binderDied();
+ // Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
try {
userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
@@ -412,9 +412,9 @@ public class IpSecServiceTest {
}
@Test
- public void testDeleteInvalidTransportModeTransform() throws Exception {
+ public void testDeleteInvalidTransform() throws Exception {
try {
- mIpSecService.deleteTransportModeTransform(1);
+ mIpSecService.deleteTransform(1);
fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) {
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 10d6deba61df..9f2cb921ea8e 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -66,6 +66,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -174,6 +175,7 @@ public class IpConnectivityMetricsTest {
}
@Test
+ @Ignore
public void testDefaultNetworkEvents() throws Exception {
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
@@ -292,6 +294,7 @@ public class IpConnectivityMetricsTest {
}
@Test
+ @Ignore
public void testEndToEndLogging() throws Exception {
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
index 4098b989a16b..0504c79c55bd 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
@@ -62,6 +62,11 @@ public class ServiceManagerPermissionTests extends TestCase {
public boolean isRuntimePermission(String permission) {
return false;
}
+
+ @Override
+ public int getPackageUid(String packageName, int flags) {
+ return -1;
+ }
};
ServiceManagerNative.asInterface(BinderInternal.getContextObject())
.setPermissionController(pc);
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 29bf02cac8fe..03c9fbeec918 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -56,6 +56,11 @@ public final class WifiActivityEnergyInfo implements Parcelable {
/**
* @hide
*/
+ public long mControllerScanTimeMs;
+
+ /**
+ * @hide
+ */
public long mControllerIdleTimeMs;
/**
@@ -69,13 +74,14 @@ public final class WifiActivityEnergyInfo implements Parcelable {
public static final int STACK_STATE_STATE_IDLE = 3;
public WifiActivityEnergyInfo(long timestamp, int stackState,
- long txTime, long[] txTimePerLevel, long rxTime, long idleTime,
- long energyUsed) {
+ long txTime, long[] txTimePerLevel, long rxTime, long scanTime,
+ long idleTime, long energyUsed) {
mTimestamp = timestamp;
mStackState = stackState;
mControllerTxTimeMs = txTime;
mControllerTxTimePerLevelMs = txTimePerLevel;
mControllerRxTimeMs = rxTime;
+ mControllerScanTimeMs = scanTime;
mControllerIdleTimeMs = idleTime;
mControllerEnergyUsed = energyUsed;
}
@@ -88,6 +94,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {
+ " mControllerTxTimeMs=" + mControllerTxTimeMs
+ " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
+ " mControllerRxTimeMs=" + mControllerRxTimeMs
+ + " mControllerScanTimeMs=" + mControllerScanTimeMs
+ " mControllerIdleTimeMs=" + mControllerIdleTimeMs
+ " mControllerEnergyUsed=" + mControllerEnergyUsed
+ " }";
@@ -101,10 +108,11 @@ public final class WifiActivityEnergyInfo implements Parcelable {
long txTime = in.readLong();
long[] txTimePerLevel = in.createLongArray();
long rxTime = in.readLong();
+ long scanTime = in.readLong();
long idleTime = in.readLong();
long energyUsed = in.readLong();
return new WifiActivityEnergyInfo(timestamp, stackState,
- txTime, txTimePerLevel, rxTime, idleTime, energyUsed);
+ txTime, txTimePerLevel, rxTime, scanTime, idleTime, energyUsed);
}
public WifiActivityEnergyInfo[] newArray(int size) {
return new WifiActivityEnergyInfo[size];
@@ -117,6 +125,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {
out.writeLong(mControllerTxTimeMs);
out.writeLongArray(mControllerTxTimePerLevelMs);
out.writeLong(mControllerRxTimeMs);
+ out.writeLong(mControllerScanTimeMs);
out.writeLong(mControllerIdleTimeMs);
out.writeLong(mControllerEnergyUsed);
}
@@ -157,6 +166,13 @@ public final class WifiActivityEnergyInfo implements Parcelable {
}
/**
+ * @return scan time in ms
+ */
+ public long getControllerScanTimeMillis() {
+ return mControllerScanTimeMs;
+ }
+
+ /**
* @return idle time in ms
*/
public long getControllerIdleTimeMillis() {
@@ -183,6 +199,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {
public boolean isValid() {
return ((mControllerTxTimeMs >=0) &&
(mControllerRxTimeMs >=0) &&
+ (mControllerScanTimeMs >=0) &&
(mControllerIdleTimeMs >=0));
}
-}
+} \ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 6438631cb8ed..2c6011823828 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -267,8 +267,15 @@ public class WifiConfiguration implements Parcelable {
public static final int AP_BAND_5GHZ = 1;
/**
+ * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability,
+ * operating country code and current radio conditions.
+ * @hide
+ */
+ public static final int AP_BAND_ANY = -1;
+
+ /**
* The band which AP resides on
- * 0-2G 1-5G
+ * -1:Any 0:2G 1:5G
* By default, 2G is chosen
* @hide
*/
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 99080d6cc2c0..e4b510db68f4 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1625,6 +1625,7 @@ public class WifiManager {
*
* @return hex-string encoded configuration token or null if there is no current network
* @hide
+ * @deprecated This API is deprecated
*/
public String getCurrentNetworkWpsNfcConfigurationToken() {
try {
@@ -2210,20 +2211,34 @@ public class WifiManager {
/** @hide */
public static final int SAVE_NETWORK_SUCCEEDED = BASE + 9;
- /** @hide */
+ /** @hide
+ * @deprecated This is deprecated
+ */
public static final int START_WPS = BASE + 10;
- /** @hide */
+ /** @hide
+ * @deprecated This is deprecated
+ */
public static final int START_WPS_SUCCEEDED = BASE + 11;
- /** @hide */
+ /** @hide
+ * @deprecated This is deprecated
+ */
public static final int WPS_FAILED = BASE + 12;
- /** @hide */
+ /** @hide
+ * @deprecated This is deprecated
+ */
public static final int WPS_COMPLETED = BASE + 13;
- /** @hide */
+ /** @hide
+ * @deprecated This is deprecated
+ */
public static final int CANCEL_WPS = BASE + 14;
- /** @hide */
+ /** @hide
+ * @deprecated This is deprecated
+ */
public static final int CANCEL_WPS_FAILED = BASE + 15;
- /** @hide */
+ /** @hide
+ * @deprecated This is deprecated
+ */
public static final int CANCEL_WPS_SUCCEDED = BASE + 16;
/** @hide */
@@ -2263,15 +2278,25 @@ public class WifiManager {
public static final int BUSY = 2;
/* WPS specific errors */
- /** WPS overlap detected */
+ /** WPS overlap detected
+ * @deprecated This is deprecated
+ */
public static final int WPS_OVERLAP_ERROR = 3;
- /** WEP on WPS is prohibited */
+ /** WEP on WPS is prohibited
+ * @deprecated This is deprecated
+ */
public static final int WPS_WEP_PROHIBITED = 4;
- /** TKIP only prohibited */
+ /** TKIP only prohibited
+ * @deprecated This is deprecated
+ */
public static final int WPS_TKIP_ONLY_PROHIBITED = 5;
- /** Authentication failure on WPS */
+ /** Authentication failure on WPS
+ * @deprecated This is deprecated
+ */
public static final int WPS_AUTH_FAILURE = 6;
- /** WPS timed out */
+ /** WPS timed out
+ * @deprecated This is deprecated
+ */
public static final int WPS_TIMED_OUT = 7;
/**
@@ -2312,12 +2337,19 @@ public class WifiManager {
public void onFailure(int reason);
}
- /** Interface for callback invocation on a start WPS action */
+ /** Interface for callback invocation on a start WPS action
+ * @deprecated This is deprecated
+ */
public static abstract class WpsCallback {
- /** WPS start succeeded */
+
+ /** WPS start succeeded
+ * @deprecated This API is deprecated
+ */
public abstract void onStarted(String pin);
- /** WPS operation completed successfully */
+ /** WPS operation completed successfully
+ * @deprecated This API is deprecated
+ */
public abstract void onSucceeded();
/**
@@ -2326,6 +2358,7 @@ public class WifiManager {
* {@link #WPS_TKIP_ONLY_PROHIBITED}, {@link #WPS_OVERLAP_ERROR},
* {@link #WPS_WEP_PROHIBITED}, {@link #WPS_TIMED_OUT} or {@link #WPS_AUTH_FAILURE}
* and some generic errors.
+ * @deprecated This API is deprecated
*/
public abstract void onFailed(int reason);
}
@@ -3032,6 +3065,7 @@ public class WifiManager {
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again
+ * @deprecated This API is deprecated
*/
public void startWps(WpsInfo config, WpsCallback listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
@@ -3044,6 +3078,7 @@ public class WifiManager {
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again
+ * @deprecated This API is deprecated
*/
public void cancelWps(WpsCallback listener) {
getChannel().sendMessage(CANCEL_WPS, 0, putListener(listener));
diff --git a/wifi/java/android/net/wifi/rtt/LocationCivic.java b/wifi/java/android/net/wifi/rtt/LocationCivic.java
new file mode 100644
index 000000000000..610edb6399b4
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationCivic.java
@@ -0,0 +1,118 @@
+/*
+ * 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.net.wifi.rtt;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Location Civic Report (LCR).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCR report.
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ */
+public final class LocationCivic implements Parcelable {
+ private final byte[] mData;
+
+ /**
+ * Parse the raw LCR information element (byte array) and extract the LocationCivic structure.
+ *
+ * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+ *
+ * @hide
+ */
+ @Nullable
+ public static LocationCivic parseInformationElement(byte id, byte[] data) {
+ // TODO
+ return null;
+ }
+
+ /** @hide */
+ public LocationCivic(byte[] data) {
+ mData = data;
+ }
+
+ /**
+ * Return the Location Civic data reported by the peer.
+ *
+ * @return An arbitrary location information.
+ */
+ public byte[] getData() {
+ return mData;
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mData);
+ }
+
+ public static final Parcelable.Creator<LocationCivic> CREATOR =
+ new Parcelable.Creator<LocationCivic>() {
+ @Override
+ public LocationCivic[] newArray(int size) {
+ return new LocationCivic[size];
+ }
+
+ @Override
+ public LocationCivic createFromParcel(Parcel in) {
+ byte[] data = in.createByteArray();
+
+ return new LocationCivic(data);
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString() {
+ return new StringBuilder("LCR: data=").append(Arrays.toString(mData)).toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof LocationCivic)) {
+ return false;
+ }
+
+ LocationCivic lhs = (LocationCivic) o;
+
+ return Arrays.equals(mData, lhs.mData);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mData);
+ }
+}
diff --git a/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
new file mode 100644
index 000000000000..8aba56aa0ee7
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
@@ -0,0 +1,272 @@
+/*
+ * 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.net.wifi.rtt;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The Device Location Configuration Information (LCI) specifies the location information of a peer
+ * device (e.g. an Access Point).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCI report (Location configuration information
+ * report).
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ */
+public final class LocationConfigurationInformation implements Parcelable {
+ /** @hide */
+ @IntDef({
+ ALTITUDE_UNKNOWN, ALTITUDE_IN_METERS, ALTITUDE_IN_FLOORS })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AltitudeTypes {
+ }
+
+ /**
+ * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+ * does not specify an altitude or altitude uncertainty. The corresponding methods,
+ * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} are not valid and will throw
+ * an exception.
+ */
+ public static final int ALTITUDE_UNKNOWN = 0;
+
+ /**
+ * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+ * specifies the altitude and altitude uncertainty in meters. The corresponding methods,
+ * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} return a valid value in meters.
+ */
+ public static final int ALTITUDE_IN_METERS = 1;
+
+ /**
+ * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the
+ * location specifies the altitude in floors, and does not specify an altitude uncertainty.
+ * The {@link #getAltitude()} method returns valid value in floors, and the
+ * {@link #getAltitudeUncertainty()} method is not valid and will throw an exception.
+ */
+ public static final int ALTITUDE_IN_FLOORS = 2;
+
+ private final double mLatitude;
+ private final double mLatitudeUncertainty;
+ private final double mLongitude;
+ private final double mLongitudeUncertainty;
+ private final int mAltitudeType;
+ private final double mAltitude;
+ private final double mAltitudeUncertainty;
+
+ /**
+ * Parse the raw LCI information element (byte array) and extract the
+ * LocationConfigurationInformation structure.
+ *
+ * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+ *
+ * @hide
+ */
+ @Nullable
+ public static LocationConfigurationInformation parseInformationElement(byte id, byte[] data) {
+ // TODO
+ return null;
+ }
+
+ /** @hide */
+ public LocationConfigurationInformation(double latitude, double latitudeUncertainty,
+ double longitude, double longitudeUncertainty, @AltitudeTypes int altitudeType,
+ double altitude, double altitudeUncertainty) {
+ mLatitude = latitude;
+ mLatitudeUncertainty = latitudeUncertainty;
+ mLongitude = longitude;
+ mLongitudeUncertainty = longitudeUncertainty;
+ mAltitudeType = altitudeType;
+ mAltitude = altitude;
+ mAltitudeUncertainty = altitudeUncertainty;
+ }
+
+ /**
+ * Get latitude in degrees. Values are per WGS 84 reference system. Valid values are between
+ * -90 and 90.
+ *
+ * @return Latitude in degrees.
+ */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /**
+ * Get the uncertainty of the latitude {@link #getLatitude()} in degrees. A value of 0 indicates
+ * an unknown uncertainty.
+ *
+ * @return Uncertainty of the latitude in degrees.
+ */
+ public double getLatitudeUncertainty() {
+ return mLatitudeUncertainty;
+ }
+
+ /**
+ * Get longitude in degrees. Values are per WGS 84 reference system. Valid values are between
+ * -180 and 180.
+ *
+ * @return Longitude in degrees.
+ */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /**
+ * Get the uncertainty of the longitude {@link #getLongitude()} ()} in degrees. A value of 0
+ * indicates an unknown uncertainty.
+ *
+ * @return Uncertainty of the longitude in degrees.
+ */
+ public double getLongitudeUncertainty() {
+ return mLongitudeUncertainty;
+ }
+
+ /**
+ * Specifies the type of the altitude measurement returned by {@link #getAltitude()} and
+ * {@link #getAltitudeUncertainty()}. The possible values are:
+ * <li>{@link #ALTITUDE_UNKNOWN}: The altitude and altitude uncertainty are not provided.
+ * <li>{@link #ALTITUDE_IN_METERS}: The altitude and altitude uncertainty are provided in
+ * meters. Values are per WGS 84 reference system.
+ * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors, the altitude uncertainty
+ * is not provided.
+ *
+ * @return The type of the altitude and altitude uncertainty.
+ */
+ public @AltitudeTypes int getAltitudeType() {
+ return mAltitudeType;
+ }
+
+ /**
+ * The altitude is interpreted according to the {@link #getAltitudeType()}. The possible values
+ * are:
+ * <li>{@link #ALTITUDE_UNKNOWN}: The altitude is not provided - this method will throw an
+ * exception.
+ * <li>{@link #ALTITUDE_IN_METERS}: The altitude is provided in meters. Values are per WGS 84
+ * reference system.
+ * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors.
+ *
+ * @return Altitude value whose meaning is specified by {@link #getAltitudeType()}.
+ */
+ public double getAltitude() {
+ if (mAltitudeType == ALTITUDE_UNKNOWN) {
+ throw new IllegalStateException(
+ "getAltitude(): invoked on an invalid type: getAltitudeType()==UNKNOWN");
+ }
+ return mAltitude;
+ }
+
+ /**
+ * Only valid if the the {@link #getAltitudeType()} is equal to {@link #ALTITUDE_IN_METERS} -
+ * otherwise this method will throw an exception.
+ * <p>
+ * Get the uncertainty of the altitude {@link #getAltitude()} in meters. A value of 0
+ * indicates an unknown uncertainty.
+ *
+ * @return Uncertainty of the altitude in meters.
+ */
+ public double getAltitudeUncertainty() {
+ if (mAltitudeType != ALTITUDE_IN_METERS) {
+ throw new IllegalStateException(
+ "getAltitude(): invoked on an invalid type: getAltitudeType()!=IN_METERS");
+ }
+ return mAltitudeUncertainty;
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeDouble(mLatitude);
+ dest.writeDouble(mLatitudeUncertainty);
+ dest.writeDouble(mLongitude);
+ dest.writeDouble(mLongitudeUncertainty);
+ dest.writeInt(mAltitudeType);
+ dest.writeDouble(mAltitude);
+ dest.writeDouble(mAltitudeUncertainty);
+ }
+
+ public static final Creator<LocationConfigurationInformation> CREATOR =
+ new Creator<LocationConfigurationInformation>() {
+ @Override
+ public LocationConfigurationInformation[] newArray(int size) {
+ return new LocationConfigurationInformation[size];
+ }
+
+ @Override
+ public LocationConfigurationInformation createFromParcel(Parcel in) {
+ double latitude = in.readDouble();
+ double latitudeUnc = in.readDouble();
+ double longitude = in.readDouble();
+ double longitudeUnc = in.readDouble();
+ int altitudeType = in.readInt();
+ double altitude = in.readDouble();
+ double altitudeUnc = in.readDouble();
+
+ return new LocationConfigurationInformation(latitude, latitudeUnc, longitude,
+ longitudeUnc, altitudeType, altitude, altitudeUnc);
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString() {
+ return new StringBuilder("LCI: latitude=").append(mLatitude).append(
+ ", latitudeUncertainty=").append(mLatitudeUncertainty).append(
+ ", longitude=").append(mLongitude).append(", longitudeUncertainty=").append(
+ mLongitudeUncertainty).append(", altitudeType=").append(mAltitudeType).append(
+ ", altitude=").append(mAltitude).append(", altitudeUncertainty=").append(
+ mAltitudeUncertainty).toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof LocationConfigurationInformation)) {
+ return false;
+ }
+
+ LocationConfigurationInformation lhs = (LocationConfigurationInformation) o;
+
+ return mLatitude == lhs.mLatitude && mLatitudeUncertainty == lhs.mLatitudeUncertainty
+ && mLongitude == lhs.mLongitude
+ && mLongitudeUncertainty == lhs.mLongitudeUncertainty
+ && mAltitudeType == lhs.mAltitudeType && mAltitude == lhs.mAltitude
+ && mAltitudeUncertainty == lhs.mAltitudeUncertainty;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLatitude, mLatitudeUncertainty, mLongitude, mLongitudeUncertainty,
+ mAltitudeType, mAltitude, mAltitudeUncertainty);
+ }
+}
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index d5ca8f7f9fb0..201833bff431 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -65,29 +65,37 @@ public final class RangingResult implements Parcelable {
private final int mDistanceMm;
private final int mDistanceStdDevMm;
private final int mRssi;
+ private final LocationConfigurationInformation mLci;
+ private final LocationCivic mLcr;
private final long mTimestamp;
/** @hide */
public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
- int distanceStdDevMm, int rssi, long timestamp) {
+ int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+ long timestamp) {
mStatus = status;
mMac = mac;
mPeerHandle = null;
mDistanceMm = distanceMm;
mDistanceStdDevMm = distanceStdDevMm;
mRssi = rssi;
+ mLci = lci;
+ mLcr = lcr;
mTimestamp = timestamp;
}
/** @hide */
public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm,
- int distanceStdDevMm, int rssi, long timestamp) {
+ int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+ long timestamp) {
mStatus = status;
mMac = null;
mPeerHandle = peerHandle;
mDistanceMm = distanceMm;
mDistanceStdDevMm = distanceStdDevMm;
mRssi = rssi;
+ mLci = lci;
+ mLcr = lcr;
mTimestamp = timestamp;
}
@@ -169,6 +177,38 @@ public final class RangingResult implements Parcelable {
}
/**
+ * @return The Location Configuration Information (LCI) as self-reported by the peer.
+ * <p>
+ * Note: the information is NOT validated - use with caution. Consider validating it with
+ * other sources of information before using it.
+ */
+ @Nullable
+ public LocationConfigurationInformation getReportedLocationConfigurationInformation() {
+ if (mStatus != STATUS_SUCCESS) {
+ throw new IllegalStateException(
+ "getReportedLocationConfigurationInformation(): invoked on an invalid result: "
+ + "getStatus()=" + mStatus);
+ }
+ return mLci;
+ }
+
+ /**
+ * @return The Location Civic report (LCR) as self-reported by the peer.
+ * <p>
+ * Note: the information is NOT validated - use with caution. Consider validating it with
+ * other sources of information before using it.
+ */
+ @Nullable
+ public LocationCivic getReportedLocationCivic() {
+ if (mStatus != STATUS_SUCCESS) {
+ throw new IllegalStateException(
+ "getReportedLocationCivic(): invoked on an invalid result: getStatus()="
+ + mStatus);
+ }
+ return mLcr;
+ }
+
+ /**
* @return The timestamp, in us since boot, at which the ranging operation was performed.
* <p>
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
@@ -205,6 +245,18 @@ public final class RangingResult implements Parcelable {
dest.writeInt(mDistanceMm);
dest.writeInt(mDistanceStdDevMm);
dest.writeInt(mRssi);
+ if (mLci == null) {
+ dest.writeBoolean(false);
+ } else {
+ dest.writeBoolean(true);
+ mLci.writeToParcel(dest, flags);
+ }
+ if (mLcr == null) {
+ dest.writeBoolean(false);
+ } else {
+ dest.writeBoolean(true);
+ mLcr.writeToParcel(dest, flags);
+ }
dest.writeLong(mTimestamp);
}
@@ -230,13 +282,23 @@ public final class RangingResult implements Parcelable {
int distanceMm = in.readInt();
int distanceStdDevMm = in.readInt();
int rssi = in.readInt();
+ boolean lciPresent = in.readBoolean();
+ LocationConfigurationInformation lci = null;
+ if (lciPresent) {
+ lci = LocationConfigurationInformation.CREATOR.createFromParcel(in);
+ }
+ boolean lcrPresent = in.readBoolean();
+ LocationCivic lcr = null;
+ if (lcrPresent) {
+ lcr = LocationCivic.CREATOR.createFromParcel(in);
+ }
long timestamp = in.readLong();
if (peerHandlePresent) {
return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
- timestamp);
+ lci, lcr, timestamp);
} else {
return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
- timestamp);
+ lci, lcr, timestamp);
}
}
};
@@ -248,8 +310,8 @@ public final class RangingResult implements Parcelable {
mMac).append(", peerHandle=").append(
mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
- ", rssi=").append(mRssi).append(", timestamp=").append(mTimestamp).append(
- "]").toString();
+ ", rssi=").append(mRssi).append(", lci=").append(mLci).append(", lcr=").append(
+ mLcr).append(", timestamp=").append(mTimestamp).append("]").toString();
}
@Override
@@ -267,12 +329,13 @@ public final class RangingResult implements Parcelable {
return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
&& mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
+ && Objects.equals(mLci, lhs.mLci) && Objects.equals(mLcr, lhs.mLcr)
&& mTimestamp == lhs.mTimestamp;
}
@Override
public int hashCode() {
return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi,
- mTimestamp);
+ mLci, mLcr, mTimestamp);
}
}
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 72e95b93e741..41c7f8644e2e 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -17,6 +17,7 @@
package android.net.wifi.rtt;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -32,7 +33,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
import org.junit.Test;
@@ -46,7 +46,6 @@ import java.util.List;
/**
* Unit test harness for WifiRttManager class.
*/
-@SmallTest
public class WifiRttManagerTest {
private WifiRttManager mDut;
private TestLooper mMockLooper;
@@ -80,7 +79,7 @@ public class WifiRttManagerTest {
List<RangingResult> results = new ArrayList<>();
results.add(
new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
- 10, 666));
+ 10, null, null, 666));
RangingResultCallback callbackMock = mock(RangingResultCallback.class);
ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
@@ -236,10 +235,23 @@ public class WifiRttManagerTest {
int distanceStdDevCm = 10;
int rssi = 5;
long timestamp = System.currentTimeMillis();
+ double latitude = 5.5;
+ double latitudeUncertainty = 6.5;
+ double longitude = 7.5;
+ double longitudeUncertainty = 8.5;
+ int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_METERS;
+ double altitude = 9.5;
+ double altitudeUncertainty = 55.5;
+ byte[] lcrData = { 0x1, 0x2, 0x3, 0xA, 0xB, 0xC };
+
+ LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+ latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+ altitudeUncertainty);
+ LocationCivic lcr = new LocationCivic(lcrData);
// RangingResults constructed with a MAC address
RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
- timestamp);
+ lci, lcr, timestamp);
Parcel parcelW = Parcel.obtain();
result.writeToParcel(parcelW, 0);
@@ -255,7 +267,7 @@ public class WifiRttManagerTest {
// RangingResults constructed with a PeerHandle
result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
- timestamp);
+ null, null, timestamp);
parcelW = Parcel.obtain();
result.writeToParcel(parcelW, 0);
@@ -269,4 +281,83 @@ public class WifiRttManagerTest {
assertEquals(result, rereadResult);
}
+
+ /**
+ * Validate that LocationConfigurationInformation parcel works (produces same object on
+ * write/read).
+ */
+ @Test
+ public void testLciParcel() {
+ double latitude = 1.5;
+ double latitudeUncertainty = 2.5;
+ double longitude = 3.5;
+ double longitudeUncertainty = 4.5;
+ int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_FLOORS;
+ double altitude = 5.5;
+ double altitudeUncertainty = 6.5;
+
+ LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+ latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+ altitudeUncertainty);
+
+ Parcel parcelW = Parcel.obtain();
+ lci.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ LocationConfigurationInformation rereadLci =
+ LocationConfigurationInformation.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(lci, rereadLci);
+ }
+
+ /**
+ * Validate that the LCI throws an exception when accessing invalid fields an certain altitude
+ * types.
+ */
+ @Test
+ public void testLciInvalidAltitudeFieldAccess() {
+ boolean exceptionThrown;
+ LocationConfigurationInformation lci = new LocationConfigurationInformation(0, 0, 0, 0,
+ LocationConfigurationInformation.ALTITUDE_UNKNOWN, 0, 0);
+
+ // UNKNOWN - invalid altitude & altitude uncertainty
+ exceptionThrown = false;
+ try {
+ lci.getAltitude();
+ } catch (IllegalStateException e) {
+ exceptionThrown = true;
+ }
+ assertTrue("UNKNOWN / getAltitude()", exceptionThrown);
+
+ exceptionThrown = false;
+ try {
+ lci.getAltitudeUncertainty();
+ } catch (IllegalStateException e) {
+ exceptionThrown = true;
+ }
+ assertTrue("UNKNOWN / getAltitudeUncertainty()", exceptionThrown);
+
+ lci = new LocationConfigurationInformation(0, 0, 0, 0,
+ LocationConfigurationInformation.ALTITUDE_IN_FLOORS, 0, 0);
+
+ // FLOORS - invalid altitude uncertainty
+ exceptionThrown = false;
+ try {
+ lci.getAltitudeUncertainty();
+ } catch (IllegalStateException e) {
+ exceptionThrown = true;
+ }
+ assertTrue("FLOORS / getAltitudeUncertainty()", exceptionThrown);
+
+ // and good accesses just in case
+ lci.getAltitude();
+ lci = new LocationConfigurationInformation(0, 0, 0, 0,
+ LocationConfigurationInformation.ALTITUDE_IN_METERS, 0, 0);
+ lci.getAltitude();
+ lci.getAltitudeUncertainty();
+ }
}