summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt46
-rw-r--r--api/removed.txt7
-rw-r--r--api/system-current.txt12
-rw-r--r--api/system-removed.txt41
-rw-r--r--api/test-current.txt2
-rw-r--r--api/test-removed.txt9
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp45
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h25
-rw-r--r--cmds/statsd/src/StatsService.cpp13
-rw-r--r--cmds/statsd/src/StatsService.h1
-rw-r--r--cmds/statsd/src/active_config_list.proto11
-rw-r--r--cmds/statsd/src/atoms.proto150
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp3
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp37
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h17
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp4
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h3
-rw-r--r--cmds/statsd/src/statsd_config.proto2
-rw-r--r--cmds/statsd/src/subscriber/IncidentdReporter.cpp11
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp237
-rw-r--r--core/java/android/app/AppOpsManager.java1
-rw-r--r--core/java/android/app/JobSchedulerImpl.java4
-rw-r--r--core/java/android/app/VoiceInteractor.java10
-rw-r--r--core/java/android/app/admin/SecurityLog.java5
-rw-r--r--core/java/android/app/backup/RestoreSession.java2
-rw-r--r--core/java/android/app/job/IJobScheduler.aidl5
-rw-r--r--core/java/android/app/prediction/AppTarget.java3
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java20
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java19
-rw-r--r--core/java/android/content/ContentProvider.java10
-rw-r--r--core/java/android/content/Intent.java25
-rw-r--r--core/java/android/content/pm/PackageItemInfo.java3
-rw-r--r--core/java/android/content/pm/PackageManager.java1
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java4
-rwxr-xr-xcore/java/android/os/Build.java3
-rw-r--r--core/java/android/os/IncidentManager.java56
-rw-r--r--core/java/android/os/ZygoteProcess.java21
-rw-r--r--core/java/android/os/storage/StorageManager.java2
-rw-r--r--core/java/android/os/storage/VolumeRecord.java9
-rw-r--r--core/java/android/provider/DocumentsContract.java11
-rw-r--r--core/java/android/service/appprediction/AppPredictionService.java16
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java73
-rw-r--r--core/java/android/service/contentcapture/FlushMetrics.aidl20
-rw-r--r--core/java/android/service/contentcapture/FlushMetrics.java79
-rw-r--r--core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl8
-rw-r--r--core/java/android/util/FeatureFlagUtils.java6
-rw-r--r--core/java/android/view/SurfaceControl.java3
-rw-r--r--core/java/android/view/SurfaceView.java215
-rw-r--r--core/java/android/view/WindowInsets.java4
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl4
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java2
-rw-r--r--core/java/android/widget/PopupWindow.java4
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java12
-rw-r--r--core/java/com/android/internal/infra/AbstractRemoteService.java6
-rw-r--r--core/java/com/android/internal/os/Zygote.java47
-rw-r--r--core/java/com/android/internal/os/ZygoteConfig.java3
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java2
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java5
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java337
-rw-r--r--core/java/com/android/internal/policy/DecorView.java2
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp43
-rw-r--r--core/res/AndroidManifest.xml9
-rw-r--r--core/res/res/layout/autofill_save.xml23
-rw-r--r--core/res/res/layout/media_route_chooser_dialog.xml2
-rw-r--r--core/res/res/layout/media_route_list_item.xml2
-rw-r--r--core/res/res/values/dimens.xml15
-rw-r--r--core/res/res/values/public.xml194
-rw-r--r--core/res/res/values/strings.xml9
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--data/etc/com.android.dialer.xml2
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java170
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp3
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp2
-rw-r--r--libs/hwui/renderthread/VulkanManager.h7
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp5
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h1
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp17
-rw-r--r--libs/protoutil/tests/EncodedBuffer_test.cpp122
-rw-r--r--libs/protoutil/tests/ProtoOutputStream_test.cpp85
-rw-r--r--media/java/android/media/RingtoneManager.java29
-rw-r--r--media/java/android/media/ThumbnailUtils.java9
-rw-r--r--packages/CaptivePortalLogin/Android.bp20
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java7
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java15
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java4
-rw-r--r--packages/NetworkPermissionConfig/Android.bp21
-rw-r--r--packages/NetworkStack/Android.bp9
-rw-r--r--packages/NetworkStack/AndroidManifest.xml4
-rw-r--r--packages/NetworkStack/AndroidManifestBase.xml1
-rw-r--r--packages/NetworkStack/src/android/net/apf/ApfFilter.java266
-rw-r--r--packages/NetworkStack/src/android/net/util/NetworkStackUtils.java11
-rw-r--r--packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java23
-rw-r--r--packages/NetworkStack/tests/src/android/net/apf/ApfTest.java157
-rw-r--r--packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java21
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java69
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java26
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java30
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java21
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java20
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java49
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java11
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java42
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java2
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java6
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml1
-rw-r--r--packages/SystemUI/res/drawable/bubble_flyout.xml30
-rw-r--r--packages/SystemUI/res/layout-land/global_actions_grid.xml13
-rw-r--r--packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml12
-rw-r--r--packages/SystemUI/res/layout/biometric_dialog.xml3
-rw-r--r--packages/SystemUI/res/layout/bubble_flyout.xml13
-rw-r--r--packages/SystemUI/res/layout/global_actions_grid.xml12
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml41
-rw-r--r--packages/SystemUI/res/layout/left_docked_overlay.xml19
-rw-r--r--packages/SystemUI/res/layout/right_docked_overlay.xml19
-rw-r--r--packages/SystemUI/res/values-sw320dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw392dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw410dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml17
-rw-r--r--packages/SystemUI/res/values/strings.xml12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java17
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java412
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java433
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java1
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto1
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java106
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java42
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java19
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java9
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java3
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java16
-rw-r--r--services/core/java/com/android/server/Watchdog.java79
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java58
-rw-r--r--services/core/java/com/android/server/am/PreBootBroadcaster.java8
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java38
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java23
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkNotificationManager.java16
-rw-r--r--services/core/java/com/android/server/connectivity/PermissionMonitor.java4
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java32
-rw-r--r--services/core/java/com/android/server/location/GnssConfiguration.java6
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java73
-rw-r--r--services/core/java/com/android/server/location/GnssVisibilityControl.java26
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java12
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java20
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java12
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerShellCommand.java71
-rw-r--r--services/core/java/com/android/server/pm/Installer.java1
-rw-r--r--services/core/java/com/android/server/pm/ModuleInfoProvider.java24
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java58
-rw-r--r--services/core/java/com/android/server/power/AttentionDetector.java8
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java6
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java60
-rw-r--r--services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java5
-rw-r--r--services/core/java/com/android/server/updates/LangIdInstallReceiver.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java28
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java94
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java11
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java14
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java34
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java37
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java8
-rw-r--r--services/core/jni/com_android_server_power_PowerManagerService.cpp3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java2
-rw-r--r--services/java/com/android/server/SystemServer.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java51
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java183
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java15
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java66
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java1
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java2
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java1
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java5
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java6
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java16
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthGsm.java3
-rw-r--r--telephony/java/android/telephony/ServiceState.java215
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java38
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java15
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java20
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java17
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl11
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyPermissions.java146
-rw-r--r--tests/net/common/Android.bp1
-rw-r--r--tests/net/common/java/android/net/NetworkTest.java2
-rw-r--r--tests/net/java/android/net/IpMemoryStoreTest.java45
-rw-r--r--tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java8
-rw-r--r--tools/aapt/SdkConstants.h1
-rw-r--r--tools/aapt2/SdkConstants.h1
-rwxr-xr-xtools/fonts/fontchain_linter.py8
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java5
256 files changed, 5434 insertions, 2250 deletions
diff --git a/api/current.txt b/api/current.txt
index 8b24826a267b..3ec7f447e8b9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -143,6 +143,7 @@ package android {
field public static final String SET_WALLPAPER_HINTS = "android.permission.SET_WALLPAPER_HINTS";
field public static final String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
field public static final String SMS_FINANCIAL_TRANSACTIONS = "android.permission.SMS_FINANCIAL_TRANSACTIONS";
+ field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
@@ -219,12 +220,6 @@ package android {
public static final class R.attr {
ctor public R.attr();
- field public static final int __removed1 = 16844185; // 0x1010599
- field public static final int __removed2 = 16844186; // 0x101059a
- field public static final int __removed3 = 16844187; // 0x101059b
- field public static final int __removed4 = 16844188; // 0x101059c
- field public static final int __removed5 = 16844189; // 0x101059d
- field public static final int __removed6 = 16844182; // 0x1010596
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -283,7 +278,7 @@ package android {
field public static final int alertDialogTheme = 16843529; // 0x1010309
field public static final int alignmentMode = 16843642; // 0x101037a
field public static final int allContactsName = 16843468; // 0x10102cc
- field public static final int allowAudioPlaybackCapture = 16844199; // 0x10105a7
+ field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
@@ -571,8 +566,8 @@ package android {
field public static final int endX = 16844050; // 0x1010512
field public static final int endY = 16844051; // 0x1010513
field @Deprecated public static final int endYear = 16843133; // 0x101017d
- field public static final int enforceNavigationBarContrast = 16844203; // 0x10105ab
- field public static final int enforceStatusBarContrast = 16844202; // 0x10105aa
+ field public static final int enforceNavigationBarContrast = 16844293; // 0x1010605
+ field public static final int enforceStatusBarContrast = 16844292; // 0x1010604
field public static final int enterFadeDuration = 16843532; // 0x101030c
field public static final int entries = 16842930; // 0x10100b2
field public static final int entryValues = 16843256; // 0x10101f8
@@ -645,10 +640,10 @@ package android {
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceDarkAllowed = 16844172; // 0x101058c
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
- field public static final int forceUriPermissions = 16844197; // 0x10105a5
+ field public static final int forceUriPermissions = 16844191; // 0x101059f
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
- field public static final int foregroundServiceType = 16844191; // 0x101059f
+ field public static final int foregroundServiceType = 16844185; // 0x1010599
field public static final int foregroundTint = 16843885; // 0x101046d
field public static final int foregroundTintMode = 16843886; // 0x101046e
field public static final int format = 16843013; // 0x1010105
@@ -706,7 +701,7 @@ package android {
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
field public static final int hardwareAccelerated = 16843475; // 0x10102d3
field public static final int hasCode = 16842764; // 0x101000c
- field public static final int hasFragileUserData = 16844192; // 0x10105a0
+ field public static final int hasFragileUserData = 16844186; // 0x101059a
field @Deprecated public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
field public static final int headerBackground = 16843055; // 0x101012f
field @Deprecated public static final int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
@@ -734,7 +729,7 @@ package android {
field public static final int iconTintMode = 16844127; // 0x101055f
field public static final int iconifiedByDefault = 16843514; // 0x10102fa
field public static final int id = 16842960; // 0x10100d0
- field public static final int identifier = 16844204; // 0x10105ac
+ field public static final int identifier = 16844294; // 0x1010606
field public static final int ignoreGravity = 16843263; // 0x10101ff
field public static final int imageButtonStyle = 16842866; // 0x1010072
field public static final int imageWellStyle = 16842867; // 0x1010073
@@ -766,7 +761,7 @@ package android {
field public static final int indicatorRight = 16843022; // 0x101010e
field public static final int indicatorStart = 16843729; // 0x10103d1
field public static final int inflatedId = 16842995; // 0x10100f3
- field public static final int inheritShowWhenLocked = 16844194; // 0x10105a2
+ field public static final int inheritShowWhenLocked = 16844188; // 0x101059c
field public static final int initOrder = 16842778; // 0x101001a
field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
field public static final int initialLayout = 16843345; // 0x1010251
@@ -943,7 +938,7 @@ package android {
field public static final int menuCategory = 16843230; // 0x10101de
field public static final int mimeType = 16842790; // 0x1010026
field public static final int min = 16844089; // 0x1010539
- field public static final int minAspectRatio = 16844193; // 0x10105a1
+ field public static final int minAspectRatio = 16844187; // 0x101059b
field public static final int minDate = 16843583; // 0x101033f
field public static final int minEms = 16843098; // 0x101015a
field public static final int minHeight = 16843072; // 0x1010140
@@ -1124,7 +1119,7 @@ package android {
field public static final int reqKeyboardType = 16843304; // 0x1010228
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
- field public static final int requestLegacyExternalStorage = 16844201; // 0x10105a9
+ field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
@@ -1203,7 +1198,7 @@ package android {
field public static final int secondaryProgress = 16843064; // 0x1010138
field public static final int secondaryProgressTint = 16843879; // 0x1010467
field public static final int secondaryProgressTintMode = 16843880; // 0x1010468
- field public static final int secureElementName = 16844200; // 0x10105a8
+ field public static final int secureElementName = 16844290; // 0x1010602
field public static final int seekBarStyle = 16842875; // 0x101007b
field public static final int segmentedButtonStyle = 16843568; // 0x1010330
field public static final int selectAllOnFocus = 16843102; // 0x101015e
@@ -1212,7 +1207,7 @@ package android {
field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
field @Deprecated public static final int selectedDateVerticalBar = 16843591; // 0x1010347
field @Deprecated public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
- field public static final int selectionDividerHeight = 16844190; // 0x101059e
+ field public static final int selectionDividerHeight = 16844184; // 0x1010598
field public static final int sessionService = 16843837; // 0x101043d
field public static final int settingsActivity = 16843301; // 0x1010225
field public static final int settingsSliceUri = 16844179; // 0x1010593
@@ -1331,7 +1326,7 @@ package android {
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
- field public static final int supportsMultipleDisplays = 16844183; // 0x1010597
+ field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
@@ -1512,9 +1507,9 @@ package android {
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
field public static final int use32bitAbi = 16844053; // 0x1010515
- field public static final int useAppZygote = 16844184; // 0x1010598
+ field public static final int useAppZygote = 16844183; // 0x1010597
field public static final int useDefaultMargins = 16843641; // 0x1010379
- field public static final int useEmbeddedDex = 16844196; // 0x10105a4
+ field public static final int useEmbeddedDex = 16844190; // 0x101059e
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
field public static final int userVisible = 16843409; // 0x1010291
@@ -1626,7 +1621,7 @@ package android {
field @Deprecated public static final int yearListSelectorColor = 16843930; // 0x101049a
field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
field public static final int zAdjustment = 16843201; // 0x10101c1
- field public static final int zygotePreloadName = 16844195; // 0x10105a3
+ field public static final int zygotePreloadName = 16844189; // 0x101059d
}
public static final class R.bool {
@@ -10326,6 +10321,7 @@ package android.content {
field public static final String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
field public static final String ACTION_VIEW = "android.intent.action.VIEW";
field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
+ field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -34164,7 +34160,7 @@ package android.os {
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
field public static final int P = 28; // 0x1c
- field public static final int Q = 10000; // 0x2710
+ field public static final int Q = 29; // 0x1d
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -56249,12 +56245,10 @@ package android.widget {
method public int getWindowLayoutType();
method public boolean isAboveAnchor();
method public boolean isAttachedInDecor();
- method @Deprecated public boolean isClipToScreenEnabled();
method public boolean isClippedToScreen();
method public boolean isClippingEnabled();
method public boolean isFocusable();
method public boolean isLaidOutInScreen();
- method @Deprecated public boolean isLayoutInScreenEnabled();
method public boolean isOutsideTouchable();
method public boolean isShowing();
method public boolean isSplitTouchEnabled();
@@ -56263,7 +56257,6 @@ package android.widget {
method public void setAnimationStyle(int);
method public void setAttachedInDecor(boolean);
method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
- method @Deprecated public void setClipToScreenEnabled(boolean);
method public void setClippingEnabled(boolean);
method public void setContentView(android.view.View);
method public void setElevation(float);
@@ -56276,7 +56269,6 @@ package android.widget {
method public void setInputMethodMode(int);
method public void setIsClippedToScreen(boolean);
method public void setIsLaidOutInScreen(boolean);
- method @Deprecated public void setLayoutInScreenEnabled(boolean);
method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
method public void setOutsideTouchable(boolean);
method public void setOverlapAnchor(boolean);
diff --git a/api/removed.txt b/api/removed.txt
index ab5e7e5deeaa..536eba1779f2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -691,6 +691,13 @@ package android.widget {
method protected <T extends android.view.View> T findViewWithTagTraversal(Object);
}
+ public class PopupWindow {
+ method @Deprecated public boolean isClipToScreenEnabled();
+ method @Deprecated public boolean isLayoutInScreenEnabled();
+ method @Deprecated public void setClipToScreenEnabled(boolean);
+ method @Deprecated public void setLayoutInScreenEnabled(boolean);
+ }
+
@android.widget.RemoteViews.RemoteView public class TextView extends android.view.View implements android.view.ViewTreeObserver.OnPreDrawListener {
method public static int getTextColor(android.content.Context, android.content.res.TypedArray, int);
method public static android.content.res.ColorStateList getTextColors(android.content.Context, android.content.res.TypedArray);
diff --git a/api/system-current.txt b/api/system-current.txt
index 7c66af559673..97488c1d569b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -218,7 +218,7 @@ package android {
}
public static final class R.attr {
- field public static final int allowClearUserDataOnFailedRestore = 16844198; // 0x10105a6
+ field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
@@ -317,7 +317,6 @@ package android.app {
public class AppOpsManager {
method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static String[] getOpStrs();
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable int[]);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]);
method public static int opToDefaultMode(@NonNull String);
@@ -936,8 +935,6 @@ package android.app.backup {
method public int restorePackage(String, android.app.backup.RestoreObserver);
method public int restorePackages(long, @Nullable android.app.backup.RestoreObserver, @NonNull java.util.Set<java.lang.String>, @Nullable android.app.backup.BackupManagerMonitor);
method public int restorePackages(long, @Nullable android.app.backup.RestoreObserver, @NonNull java.util.Set<java.lang.String>);
- method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, String[]);
- method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, String[]);
}
public class RestoreSet implements android.os.Parcelable {
@@ -1092,14 +1089,11 @@ package android.app.prediction {
}
public static final class AppTarget.Builder {
- ctor @Deprecated public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId);
ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle);
ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo);
method @NonNull public android.app.prediction.AppTarget build();
method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String);
method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int);
- method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle);
- method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo);
}
public final class AppTargetEvent implements android.os.Parcelable {
@@ -1602,9 +1596,6 @@ package android.content.pm {
method public static void forceSafeLabels();
method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
- field @Deprecated public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
- field @Deprecated public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
- field @Deprecated public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
}
public abstract class PackageManager {
@@ -7102,7 +7093,6 @@ package android.telecom {
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
- method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(@Nullable String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 8f7112266457..5802f6cc09b6 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -9,6 +9,10 @@ package android {
package android.app {
+ public class AppOpsManager {
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable int[]);
+ }
+
public class Notification implements android.os.Parcelable {
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
}
@@ -28,6 +32,25 @@ package android.app.admin {
}
+package android.app.backup {
+
+ public class RestoreSession {
+ method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, String[]);
+ method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, String[]);
+ }
+
+}
+
+package android.app.prediction {
+
+ public static final class AppTarget.Builder {
+ ctor @Deprecated public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId);
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo);
+ }
+
+}
+
package android.content {
public class Intent implements java.lang.Cloneable android.os.Parcelable {
@@ -60,6 +83,16 @@ package android.content {
}
+package android.content.pm {
+
+ public class PackageItemInfo {
+ field @Deprecated public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
+ field @Deprecated public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
+ field @Deprecated public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
+ }
+
+}
+
package android.hardware.hdmi {
public final class HdmiControlManager {
@@ -158,6 +191,14 @@ package android.service.notification {
}
+package android.telecom {
+
+ public class TelecomManager {
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(@Nullable String);
+ }
+
+}
+
package android.telephony {
public class TelephonyManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 181932cf1260..0de0d7503391 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -520,8 +520,6 @@ package android.app.prediction {
method @NonNull public android.app.prediction.AppTarget build();
method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String);
method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int);
- method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle);
- method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo);
}
public final class AppTargetEvent implements android.os.Parcelable {
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 83a5708a2eb3..ef0aac7eac9a 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -1,4 +1,13 @@
// Signature format: 2.0
+package android.app.prediction {
+
+ public static final class AppTarget.Builder {
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo);
+ }
+
+}
+
package android.provider {
public final class DeviceConfig {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index ec02b121d0dd..4e0a8ebdf380 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -611,11 +611,8 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key,
void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
-
const int64_t timeNs = getElapsedRealtimeNs();
// Do not write to disk if we already have in the last few seconds.
- // This is to avoid overwriting files that would have the same name if we
- // write twice in the same second.
if (static_cast<unsigned long long> (timeNs) <
mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds",
@@ -625,13 +622,7 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
mLastActiveMetricsWriteNs = timeNs;
ProtoOutputStream proto;
- for (const auto& pair : mMetricsManagers) {
- const sp<MetricsManager>& metricsManager = pair.second;
- uint64_t configToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
- metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, &proto);
- proto.end(configToken);
- }
+ WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto);
string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
StorageManager::deleteFile(file_name.c_str());
@@ -644,9 +635,24 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
proto.flush(fd.get());
}
-void StatsLogProcessor::LoadActiveConfigsFromDisk() {
+void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream(
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto);
+}
+void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked(
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
+ for (const auto& pair : mMetricsManagers) {
+ const sp<MetricsManager>& metricsManager = pair.second;
+ uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
+ metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto);
+ proto->end(configToken);
+ }
+}
+void StatsLogProcessor::LoadActiveConfigsFromDisk() {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
if (-1 == fd) {
@@ -670,6 +676,19 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() {
StorageManager::deleteFile(file_name.c_str());
return;
}
+ // Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts.
+ SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs);
+ StorageManager::deleteFile(file_name.c_str());
+}
+
+void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList,
+ int64_t currentTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ SetConfigsActiveStateLocked(activeConfigList, currentTimeNs);
+}
+
+void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
+ int64_t currentTimeNs) {
for (int i = 0; i < activeConfigList.config_size(); i++) {
const auto& config = activeConfigList.config(i);
ConfigKey key(config.uid(), config.id());
@@ -679,11 +698,9 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() {
continue;
}
VLOG("Setting active config %s", key.ToString().c_str());
- it->second->loadActiveConfig(config, mTimeBaseNs);
+ it->second->loadActiveConfig(config, currentTimeNs);
}
VLOG("Successfully loaded %d active configs.", activeConfigList.config_size());
-
- StorageManager::deleteFile(file_name.c_str());
}
void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 0dc597b4cb02..92aa425a731d 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -31,17 +31,6 @@ namespace android {
namespace os {
namespace statsd {
-// Keep this in sync with DumpReportReason enum in stats_log.proto
-enum DumpReportReason {
- DEVICE_SHUTDOWN = 1,
- CONFIG_UPDATED = 2,
- CONFIG_REMOVED = 3,
- GET_DATA_CALLED = 4,
- ADB_DUMP = 5,
- CONFIG_RESET = 6,
- STATSCOMPANION_DIED = 7,
- TERMINATION_SIGNAL_RECEIVED = 8
-};
class StatsLogProcessor : public ConfigListener {
public:
@@ -92,9 +81,16 @@ public:
/* Persist configs containing metrics with active activations to disk. */
void SaveActiveConfigsToDisk(int64_t currentTimeNs);
+ /* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */
+ void WriteActiveConfigsToProtoOutputStream(
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
+
/* Load configs containing metrics with active activations from disk. */
void LoadActiveConfigsFromDisk();
+ /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
+ void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
+
// Reset all configs.
void resetConfigs();
@@ -158,6 +154,12 @@ private:
void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
+ void WriteActiveConfigsToProtoOutputStreamLocked(
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
+
+ void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
+ int64_t currentTimeNs);
+
void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
@@ -224,6 +226,7 @@ private:
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
+ FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 623a1f2cdafb..8191d37bb603 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1478,8 +1478,21 @@ void StatsService::binderDied(const wp <IBinder>& who) {
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
if (mProcessor != nullptr) {
ALOGW("Reset statsd upon system server restarts.");
+ int64_t systemServerRestartNs = getElapsedRealtimeNs();
+ ProtoOutputStream proto;
+ mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs,
+ STATSCOMPANION_DIED, &proto);
+
mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST);
mProcessor->resetConfigs();
+
+ std::string serializedActiveConfigs;
+ if (proto.serializeToString(&serializedActiveConfigs)) {
+ ActiveConfigList activeConfigs;
+ if (activeConfigs.ParseFromString(serializedActiveConfigs)) {
+ mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs);
+ }
+ }
}
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 936f7db52c38..a4e6d7fec4cc 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -427,6 +427,7 @@ private:
std::shared_ptr<LogEventQueue> mEventQueue;
+ FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto
index ef8e50bb2596..992983358ae6 100644
--- a/cmds/statsd/src/active_config_list.proto
+++ b/cmds/statsd/src/active_config_list.proto
@@ -26,7 +26,18 @@ message ActiveEventActivation {
// Time left in activation. When this proto is loaded after device boot,
// the activation should be set to active for this duration.
+ // This field will only be set when the state is ACTIVE
optional int64 remaining_ttl_nanos = 2;
+
+ enum State {
+ UNNKNOWN = 0;
+ // This metric should activate for remaining_ttl_nanos when we load the activations.
+ ACTIVE = 1;
+ // When we load the activations, this metric should activate on next boot for the tll
+ // specified in the config.
+ ACTIVATE_ON_BOOT = 2;
+ }
+ optional State state = 3;
}
message ActiveMetric {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b9a4b52d6c48..d69eced236c8 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -298,6 +298,11 @@ message Atom {
VehicleMapServicePacketFailureReported vms_packet_failure_reported = 202;
CarPowerStateChanged car_power_state_changed = 203;
GarageModeInfo garage_mode_info = 204;
+ TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
+ ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
+ ContentCaptureServiceEvents content_capture_service_events = 207;
+ ContentCaptureSessionEvents content_capture_session_events = 208;
+ ContentCaptureFlushed content_capture_flushed = 209;
}
// Pulled events will start at field 10000.
@@ -363,6 +368,7 @@ message Atom {
AppsOnExternalStorageInfo apps_on_external_storage_info = 10057;
FaceSettings face_settings = 10058;
CoolingDevice cooling_device = 10059;
+ AppOps app_ops = 10060;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3342,6 +3348,23 @@ message BinaryPushStateChanged {
optional int32 user_id = 8;
}
+/* Test atom, is not logged anywhere */
+message TestAtomReported {
+ repeated AttributionNode attribution_node = 1;
+ optional int32 int_field = 2;
+ optional int64 long_field = 3;
+ optional float float_field = 4;
+ optional string string_field = 5;
+ optional bool boolean_field = 6;
+ enum State {
+ UNKNOWN = 0;
+ OFF = 1;
+ ON = 2;
+ }
+ optional State state = 7;
+ optional TrainExperimentIds bytes_field = 8 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
/** Represents USB port overheat event. */
message UsbPortOverheatEvent {
/* Temperature of USB port at USB plug event, in 1/10ths of degree C. */
@@ -4812,6 +4835,95 @@ message BuildInformation {
}
/**
+ * Logs information about mismatched caller for content capture.
+ *
+ * Logged from:
+ * frameworks/base/core/java/android/service/contentcapture/ContentCaptureService.java
+ */
+message ContentCaptureCallerMismatchReported {
+ optional string intended_package = 1;
+ optional string calling_package = 2;
+}
+
+/**
+ * Logs information about content capture service events.
+ *
+ * Logged from:
+ * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
+ */
+message ContentCaptureServiceEvents {
+ // The type of event.
+ enum Event {
+ UNKNOWN = 0;
+ ON_CONNECTED = 1;
+ ON_DISCONNECTED = 2;
+ SET_WHITELIST = 3;
+ SET_DISABLED = 4;
+ ON_USER_DATA_REMOVED = 5;
+ }
+ optional Event event = 1;
+ // component/package of content capture service.
+ optional string service_info = 2;
+ // component/package of target.
+ // it's a concatenated list of component/package for SET_WHITELIST event
+ // separated by " ".
+ optional string target_info = 3;
+}
+
+/**
+ * Logs information about content capture session events.
+ *
+ * Logged from:
+ * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
+ */
+message ContentCaptureSessionEvents {
+ // The type of event.
+ enum Event {
+ UNKNOWN = 0;
+ ON_SESSION_STARTED = 1;
+ ON_SESSION_FINISHED = 2;
+ SESSION_NOT_CREATED = 3;
+ }
+ optional int32 session_id = 1;
+ optional Event event = 2;
+ // (n/a on session finished)
+ optional int32 state_flags = 3;
+ // component/package of content capture service.
+ optional string service_info = 4;
+ // component/package of app.
+ // (n/a on session finished)
+ optional string app_info = 5;
+ optional bool is_child_session = 6;
+}
+
+/**
+ * Logs information about session being flushed.
+ *
+ * Logged from:
+ * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
+ */
+message ContentCaptureFlushed {
+ optional int32 session_id = 1;
+ // component/package of content capture service.
+ optional string service_info = 2;
+ // component/package of app.
+ optional string app_info = 3;
+ // session start/finish events
+ optional int32 child_session_started = 4;
+ optional int32 child_session_finished = 5;
+ // count of view events.
+ optional int32 view_appeared_count = 6;
+ optional int32 view_disappeared_count = 7;
+ optional int32 view_text_changed_count = 8;
+
+ // Flush stats.
+ optional int32 max_events = 9;
+ optional int32 idle_flush_freq = 10;
+ optional int32 text_flush_freq = 11;
+ optional int32 flush_reason = 12;
+}
+
+/**
* Pulls on-device BatteryStats power use calculations for the overall device.
*/
message DeviceCalculatedPowerUse {
@@ -6339,3 +6451,41 @@ message GarageModeInfo {
// Whether GarageMode is entered.
optional bool is_garage_mode = 1;
}
+
+/**
+ * Historical app ops data per package.
+ */
+message AppOps {
+ // Uid of the package requesting the op
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Nmae of the package performing the op
+ optional string package_name = 2;
+
+ // operation id; maps to the OP_* constants in AppOpsManager.java
+ optional int32 op_id = 3;
+
+ // The number of times the op was granted while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_granted_count = 4;
+
+ // The number of times the op was granted while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_granted_count = 5;
+
+ // The number of times the op was rejected while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_rejected_count = 6;
+
+ // The number of times the op was rejected while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_rejected_count = 7;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the foreground (only for trusted requests)
+ optional int64 trusted_foreground_duration_millis = 8;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the background (only for trusted requests)
+ optional int64 trusted_background_duration_millis = 9;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 2ffe18e20c24..914d60d3daca 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -260,6 +260,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// Face Settings
{android::util::FACE_SETTINGS,
{.puller = new StatsCompanionServicePuller(android::util::FACE_SETTINGS)}},
+ // App ops
+ {android::util::APP_OPS,
+ {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 9ad7f09ab512..d913427a05dc 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -19,6 +19,7 @@
#include "MetricProducer.h"
using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_ENUM;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
@@ -37,6 +38,7 @@ const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
// for ActiveEventActivation
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
+const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
@@ -165,17 +167,21 @@ void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
continue;
}
auto& activation = it->second;
- // We don't want to change the ttl for future activations, so we set the start_ns
- // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
- activation->start_ns =
- currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
- activation->state = ActivationState::kActive;
- mIsActive = true;
+ if (activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
+ // We don't want to change the ttl for future activations, so we set the start_ns
+ // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
+ activation->start_ns =
+ currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
+ activation->state = ActivationState::kActive;
+ mIsActive = true;
+ } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
+ activation->state = ActivationState::kActiveOnBoot;
+ }
}
}
void MetricProducer::writeActiveMetricToProtoOutputStream(
- int64_t currentTimeNs, ProtoOutputStream* proto) {
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
for (auto& it : mEventActivationMap) {
const int atom_matcher_index = it.first;
@@ -196,9 +202,22 @@ void MetricProducer::writeActiveMetricToProtoOutputStream(
activation->start_ns + activation->ttl_ns - currentTimeNs;
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
(long long)remainingTtlNs);
+ proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
+ ActiveEventActivation::ACTIVE);
+
} else if (ActivationState::kActiveOnBoot == activation->state) {
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
- (long long)activation->ttl_ns);
+ if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
+ proto->write(
+ FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
+ (long long)activation->ttl_ns);
+ proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
+ ActiveEventActivation::ACTIVE);
+ } else if (reason == STATSCOMPANION_DIED) {
+ // We are saving because of system server death, not due to a device shutdown.
+ // Next time we load, we do not want to activate metrics that activate on boot.
+ proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
+ ActiveEventActivation::ACTIVATE_ON_BOOT);
+ }
}
proto->end(activationToken);
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index ec3484c80209..3ddbef44718d 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -35,6 +35,18 @@ namespace android {
namespace os {
namespace statsd {
+// Keep this in sync with DumpReportReason enum in stats_log.proto
+enum DumpReportReason {
+ DEVICE_SHUTDOWN = 1,
+ CONFIG_UPDATED = 2,
+ CONFIG_REMOVED = 3,
+ GET_DATA_CALLED = 4,
+ ADB_DUMP = 5,
+ CONFIG_RESET = 6,
+ STATSCOMPANION_DIED = 7,
+ TERMINATION_SIGNAL_RECEIVED = 8
+};
+
// If the metric has no activation requirement, it will be active once the metric producer is
// created.
// If the metric needs to be activated by atoms, the metric producer will start
@@ -244,7 +256,7 @@ public:
void flushIfExpire(int64_t elapsedTimestampNs);
void writeActiveMetricToProtoOutputStream(
- int64_t currentTimeNs, ProtoOutputStream* proto);
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
protected:
virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -268,8 +280,6 @@ protected:
return mIsActive;
}
- void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs);
-
void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
virtual void prepareFirstBucketLocked() {};
@@ -412,6 +422,7 @@ protected:
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
+ FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 947f37782fcc..207a7dd87215 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -528,14 +528,14 @@ void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t curren
}
void MetricsManager::writeActiveConfigToProtoOutputStream(
- int64_t currentTimeNs, ProtoOutputStream* proto) {
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
for (int metricIndex : mMetricIndexesWithActivation) {
const auto& metric = mAllMetricProducers[metricIndex];
const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_ACTIVE_CONFIG_METRIC);
- metric->writeActiveMetricToProtoOutputStream(currentTimeNs, proto);
+ metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto);
proto->end(metricToken);
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 818131efe138..da3be061cb57 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -141,7 +141,7 @@ public:
void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs);
void writeActiveConfigToProtoOutputStream(
- int64_t currentTimeNs, ProtoOutputStream* proto);
+ int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
private:
// For test only.
@@ -290,6 +290,7 @@ private:
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
+ FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
};
} // namespace statsd
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 4e419b6acddc..a2fd9d42f488 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -330,6 +330,8 @@ message IncidentdDetails {
// Class name of the incident report receiver.
optional string receiver_cls = 4;
+
+ optional string alert_description = 5;
}
message PerfettoDetails {
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index ff1cb4ff1450..f2c6f1ad6759 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -36,12 +36,14 @@ namespace statsd {
using android::util::ProtoOutputStream;
using std::vector;
-using util::FIELD_TYPE_MESSAGE;
using util::FIELD_TYPE_INT32;
using util::FIELD_TYPE_INT64;
+using util::FIELD_TYPE_MESSAGE;
+using util::FIELD_TYPE_STRING;
// field ids in IncidentHeaderProto
const int FIELD_ID_ALERT_ID = 1;
+const int FIELD_ID_REASON = 2;
const int FIELD_ID_CONFIG_KEY = 3;
const int FIELD_ID_CONFIG_KEY_UID = 1;
const int FIELD_ID_CONFIG_KEY_ID = 2;
@@ -57,9 +59,11 @@ const int FIELD_ID_PACKAGE_INFO = 3;
namespace {
void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
- int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) {
+ int64_t metricValue, const ConfigKey& configKey, const string& reason,
+ vector<uint8_t>* protoData) {
ProtoOutputStream headerProto;
headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
+ headerProto.write(FIELD_TYPE_STRING | FIELD_ID_REASON, reason);
uint64_t token =
headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid());
@@ -142,7 +146,8 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int
IncidentReportArgs incidentReport;
vector<uint8_t> protoData;
- getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData);
+ getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey,
+ config.alert_description(), &protoData);
incidentReport.addHeader(protoData);
for (int i = 0; i < config.section_size(); i++) {
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 49b4e904d93c..fe25a257aa67 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -13,12 +13,14 @@
// limitations under the License.
#include "StatsLogProcessor.h"
+#include "StatsService.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "packages/UidMap.h"
+#include "storage/StorageManager.h"
#include "statslog.h"
#include <gmock/gmock.h>
@@ -97,7 +99,8 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
ConfigKey key(100, 12345);
EXPECT_CALL(mockMetricsManager, byteSize())
.Times(1)
- .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95)));
+ .WillRepeatedly(::testing::Return(int(
+ StatsdStats::kMaxMetricsBytesPerConfig * .95)));
// Expect only one broadcast despite always returning a size that should trigger broadcast.
p.flushIfNecessaryLocked(1, key, mockMetricsManager);
@@ -128,7 +131,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
ConfigKey key(100, 12345);
EXPECT_CALL(mockMetricsManager, byteSize())
.Times(1)
- .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
+ .WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1);
@@ -1482,6 +1485,236 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
// }}}---------------------------------------------------------------------------
}
+TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
+ int uid = 9876;
+ long configId = 12341;
+
+ // Create config with 3 metrics:
+ // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
+ // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
+ // Metric 3: Always active
+ StatsdConfig config1;
+ config1.set_id(configId);
+ config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
+ auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
+ *config1.add_atom_matcher() = wakelockAcquireMatcher;
+ *config1.add_atom_matcher() = screenOnMatcher;
+ *config1.add_atom_matcher() = jobStartMatcher;
+ *config1.add_atom_matcher() = jobFinishMatcher;
+
+ long metricId1 = 1234561;
+ long metricId2 = 1234562;
+ long metricId3 = 1234563;
+
+ auto countMetric1 = config1.add_count_metric();
+ countMetric1->set_id(metricId1);
+ countMetric1->set_what(wakelockAcquireMatcher.id());
+ countMetric1->set_bucket(FIVE_MINUTES);
+
+ auto countMetric2 = config1.add_count_metric();
+ countMetric2->set_id(metricId2);
+ countMetric2->set_what(wakelockAcquireMatcher.id());
+ countMetric2->set_bucket(FIVE_MINUTES);
+
+ auto countMetric3 = config1.add_count_metric();
+ countMetric3->set_id(metricId3);
+ countMetric3->set_what(wakelockAcquireMatcher.id());
+ countMetric3->set_bucket(FIVE_MINUTES);
+
+ // Metric 1 activates on boot for wakelock acquire, immediately for screen on.
+ auto metric1Activation = config1.add_metric_activation();
+ metric1Activation->set_metric_id(metricId1);
+ auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+ metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+ metric1ActivationTrigger1->set_ttl_seconds(100);
+ metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+ auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+ metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+ metric1ActivationTrigger2->set_ttl_seconds(200);
+ metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+ // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
+ auto metric2Activation = config1.add_metric_activation();
+ metric2Activation->set_metric_id(metricId2);
+ auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
+ metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
+ metric2ActivationTrigger1->set_ttl_seconds(100);
+ metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+ auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
+ metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
+ metric2ActivationTrigger2->set_ttl_seconds(200);
+ metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+ // Send the config.
+ StatsService service(nullptr, nullptr);
+ string serialized = config1.SerializeAsString();
+ service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+
+ // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
+ StatsdConfig tmpConfig;
+ ConfigKey cfgKey1(uid, configId);
+ EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
+
+ // Metric 1 is not active.
+ // Metric 2 is not active.
+ // Metric 3 is active.
+ // {{{---------------------------------------------------------------------------
+ sp<StatsLogProcessor> processor = service.mProcessor;
+ EXPECT_EQ(1, processor->mMetricsManagers.size());
+ auto it = processor->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ auto& metricsManager1 = it->second;
+ EXPECT_TRUE(metricsManager1->isActive());
+ EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
+
+ auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
+ EXPECT_EQ(metricId1, metricProducer1->getMetricId());
+ EXPECT_FALSE(metricProducer1->isActive());
+
+ auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
+ EXPECT_EQ(metricId2, metricProducer2->getMetricId());
+ EXPECT_FALSE(metricProducer2->isActive());
+
+ auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
+ EXPECT_EQ(metricId3, metricProducer3->getMetricId());
+ EXPECT_TRUE(metricProducer3->isActive());
+
+ // Check event activations.
+ EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
+ EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
+ metric1ActivationTrigger1->atom_matcher_id());
+ const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+ EXPECT_EQ(0, activation1->start_ns);
+ EXPECT_EQ(kNotActive, activation1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
+
+ EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
+ metric1ActivationTrigger2->atom_matcher_id());
+ const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+ EXPECT_EQ(0, activation2->start_ns);
+ EXPECT_EQ(kNotActive, activation2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+
+ EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
+ metric2ActivationTrigger1->atom_matcher_id());
+ const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
+ EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
+ EXPECT_EQ(0, activation3->start_ns);
+ EXPECT_EQ(kNotActive, activation3->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
+
+ EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
+ metric2ActivationTrigger2->atom_matcher_id());
+ const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
+ EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
+ EXPECT_EQ(0, activation4->start_ns);
+ EXPECT_EQ(kNotActive, activation4->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
+ // }}}------------------------------------------------------------------------------
+
+ // Trigger Activation 1 for Metric 1. Should activate on boot.
+ // Trigger Activation 4 for Metric 2. Should activate immediately.
+ long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs);
+ processor->OnLogEvent(event.get());
+
+ event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs);
+ processor->OnLogEvent(event.get());
+
+ // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+ // Metric 2 is active. Activation 4 set to kActive
+ // Metric 3 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_FALSE(metricProducer1->isActive());
+ EXPECT_EQ(0, activation1->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1->state);
+ EXPECT_EQ(0, activation2->start_ns);
+ EXPECT_EQ(kNotActive, activation2->state);
+
+ EXPECT_TRUE(metricProducer2->isActive());
+ EXPECT_EQ(0, activation3->start_ns);
+ EXPECT_EQ(kNotActive, activation3->state);
+ EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
+ EXPECT_EQ(kActive, activation4->state);
+
+ EXPECT_TRUE(metricProducer3->isActive());
+ // }}}-----------------------------------------------------------------------------
+
+ // Can't fake time with StatsService.
+ // Lets get a time close to the system server death time and make sure it's sane.
+ int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
+ EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
+ EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
+
+ // System server dies.
+ service.binderDied(nullptr);
+
+ // We should have a new metrics manager. Lets get it and ensure activation status is restored.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor->mMetricsManagers.size());
+ it = processor->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ auto& metricsManager2 = it->second;
+ EXPECT_TRUE(metricsManager2->isActive());
+ EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
+
+ auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
+ EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
+ EXPECT_FALSE(metricProducer1001->isActive());
+
+ auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
+ EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
+ EXPECT_TRUE(metricProducer1002->isActive());
+
+ auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
+ EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
+ EXPECT_TRUE(metricProducer1003->isActive());
+
+ // Check event activations.
+ // Activation 1 is kActiveOnBoot.
+ // Activation 2 and 3 are not active.
+ // Activation 4 is active.
+ EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
+ EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
+ metric1ActivationTrigger1->atom_matcher_id());
+ const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
+ EXPECT_EQ(0, activation1001->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1001->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
+
+ EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
+ metric1ActivationTrigger2->atom_matcher_id());
+ const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
+ EXPECT_EQ(0, activation1002->start_ns);
+ EXPECT_EQ(kNotActive, activation1002->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
+
+ EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
+ metric2ActivationTrigger1->atom_matcher_id());
+ const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
+ EXPECT_EQ(0, activation1003->start_ns);
+ EXPECT_EQ(kNotActive, activation1003->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
+
+ EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
+ metric2ActivationTrigger2->atom_matcher_id());
+ const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
+ EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
+ EXPECT_EQ(kActive, activation1004->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
+ // }}}------------------------------------------------------------------------------
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f8d7b516ff07..45c716809d8f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4370,6 +4370,7 @@ public class AppOpsManager {
* {@link #getOpsForPackage(int, String, String...)})}.
*
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
diff --git a/core/java/android/app/JobSchedulerImpl.java b/core/java/android/app/JobSchedulerImpl.java
index 5494e2a8855c..e8770185305c 100644
--- a/core/java/android/app/JobSchedulerImpl.java
+++ b/core/java/android/app/JobSchedulerImpl.java
@@ -83,7 +83,7 @@ public class JobSchedulerImpl extends JobScheduler {
@Override
public List<JobInfo> getAllPendingJobs() {
try {
- return mBinder.getAllPendingJobs();
+ return mBinder.getAllPendingJobs().getList();
} catch (RemoteException e) {
return null;
}
@@ -110,7 +110,7 @@ public class JobSchedulerImpl extends JobScheduler {
@Override
public List<JobSnapshot> getAllJobSnapshots() {
try {
- return mBinder.getAllJobSnapshots();
+ return mBinder.getAllJobSnapshots().getList();
} catch (RemoteException e) {
return null;
}
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 7828573885a3..b37120faf281 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -79,10 +79,10 @@ public final class VoiceInteractor {
/** @hide */
public static final String KEY_KILL_SIGNAL = "key_kill_signal";
- IVoiceInteractor mInteractor;
+ @Nullable IVoiceInteractor mInteractor;
- Context mContext;
- Activity mActivity;
+ @Nullable Context mContext;
+ @Nullable Activity mActivity;
boolean mRetaining;
final HandlerCaller mHandlerCaller;
@@ -999,7 +999,9 @@ public final class VoiceInteractor {
// destroyed now
mInteractor = null;
- mActivity.setVoiceInteractor(null);
+ if (mActivity != null) {
+ mActivity.setVoiceInteractor(null);
+ }
}
public boolean submitRequest(Request request) {
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 19f4335893cb..972762152d3a 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -636,6 +636,11 @@ public class SecurityLog {
public int hashCode() {
return Objects.hash(mEvent, mId);
}
+
+ /** @hide */
+ public boolean eventEquals(SecurityEvent other) {
+ return other != null && mEvent.equals(other.mEvent);
+ }
}
/**
* Retrieve all security logs and return immediately.
diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java
index 084b13b61617..933670415f2e 100644
--- a/core/java/android/app/backup/RestoreSession.java
+++ b/core/java/android/app/backup/RestoreSession.java
@@ -214,6 +214,7 @@ public class RestoreSession {
*
* @deprecated use {@link RestoreSession#restorePackages(long, RestoreObserver,
* BackupManagerMonitor, Set)} instead.
+ * @removed
*/
@Deprecated
public int restoreSome(long token, RestoreObserver observer, BackupManagerMonitor monitor,
@@ -240,6 +241,7 @@ public class RestoreSession {
*
* @deprecated use {@link RestoreSession#restorePackages(long, RestoreObserver, Set)}
* instead.
+ * @removed
*/
@Deprecated
public int restoreSome(long token, RestoreObserver observer, String[] packages) {
diff --git a/core/java/android/app/job/IJobScheduler.aidl b/core/java/android/app/job/IJobScheduler.aidl
index 53b33c22dd81..3006f50e54fc 100644
--- a/core/java/android/app/job/IJobScheduler.aidl
+++ b/core/java/android/app/job/IJobScheduler.aidl
@@ -19,6 +19,7 @@ package android.app.job;
import android.app.job.JobInfo;
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
+import android.content.pm.ParceledListSlice;
/**
* IPC interface that supports the app-facing {@link #JobScheduler} api.
@@ -30,8 +31,8 @@ interface IJobScheduler {
int scheduleAsPackage(in JobInfo job, String packageName, int userId, String tag);
void cancel(int jobId);
void cancelAll();
- List<JobInfo> getAllPendingJobs();
+ ParceledListSlice getAllPendingJobs();
JobInfo getPendingJob(int jobId);
List<JobInfo> getStartedJobs();
- List<JobSnapshot> getAllJobSnapshots();
+ ParceledListSlice getAllJobSnapshots();
}
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
index 61e4569c1228..147c5000e333 100644
--- a/core/java/android/app/prediction/AppTarget.java
+++ b/core/java/android/app/prediction/AppTarget.java
@@ -206,6 +206,7 @@ public final class AppTarget implements Parcelable {
/**
* @deprecated Use the other Builder constructors.
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
@@ -244,6 +245,7 @@ public final class AppTarget implements Parcelable {
/**
* @deprecated Use the appropriate constructor.
+ * @removed
*/
@NonNull
@Deprecated
@@ -258,6 +260,7 @@ public final class AppTarget implements Parcelable {
/**
* @deprecated Use the appropriate constructor.
+ * @removed
*/
@NonNull
@Deprecated
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 59ae3347f417..8e40449fa546 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -278,6 +278,12 @@ public class NetworkStatsManager {
return null;
}
+ return querySummary(template, startTime, endTime);
+ }
+
+ /** @hide */
+ public NetworkStats querySummary(NetworkTemplate template, long startTime,
+ long endTime) throws SecurityException, RemoteException {
NetworkStats result;
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startSummaryEnumeration();
@@ -296,6 +302,13 @@ public class NetworkStatsManager {
NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
}
+ /** @hide */
+ public NetworkStats queryDetailsForUid(NetworkTemplate template,
+ long startTime, long endTime, int uid) throws SecurityException {
+ return queryDetailsForUidTagState(template, startTime, endTime, uid,
+ NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
+ }
+
/**
* Query network usage statistics details for a given uid and tag.
*
@@ -340,6 +353,13 @@ public class NetworkStatsManager {
NetworkTemplate template;
template = createTemplate(networkType, subscriberId);
+ return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state);
+ }
+
+ /** @hide */
+ public NetworkStats queryDetailsForUidTagState(NetworkTemplate template,
+ long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
+
NetworkStats result;
try {
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 7fa436084246..749a011153cd 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -281,20 +281,13 @@ public final class UsageStatsManager {
/**
* Gets application usage stats for the given time range, aggregated by the specified interval.
- * <p>The returned list will contain a {@link UsageStats} object for each package that
- * has data for an interval that is a subset of the time range given. To illustrate:</p>
- * <pre>
- * intervalType = INTERVAL_YEARLY
- * beginTime = 2013
- * endTime = 2015 (exclusive)
*
- * Results:
- * 2013 - com.example.alpha
- * 2013 - com.example.beta
- * 2014 - com.example.alpha
- * 2014 - com.example.beta
- * 2014 - com.example.charlie
- * </pre>
+ * <p>
+ * The returned list will contain one or more {@link UsageStats} objects for each package, with
+ * usage data that covers at least the given time range.
+ * Note: The begin and end times of the time range may be expanded to the nearest whole interval
+ * period.
+ * </p>
*
* <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
*
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 71242fbac9a5..7cdd2683905f 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -820,6 +820,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
private String setCallingPackage(String callingPackage) {
final String original = mCallingPackage.get();
mCallingPackage.set(callingPackage);
+ onCallingPackageChanged();
return original;
}
@@ -845,6 +846,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
return pkg;
}
+ /** {@hide} */
+ public final @Nullable String getCallingPackageUnchecked() {
+ return mCallingPackage.get();
+ }
+
+ /** {@hide} */
+ public void onCallingPackageChanged() {
+ }
+
/**
* Opaque token representing the identity of an incoming IPC.
*/
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 50d1785c6059..9e5fcfb6f73e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1881,6 +1881,31 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.REVIEW_PERMISSIONS";
/**
+ * Activity action: Launch UI to show information about the usage
+ * of a given permission. This action would be handled by apps that
+ * want to show details about how and why given permission is being
+ * used.
+ * <p>
+ * <strong>Important:</strong>You must protect the activity that handles
+ * this action with the {@link android.Manifest.permission#START_VIEW_PERMISSION_USAGE
+ * START_VIEW_PERMISSION_USAGE} permission to ensure that only the
+ * system can launch this activity. The system will not launch
+ * activities that are not properly protected.
+ *
+ * <p>
+ * Input: {@code android.intent.extra.PERMISSION_NAME} specifies the permission
+ * for which the launched UI would be targeted.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE)
+ public static final String ACTION_VIEW_PERMISSION_USAGE =
+ "android.intent.action.VIEW_PERMISSION_USAGE";
+
+ /**
* Activity action: Launch UI to manage a default app.
* <p>
* Input: {@link #EXTRA_ROLE_NAME} specifies the role of the default app which will be managed
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index ff7b34773268..081c5ad78762 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -62,6 +62,7 @@ public class PackageItemInfo {
*
* @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_TRIM} instead
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
@@ -75,6 +76,7 @@ public class PackageItemInfo {
*
* @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_SINGLE_LINE} instead
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
@@ -88,6 +90,7 @@ public class PackageItemInfo {
*
* @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_FIRST_LINE} instead
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bdab1e28d20a..b09eada27ce4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -223,6 +223,7 @@ public abstract class PackageManager {
/** @hide */
@IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+ MATCH_ALL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ModuleInfoFlags {}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b0142ea981de..f662b616cf86 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -98,7 +98,7 @@ public final class CameraManager {
* identifiers, while removable cameras have a unique identifier for each
* individual device, even if they are the same model.</p>
*
- * <p>This list doesn't contain physical cameras that can only used as part of a logical
+ * <p>This list doesn't contain physical cameras that can only be used as part of a logical
* multi-camera device.</p>
*
* @return The list of currently connected camera devices.
@@ -263,7 +263,7 @@ public final class CameraManager {
* immutable for a given camera.</p>
*
* <p>From API level 29, this function can also be used to query the capabilities of physical
- * cameras that can only be used as part of logical multi-camera. These cameras cannot not be
+ * cameras that can only be used as part of logical multi-camera. These cameras cannot be
* opened directly via {@link #openCamera}</p>
*
* @param cameraId The id of the camera device to query. This could be either a standalone
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 3a4741a97212..1c9740728f1f 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -976,8 +976,7 @@ public class Build {
* to come. Con permiso, Capitan. The hall is rented, the orchestra
* engaged. It's now time to see if you can dance.</em>
*/
- public static final int Q = CUR_DEVELOPMENT;
-
+ public static final int Q = 29;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index ed8d3f7cb075..a94fd65943a9 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,6 +35,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Class to take an incident report.
@@ -248,6 +250,24 @@ public class IncidentManager {
public @NonNull String toString() {
return "PendingReport(" + getUri().toString() + ")";
}
+
+ /**
+ * @inheritDoc
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof PendingReport)) {
+ return false;
+ }
+ final PendingReport that = (PendingReport) obj;
+ return this.mUri.equals(that.mUri)
+ && this.mFlags == that.mFlags
+ && this.mRequestingPackage.equals(that.mRequestingPackage)
+ && this.mTimestamp == that.mTimestamp;
+ }
}
/**
@@ -355,21 +375,35 @@ public class IncidentManager {
}
/**
- * Listener for the status of an incident report being authroized or denied.
+ * Listener for the status of an incident report being authorized or denied.
*
* @see #requestAuthorization
* @see #cancelAuthorization
*/
public static class AuthListener {
+ Executor mExecutor;
+
IIncidentAuthListener.Stub mBinder = new IIncidentAuthListener.Stub() {
@Override
public void onReportApproved() {
- AuthListener.this.onReportApproved();
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ AuthListener.this.onReportApproved();
+ });
+ } else {
+ AuthListener.this.onReportApproved();
+ }
}
@Override
public void onReportDenied() {
- AuthListener.this.onReportDenied();
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ AuthListener.this.onReportDenied();
+ });
+ } else {
+ AuthListener.this.onReportDenied();
+ }
}
};
@@ -410,7 +444,23 @@ public class IncidentManager {
@RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL)
public void requestAuthorization(int callingUid, String callingPackage, int flags,
AuthListener listener) {
+ requestAuthorization(callingUid, callingPackage, flags,
+ mContext.getMainExecutor(), listener);
+ }
+
+ /**
+ * Request authorization of an incident report.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL)
+ public void requestAuthorization(int callingUid, @NonNull String callingPackage, int flags,
+ @NonNull @CallbackExecutor Executor executor, @NonNull AuthListener listener) {
try {
+ if (listener.mExecutor != null) {
+ throw new RuntimeException("Do not reuse AuthListener objects when calling"
+ + " requestAuthorization");
+ }
+ listener.mExecutor = executor;
getCompanionServiceLocked().authorizeReport(callingUid, callingPackage, null, null,
flags, listener.mBinder);
} catch (RemoteException ex) {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 2b170c2a6587..3ea3bbc959e8 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -640,14 +640,9 @@ public class ZygoteProcess {
ZygoteConfig.USAP_POOL_ENABLED, USAP_POOL_ENABLED_DEFAULT);
if (!propertyString.isEmpty()) {
- if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) {
- // TODO(b/119800099): Tweak usap configuration in jitzygote mode.
- mUsapPoolEnabled = false;
- } else {
- mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean(
- ZygoteConfig.USAP_POOL_ENABLED,
- Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT));
- }
+ mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean(
+ ZygoteConfig.USAP_POOL_ENABLED,
+ Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT));
}
boolean valueChanged = origVal != mUsapPoolEnabled;
@@ -665,6 +660,16 @@ public class ZygoteProcess {
private boolean fetchUsapPoolEnabledPropWithMinInterval() {
final long currentTimestamp = SystemClock.elapsedRealtime();
+ if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) {
+ // TODO(b/119800099): In jitzygote mode, we want to start using USAP processes
+ // only once the boot classpath has been compiled. There is currently no callback
+ // from the runtime to notify the zygote about end of compilation, so for now just
+ // arbitrarily start USAP processes 15 seconds after boot.
+ if (currentTimestamp <= 15000) {
+ return false;
+ }
+ }
+
if (mIsFirstPropCheck
|| (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL)) {
mIsFirstPropCheck = false;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index ee62af57b9a0..69c1295df4f9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -248,6 +248,8 @@ public class StorageManager {
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
/** {@hide} */
public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
+ /** {@hide} */
+ public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL;
/** {@hide} */
public static final int FLAG_FOR_WRITE = 1 << 8;
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index b6ee26114963..1a794ebf2a59 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -25,6 +25,7 @@ import android.util.TimeUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -45,6 +46,7 @@ public class VolumeRecord implements Parcelable {
public String nickname;
public int userFlags;
public long createdMillis;
+ public long lastSeenMillis;
public long lastTrimMillis;
public long lastBenchMillis;
@@ -61,6 +63,7 @@ public class VolumeRecord implements Parcelable {
nickname = parcel.readString();
userFlags = parcel.readInt();
createdMillis = parcel.readLong();
+ lastSeenMillis = parcel.readLong();
lastTrimMillis = parcel.readLong();
lastBenchMillis = parcel.readLong();
}
@@ -73,6 +76,10 @@ public class VolumeRecord implements Parcelable {
return fsUuid;
}
+ public String getNormalizedFsUuid() {
+ return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+ }
+
public String getNickname() {
return nickname;
}
@@ -97,6 +104,7 @@ public class VolumeRecord implements Parcelable {
DebugUtils.flagsToString(VolumeRecord.class, "USER_FLAG_", userFlags));
pw.println();
pw.printPair("createdMillis", TimeUtils.formatForLogging(createdMillis));
+ pw.printPair("lastSeenMillis", TimeUtils.formatForLogging(lastSeenMillis));
pw.printPair("lastTrimMillis", TimeUtils.formatForLogging(lastTrimMillis));
pw.printPair("lastBenchMillis", TimeUtils.formatForLogging(lastBenchMillis));
pw.decreaseIndent();
@@ -155,6 +163,7 @@ public class VolumeRecord implements Parcelable {
parcel.writeString(nickname);
parcel.writeInt(userFlags);
parcel.writeLong(createdMillis);
+ parcel.writeLong(lastSeenMillis);
parcel.writeLong(lastTrimMillis);
parcel.writeLong(lastBenchMillis);
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4ac485099da8..22ce39d1784a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -917,8 +917,17 @@ public final class DocumentsContract {
* @see #getDocumentId(Uri)
*/
public static Uri buildDocumentUri(String authority, String documentId) {
+ return getBaseDocumentUriBuilder(authority).appendPath(documentId).build();
+ }
+
+ /** {@hide} */
+ public static Uri buildBaseDocumentUri(String authority) {
+ return getBaseDocumentUriBuilder(authority).build();
+ }
+
+ private static Uri.Builder getBaseDocumentUriBuilder(String authority) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority).appendPath(PATH_DOCUMENT).appendPath(documentId).build();
+ .authority(authority).appendPath(PATH_DOCUMENT);
}
/**
diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java
index 1391d43b00ca..be20570ef62d 100644
--- a/core/java/android/service/appprediction/AppPredictionService.java
+++ b/core/java/android/service/appprediction/AppPredictionService.java
@@ -39,6 +39,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.service.appprediction.IPredictionService.Stub;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
@@ -46,7 +47,7 @@ import java.util.List;
import java.util.function.Consumer;
/**
- * TODO(b/111701043): Add java docs
+ * A service used to predict app and shortcut usage.
*
* @hide
*/
@@ -58,7 +59,9 @@ public abstract class AppPredictionService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
- * TODO(b/111701043): Add any docs about permissions the service must hold
+ *
+ * <p>The service must also require the {@link android.permission#MANAGE_APP_PREDICTIONS}
+ * permission.
*
* @hide
*/
@@ -145,8 +148,11 @@ public abstract class AppPredictionService extends Service {
@Override
@NonNull
public final IBinder onBind(@NonNull Intent intent) {
- // TODO(b/111701043): Verify that the action is valid
- return mInterface.asBinder();
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
}
/**
@@ -180,7 +186,6 @@ public abstract class AppPredictionService extends Service {
/**
* Called by the client app to request sorting of targets based on prediction rank.
- * TODO(b/111701043): Implement CancellationSignal so caller can cancel a long running request
*/
@MainThread
public abstract void onSortAppTargets(@NonNull AppPredictionSessionId sessionId,
@@ -254,7 +259,6 @@ public abstract class AppPredictionService extends Service {
/**
* Called by the client app to request target predictions. This method is only called if there
* are one or more prediction callbacks registered.
- * TODO(b/111701043): Add java docs
*
* @see #updatePredictions(AppPredictionSessionId, List)
*/
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 02ce87324a4f..08d9733ac815 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -29,6 +29,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
@@ -40,6 +41,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
@@ -114,6 +116,9 @@ public abstract class ContentCaptureService extends Service {
private Handler mHandler;
private IContentCaptureServiceCallback mCallback;
+ private long mCallerMismatchTimeout = 1000;
+ private long mLastCallerMismatchLog;
+
/**
* Binder that receives calls from the system server.
*/
@@ -176,9 +181,10 @@ public abstract class ContentCaptureService extends Service {
new IContentCaptureDirectManager.Stub() {
@Override
- public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
+ public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events, int reason,
+ ContentCaptureOptions options) {
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
- ContentCaptureService.this, Binder.getCallingUid(), events));
+ ContentCaptureService.this, Binder.getCallingUid(), events, reason, options));
}
};
@@ -424,14 +430,23 @@ public abstract class ContentCaptureService extends Service {
}
private void handleSendEvents(int uid,
- @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
+ @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents, int reason,
+ @Nullable ContentCaptureOptions options) {
+ final List<ContentCaptureEvent> events = parceledEvents.getList();
+ if (events.isEmpty()) {
+ Log.w(TAG, "handleSendEvents() received empty list of events");
+ return;
+ }
+
+ // Metrics.
+ final FlushMetrics metrics = new FlushMetrics();
+ ComponentName activityComponent = null;
// Most events belong to the same session, so we can keep a reference to the last one
// to avoid creating too many ContentCaptureSessionId objects
int lastSessionId = NO_SESSION_ID;
ContentCaptureSessionId sessionId = null;
- final List<ContentCaptureEvent> events = parceledEvents.getList();
for (int i = 0; i < events.size(); i++) {
final ContentCaptureEvent event = events.get(i);
if (!handleIsRightCallerFor(event, uid)) continue;
@@ -439,22 +454,44 @@ public abstract class ContentCaptureService extends Service {
if (sessionIdInt != lastSessionId) {
sessionId = new ContentCaptureSessionId(sessionIdInt);
lastSessionId = sessionIdInt;
+ if (i != 0) {
+ writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason);
+ metrics.reset();
+ }
+ }
+ final ContentCaptureContext clientContext = event.getContentCaptureContext();
+ if (activityComponent == null && clientContext != null) {
+ activityComponent = clientContext.getActivityComponent();
}
switch (event.getType()) {
case ContentCaptureEvent.TYPE_SESSION_STARTED:
- final ContentCaptureContext clientContext = event.getContentCaptureContext();
clientContext.setParentSessionId(event.getParentSessionId());
mSessionUids.put(sessionIdInt, uid);
onCreateContentCaptureSession(clientContext, sessionId);
+ metrics.sessionStarted++;
break;
case ContentCaptureEvent.TYPE_SESSION_FINISHED:
mSessionUids.delete(sessionIdInt);
onDestroyContentCaptureSession(sessionId);
+ metrics.sessionFinished++;
+ break;
+ case ContentCaptureEvent.TYPE_VIEW_APPEARED:
+ onContentCaptureEvent(sessionId, event);
+ metrics.viewAppearedCount++;
+ break;
+ case ContentCaptureEvent.TYPE_VIEW_DISAPPEARED:
+ onContentCaptureEvent(sessionId, event);
+ metrics.viewDisappearedCount++;
+ break;
+ case ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED:
+ onContentCaptureEvent(sessionId, event);
+ metrics.viewTextChangedCount++;
break;
default:
onContentCaptureEvent(sessionId, event);
}
}
+ writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason);
}
private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) {
@@ -499,7 +536,13 @@ public abstract class ContentCaptureService extends Service {
if (rightUid != uid) {
Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
+ rightUid);
- //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
+ long now = System.currentTimeMillis();
+ if (now - mLastCallerMismatchLog > mCallerMismatchTimeout) {
+ StatsLog.write(StatsLog.CONTENT_CAPTURE_CALLER_MISMATCH_REPORTED,
+ getPackageManager().getNameForUid(rightUid),
+ getPackageManager().getNameForUid(uid));
+ mLastCallerMismatchLog = now;
+ }
return false;
}
return true;
@@ -530,4 +573,22 @@ public abstract class ContentCaptureService extends Service {
Slog.w(TAG, "Error async reporting result to client: " + e);
}
}
+
+ /**
+ * Logs the metrics for content capture events flushing.
+ */
+ private void writeFlushMetrics(int sessionId, @Nullable ComponentName app,
+ @NonNull FlushMetrics flushMetrics, @Nullable ContentCaptureOptions options,
+ int flushReason) {
+ if (mCallback == null) {
+ Log.w(TAG, "writeSessionFlush(): no server callback");
+ return;
+ }
+
+ try {
+ mCallback.writeSessionFlush(sessionId, app, flushMetrics, options, flushReason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to write flush metrics: " + e);
+ }
+ }
}
diff --git a/core/java/android/service/contentcapture/FlushMetrics.aidl b/core/java/android/service/contentcapture/FlushMetrics.aidl
new file mode 100644
index 000000000000..d0b935f3c4cb
--- /dev/null
+++ b/core/java/android/service/contentcapture/FlushMetrics.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2019, 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.service.contentcapture;
+
+/* @hide */
+parcelable FlushMetrics;
diff --git a/core/java/android/service/contentcapture/FlushMetrics.java b/core/java/android/service/contentcapture/FlushMetrics.java
new file mode 100644
index 000000000000..01f3a12ecea9
--- /dev/null
+++ b/core/java/android/service/contentcapture/FlushMetrics.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 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.service.contentcapture;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Holds metrics for content capture events flushing.
+ *
+ * @hide
+ */
+public final class FlushMetrics implements Parcelable {
+ public int viewAppearedCount;
+ public int viewDisappearedCount;
+ public int viewTextChangedCount;
+ public int sessionStarted;
+ public int sessionFinished;
+
+ /**
+ * Resets all flush metrics.
+ */
+ public void reset() {
+ viewAppearedCount = 0;
+ viewDisappearedCount = 0;
+ viewTextChangedCount = 0;
+ sessionStarted = 0;
+ sessionFinished = 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(sessionStarted);
+ out.writeInt(sessionFinished);
+ out.writeInt(viewAppearedCount);
+ out.writeInt(viewDisappearedCount);
+ out.writeInt(viewTextChangedCount);
+ }
+
+ @NonNull
+ public static final Creator<FlushMetrics> CREATOR = new Creator<FlushMetrics>() {
+ @NonNull
+ @Override
+ public FlushMetrics createFromParcel(Parcel in) {
+ final FlushMetrics flushMetrics = new FlushMetrics();
+ flushMetrics.sessionStarted = in.readInt();
+ flushMetrics.sessionFinished = in.readInt();
+ flushMetrics.viewAppearedCount = in.readInt();
+ flushMetrics.viewDisappearedCount = in.readInt();
+ flushMetrics.viewTextChangedCount = in.readInt();
+ return flushMetrics;
+ }
+
+ @Override
+ public FlushMetrics[] newArray(int size) {
+ return new FlushMetrics[size];
+ }
+ };
+}
diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
index 0550ad3ea20c..ea6e76b47853 100644
--- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
@@ -18,6 +18,8 @@ package android.service.contentcapture;
import android.content.ComponentName;
import android.view.contentcapture.ContentCaptureCondition;
+import android.service.contentcapture.FlushMetrics;
+import android.content.ContentCaptureOptions;
import java.util.List;
@@ -30,4 +32,8 @@ oneway interface IContentCaptureServiceCallback {
void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities);
void setContentCaptureConditions(String packageName, in List<ContentCaptureCondition> conditions);
void disableSelf();
- }
+
+ // Logs aggregated content capture flush metrics to Statsd
+ void writeSessionFlush(int sessionId, in ComponentName app, in FlushMetrics flushMetrics,
+ in ContentCaptureOptions options, int flushReason);
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ae36e4ecde17..c42dc817bec4 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,10 +37,6 @@ public class FeatureFlagUtils {
public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
- public static final String FORCE_GLOBAL_ACTIONS_GRID_ENABLED =
- "settings_global_actions_force_grid_enabled";
- public static final String GLOBAL_ACTIONS_PANEL_ENABLED =
- "settings_global_actions_panel_enabled";
public static final String PIXEL_WALLPAPER_CATEGORY_SWITCH =
"settings_pixel_wallpaper_category_switch";
public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
@@ -57,8 +53,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
- DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false");
- DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true");
DEFAULT_FLAGS.put(PIXEL_WALLPAPER_CATEGORY_SWITCH, "false");
DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d67c8847f3bc..63e14853b51d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2660,6 +2660,9 @@ public final class SurfaceControl implements Parcelable {
*/
@NonNull
public Transaction merge(@NonNull Transaction other) {
+ if (this == other) {
+ return this;
+ }
mResizedSurfaces.putAll(other.mResizedSurfaces);
other.mResizedSurfaces.clear();
nativeMergeTransaction(mNativeObject, other.mNativeObject);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1812d291dd8f..7fdda2a22c17 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,7 +33,6 @@ import android.graphics.Region;
import android.graphics.RenderNode;
import android.os.Build;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
import android.util.AttributeSet;
@@ -120,10 +119,11 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
final Rect mScreenRect = new Rect();
SurfaceSession mSurfaceSession;
- SurfaceControlWithBackground mSurfaceControl;
+ SurfaceControl mSurfaceControl;
// In the case of format changes we switch out the surface in-place
// we need to preserve the old one until the new one has drawn.
- SurfaceControlWithBackground mDeferredDestroySurfaceControl;
+ SurfaceControl mDeferredDestroySurfaceControl;
+ SurfaceControl mBackgroundControl;
final Rect mTmpRect = new Rect();
final Configuration mConfiguration = new Configuration();
@@ -487,6 +487,29 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
}
}
+ private void updateBackgroundVisibilityInTransaction(SurfaceControl viewRoot) {
+ if (mBackgroundControl == null) {
+ return;
+ }
+ if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) {
+ mBackgroundControl.show();
+ mBackgroundControl.setRelativeLayer(viewRoot, Integer.MIN_VALUE);
+ } else {
+ mBackgroundControl.hide();
+ }
+ }
+
+ private void releaseSurfaces() {
+ if (mSurfaceControl != null) {
+ mSurfaceControl.remove();
+ mSurfaceControl = null;
+ }
+ if (mBackgroundControl != null) {
+ mBackgroundControl.remove();
+ mBackgroundControl = null;
+ }
+ }
+
/** @hide */
protected void updateSurface() {
if (!mHaveFrame) {
@@ -553,14 +576,21 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
updateOpaqueFlag();
final String name = "SurfaceView - " + viewRoot.getTitle().toString();
- mSurfaceControl = new SurfaceControlWithBackground(
- name,
- (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
- new SurfaceControl.Builder(mSurfaceSession)
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
- .setFormat(mFormat)
- .setParent(viewRoot.getSurfaceControl())
- .setFlags(mSurfaceFlags));
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name)
+ .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFormat(mFormat)
+ .setParent(viewRoot.getSurfaceControl())
+ .setFlags(mSurfaceFlags)
+ .build();
+ mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Background for -" + name)
+ .setOpaque(true)
+ .setColorLayer()
+ .setParent(mSurfaceControl)
+ .build();
+
} else if (mSurfaceControl == null) {
return;
}
@@ -577,11 +607,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
SurfaceControl.openTransaction();
try {
mSurfaceControl.setLayer(mSubLayer);
+
if (mViewVisibility) {
mSurfaceControl.show();
} else {
mSurfaceControl.hide();
}
+ updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
// While creating the surface, we will set it's initial
// geometry. Outside of that though, we should generally
@@ -667,7 +699,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
}
if (creating) {
- mSurface.copyFrom(mSurfaceControl.mForegroundControl);
+ mSurface.copyFrom(mSurfaceControl);
}
if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
@@ -677,7 +709,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
// existing {@link Surface} will be ignored when the size changes.
// Therefore, we must explicitly recreate the {@link Surface} in these
// cases.
- mSurface.createFrom(mSurfaceControl.mForegroundControl);
+ mSurface.createFrom(mSurfaceControl);
}
if (visible && mSurface.isValid()) {
@@ -724,8 +756,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
if (mSurfaceControl != null && !mSurfaceCreated) {
mSurface.release();
- mSurfaceControl.remove();
- mSurfaceControl = null;
+ releaseSurfaces();
}
}
} catch (Exception ex) {
@@ -826,8 +857,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
private void setParentSpaceRectangle(Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
- applySurfaceTransforms(mSurfaceControl.mForegroundControl, position, frameNumber);
- applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber);
+ applySurfaceTransforms(mSurfaceControl, position, frameNumber);
applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface,
frameNumber);
@@ -892,13 +922,10 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
if (frameNumber > 0) {
final ViewRootImpl viewRoot = getViewRootImpl();
- mRtTransaction.deferTransactionUntilSurface(mSurfaceControl.mForegroundControl, viewRoot.mSurface,
- frameNumber);
- mRtTransaction.deferTransactionUntilSurface(mSurfaceControl.mBackgroundControl, viewRoot.mSurface,
+ mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface,
frameNumber);
}
- mRtTransaction.hide(mSurfaceControl.mForegroundControl);
- mRtTransaction.hide(mSurfaceControl.mBackgroundControl);
+ mRtTransaction.hide(mSurfaceControl);
mRtTransaction.apply();
}
};
@@ -945,7 +972,19 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
* @hide
*/
public void setResizeBackgroundColor(int bgColor) {
- mSurfaceControl.setBackgroundColor(bgColor);
+ if (mBackgroundControl == null) {
+ return;
+ }
+
+ final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
+ Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
+
+ SurfaceControl.openTransaction();
+ try {
+ mBackgroundControl.setColor(colorComponents);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
}
@UnsupportedAppUsage
@@ -1129,134 +1168,6 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
* @return The SurfaceControl for this SurfaceView.
*/
public SurfaceControl getSurfaceControl() {
- return mSurfaceControl.mForegroundControl;
- }
-
- class SurfaceControlWithBackground {
- SurfaceControl mForegroundControl;
- SurfaceControl mBackgroundControl;
- private boolean mOpaque = true;
- public boolean mVisible = false;
-
- public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b)
- throws Exception {
- mForegroundControl = b.setName(name).build();
- mBackgroundControl = b.setName("Background for -" + name)
- // Unset the buffer size of the background color layer.
- .setBufferSize(0, 0)
- .setColorLayer()
- .build();
-
- mOpaque = opaque;
- }
-
- public void setAlpha(float alpha) {
- mForegroundControl.setAlpha(alpha);
- mBackgroundControl.setAlpha(alpha);
- }
-
- public void setLayer(int zorder) {
- mForegroundControl.setLayer(zorder);
- // -3 is below all other child layers as SurfaceView never goes below -2
- mBackgroundControl.setLayer(-3);
- }
-
- public void setPosition(float x, float y) {
- mForegroundControl.setPosition(x, y);
- mBackgroundControl.setPosition(x, y);
- }
-
- public void setBufferSize(int w, int h) {
- mForegroundControl.setBufferSize(w, h);
- // The background surface is a color layer so we do not set a size.
- }
-
- public void setWindowCrop(Rect crop) {
- mForegroundControl.setWindowCrop(crop);
- mBackgroundControl.setWindowCrop(crop);
- }
-
- public void setWindowCrop(int width, int height) {
- mForegroundControl.setWindowCrop(width, height);
- mBackgroundControl.setWindowCrop(width, height);
- }
-
- public void setLayerStack(int layerStack) {
- mForegroundControl.setLayerStack(layerStack);
- mBackgroundControl.setLayerStack(layerStack);
- }
-
- public void setOpaque(boolean isOpaque) {
- mForegroundControl.setOpaque(isOpaque);
- mOpaque = isOpaque;
- updateBackgroundVisibility();
- }
-
- public void setSecure(boolean isSecure) {
- mForegroundControl.setSecure(isSecure);
- }
-
- public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- mForegroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
- mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
- }
-
- public void hide() {
- mForegroundControl.hide();
- mVisible = false;
- updateBackgroundVisibility();
- }
-
- public void show() {
- mForegroundControl.show();
- mVisible = true;
- updateBackgroundVisibility();
- }
-
- public void remove() {
- mForegroundControl.remove();
- mBackgroundControl.remove();
- }
-
- public void release() {
- mForegroundControl.release();
- mBackgroundControl.release();
- }
-
- public void setTransparentRegionHint(Region region) {
- mForegroundControl.setTransparentRegionHint(region);
- mBackgroundControl.setTransparentRegionHint(region);
- }
-
- public void deferTransactionUntil(IBinder handle, long frame) {
- mForegroundControl.deferTransactionUntil(handle, frame);
- mBackgroundControl.deferTransactionUntil(handle, frame);
- }
-
- public void deferTransactionUntil(Surface barrier, long frame) {
- mForegroundControl.deferTransactionUntil(barrier, frame);
- mBackgroundControl.deferTransactionUntil(barrier, frame);
- }
-
- /** Set the color to fill the background with. */
- private void setBackgroundColor(int bgColor) {
- final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
- Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
-
- SurfaceControl.openTransaction();
- try {
- mBackgroundControl.setColor(colorComponents);
- } finally {
- SurfaceControl.closeTransaction();
- }
- }
-
- void updateBackgroundVisibility() {
- if (mOpaque && mVisible) {
- mBackgroundControl.show();
- } else {
- mBackgroundControl.hide();
- }
- }
+ return mSurfaceControl;
}
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2d292ef7b25c..9340b71a5280 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -156,7 +156,9 @@ public final class WindowInsets {
* @param src Source to copy insets from
*/
public WindowInsets(WindowInsets src) {
- this(src.mTypeInsetsMap, src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound,
+ this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
+ src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
+ src.mTypeVisibilityMap, src.mIsRound,
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src));
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
index 8d8117bf9ca1..959bf13c55fc 100644
--- a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -18,6 +18,7 @@ package android.view.contentcapture;
import android.content.pm.ParceledListSlice;
import android.view.contentcapture.ContentCaptureEvent;
+import android.content.ContentCaptureOptions;
/**
* Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
@@ -26,5 +27,6 @@ import android.view.contentcapture.ContentCaptureEvent;
* @hide
*/
oneway interface IContentCaptureDirectManager {
- void sendEvents(in ParceledListSlice events);
+ // reason and options are used only for metrics logging.
+ void sendEvents(in ParceledListSlice events, int reason, in ContentCaptureOptions options);
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 7241664602e9..c5a5f7360321 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -498,7 +498,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
- mDirectServiceInterface.sendEvents(events);
+ mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
} catch (RemoteException e) {
Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
+ ": " + e);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 20fc0b1a9f0b..3779779e9f21 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -887,6 +887,7 @@ public class PopupWindow {
*
* @return true if popup will be clipped to the screen instead of the window, false otherwise
* @deprecated Use {@link #isClippedToScreen()} instead
+ * @removed
*/
@Deprecated
public boolean isClipToScreenEnabled() {
@@ -901,6 +902,7 @@ public class PopupWindow {
* the {@link #update()} methods.</p>
*
* @deprecated Use {@link #setIsClippedToScreen(boolean)} instead
+ * @removed
*/
@Deprecated
public void setClipToScreenEnabled(boolean enabled) {
@@ -988,6 +990,7 @@ public class PopupWindow {
* @return true if the window will always be positioned in screen coordinates.
*
* @deprecated Use {@link #isLaidOutInScreen()} instead
+ * @removed
*/
@Deprecated
public boolean isLayoutInScreenEnabled() {
@@ -1001,6 +1004,7 @@ public class PopupWindow {
*
* @param enabled true if the popup should always be positioned in screen coordinates
* @deprecated Use {@link #setIsLaidOutInScreen(boolean)} instead
+ * @removed
*/
@Deprecated
public void setLayoutInScreenEnabled(boolean enabled) {
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index cc2caca49276..cdb79abbb7ce 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -247,7 +247,6 @@ public abstract class FileSystemProvider extends DocumentsProvider {
}
childId = getDocIdForFile(file);
onDocIdChanged(childId);
- addFolderToMediaStore(getFileForDocId(childId, true));
} else {
try {
if (!file.createNewFile()) {
@@ -259,19 +258,11 @@ public abstract class FileSystemProvider extends DocumentsProvider {
throw new IllegalStateException("Failed to touch " + file + ": " + e);
}
}
+ MediaStore.scanFile(getContext(), file);
return childId;
}
- private void addFolderToMediaStore(@Nullable File visibleFolder) {
- // visibleFolder is null if we're adding a folder to external thumb drive or SD card.
- if (visibleFolder != null) {
- assert (visibleFolder.isDirectory());
-
- MediaStore.scanFile(getContext(), visibleFolder);
- }
- }
-
@Override
public String renameDocument(String docId, String displayName) throws FileNotFoundException {
// Since this provider treats renames as generating a completely new
@@ -293,7 +284,6 @@ public abstract class FileSystemProvider extends DocumentsProvider {
moveInMediaStore(beforeVisibleFile, afterVisibleFile);
if (!TextUtils.equals(docId, afterDocId)) {
- scanFile(afterVisibleFile);
return afterDocId;
} else {
return null;
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index 64f885770336..3900f1674c13 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -231,6 +231,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
@SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
final S castService = (S) this;
mVultureCallback.onServiceDied(castService);
+ handleBindFailure();
}
// Note: we are dumping without a lock held so this is a bit racy but
@@ -406,7 +407,8 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
@NonNull BasePendingRequest<S, I> pendingRequest);
/**
- * Called if {@link Context#bindServiceAsUser} returns {@code false}.
+ * Called if {@link Context#bindServiceAsUser} returns {@code false}, or
+ * if {@link DeathRecipient#binderDied()} is called.
*/
abstract void handleBindFailure();
@@ -431,8 +433,6 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
mBinding = false;
if (!mServiceDied) {
- // TODO(b/126266412): merge these 2 calls?
- handleBindFailure();
handleBinderDied();
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 2736c6a7149f..dbc15b31b6c3 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -172,6 +172,11 @@ public final class Zygote {
*/
public static final int SOCKET_BUFFER_SIZE = 256;
+ /**
+ * @hide for internal use only
+ */
+ private static final int PRIORITY_MAX = -20;
+
/** a prototype instance for a future List.toArray() */
protected static final int[][] INT_ARRAY_2D = new int[0][0];
@@ -236,8 +241,7 @@ public final class Zygote {
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
int targetSdkVersion) {
ZygoteHooks.preFork();
- // Resets nice priority for zygote process.
- resetNicePriority();
+
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir);
@@ -249,6 +253,7 @@ public final class Zygote {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
+
ZygoteHooks.postForkCommon();
return pid;
}
@@ -335,15 +340,16 @@ public final class Zygote {
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
ZygoteHooks.preFork();
- // Resets nice priority for zygote process.
- resetNicePriority();
+
int pid = nativeForkSystemServer(
uid, gid, gids, runtimeFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
+
// Enable tracing as soon as we enter the system_server.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
}
+
ZygoteHooks.postForkCommon();
return pid;
}
@@ -461,13 +467,16 @@ public final class Zygote {
/**
* Fork a new unspecialized app process from the zygote
*
+ * @param usapPoolSocket The server socket the USAP will call accept on
* @param sessionSocketRawFDs Anonymous session sockets that are currently open
+ * @param isPriorityFork Value controlling the process priority level until accept is called
* @return In the Zygote process this function will always return null; in unspecialized app
* processes this function will return a Runnable object representing the new
* application that is passed up from usapMain.
*/
static Runnable forkUsap(LocalServerSocket usapPoolSocket,
- int[] sessionSocketRawFDs) {
+ int[] sessionSocketRawFDs,
+ boolean isPriorityFork) {
FileDescriptor[] pipeFDs = null;
try {
@@ -477,7 +486,8 @@ public final class Zygote {
}
int pid =
- nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), sessionSocketRawFDs);
+ nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(),
+ sessionSocketRawFDs, isPriorityFork);
if (pid == 0) {
IoUtils.closeQuietly(pipeFDs[0]);
@@ -491,8 +501,9 @@ public final class Zygote {
}
private static native int nativeForkUsap(int readPipeFD,
- int writePipeFD,
- int[] sessionSocketRawFDs);
+ int writePipeFD,
+ int[] sessionSocketRawFDs,
+ boolean isPriorityFork);
/**
* This function is used by unspecialized app processes to wait for specialization requests from
@@ -515,6 +526,11 @@ public final class Zygote {
// Load resources
ZygoteInit.nativePreloadGraphicsDriver();
+ // Change the priority to max before calling accept so we can respond to new specialization
+ // requests as quickly as possible. This will be reverted to the default priority in the
+ // native specialization code.
+ boostUsapPriority();
+
while (true) {
try {
sessionSocket = usapPoolSocket.accept();
@@ -618,6 +634,12 @@ public final class Zygote {
null /* classLoader */);
}
+ private static void boostUsapPriority() {
+ nativeBoostUsapPriority();
+ }
+
+ private static native void nativeBoostUsapPriority();
+
private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: ";
/**
@@ -858,15 +880,6 @@ public final class Zygote {
}
/**
- * Resets the calling thread priority to the default value (Thread.NORM_PRIORITY
- * or nice value 0). This updates both the priority value in java.lang.Thread and
- * the nice value (setpriority).
- */
- static void resetNicePriority() {
- Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
- }
-
- /**
* Executes "/system/bin/sh -c &lt;command&gt;" using the exec() system call.
* This method throws a runtime exception if exec() failed, otherwise, this
* method never returns.
diff --git a/core/java/com/android/internal/os/ZygoteConfig.java b/core/java/com/android/internal/os/ZygoteConfig.java
index c6af8c2f4316..6ebcae182b11 100644
--- a/core/java/com/android/internal/os/ZygoteConfig.java
+++ b/core/java/com/android/internal/os/ZygoteConfig.java
@@ -34,4 +34,7 @@ public class ZygoteConfig {
/** The minimum number of processes to keep in the USAP pool */
public static final String USAP_POOL_SIZE_MIN = "usap_pool_size_min";
+
+ /** The number of milliseconds to delay before refilling the USAP pool */
+ public static final String USAP_POOL_REFILL_DELAY_MS = "usap_pool_refill_delay_ms";
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 785256eb6351..1a8ba7632376 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -346,7 +346,7 @@ class ZygoteConnection {
if (zygoteServer.isUsapPoolEnabled()) {
Runnable fpResult =
zygoteServer.fillUsapPool(
- new int[]{mSocket.getFileDescriptor().getInt$()});
+ new int[]{mSocket.getFileDescriptor().getInt$()}, false);
if (fpResult != null) {
zygoteServer.setForkChild();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f9e868fafe50..c5a0af75c9d9 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -822,6 +822,9 @@ public class ZygoteInit {
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;
+ // Set the initial thread priority to the "normal" value.
+ Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
+
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
@@ -881,8 +884,6 @@ public class ZygoteInit {
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
- } else {
- Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 5d1911b9c29a..492bc94804fe 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -66,6 +66,12 @@ class ZygoteServer {
/** The default value used for the USAP_POOL_SIZE_MIN device property */
private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1";
+ /** The default value used for the USAP_REFILL_DELAY_MS device property */
+ private static final String USAP_POOL_REFILL_DELAY_MS_DEFAULT = "3000";
+
+ /** The "not a timestamp" value for the refill delay timestamp mechanism. */
+ private static final int INVALID_TIMESTAMP = -1;
+
/**
* Indicates if this Zygote server can support a unspecialized app process pool. Currently this
* should only be true for the primary and secondary Zygotes, and not the App Zygotes or the
@@ -131,6 +137,18 @@ class ZygoteServer {
*/
private int mUsapPoolRefillThreshold = 0;
+ /**
+ * Number of milliseconds to delay before refilling the pool if it hasn't reached its
+ * minimum value.
+ */
+ private int mUsapPoolRefillDelayMs = -1;
+
+ private enum UsapPoolRefillAction {
+ DELAYED,
+ IMMEDIATE,
+ NONE
+ }
+
ZygoteServer() {
mUsapPoolEventFD = null;
mZygoteSocket = null;
@@ -267,6 +285,13 @@ class ZygoteServer {
mUsapPoolSizeMax);
}
+ final String usapPoolRefillDelayMsPropString = Zygote.getConfigurationProperty(
+ ZygoteConfig.USAP_POOL_REFILL_DELAY_MS, USAP_POOL_REFILL_DELAY_MS_DEFAULT);
+
+ if (!usapPoolRefillDelayMsPropString.isEmpty()) {
+ mUsapPoolRefillDelayMs = Integer.parseInt(usapPoolRefillDelayMsPropString);
+ }
+
// Sanity check
if (mUsapPoolSizeMin >= mUsapPoolSizeMax) {
Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size."
@@ -293,9 +318,16 @@ class ZygoteServer {
}
}
+ private void fetchUsapPoolPolicyPropsIfUnfetched() {
+ if (mIsFirstPropertyCheck) {
+ mIsFirstPropertyCheck = false;
+ fetchUsapPoolPolicyProps();
+ }
+ }
+
/**
- * Checks to see if the current policy says that pool should be refilled, and spawns new USAPs
- * if necessary.
+ * Refill the USAP Pool to the appropriate level, determined by whether this is a priority
+ * refill event or not.
*
* @param sessionSocketRawFDs Anonymous session sockets that are currently open
* @return In the Zygote process this function will always return null; in unspecialized app
@@ -303,39 +335,46 @@ class ZygoteServer {
* application that is passed up from usapMain.
*/
- Runnable fillUsapPool(int[] sessionSocketRawFDs) {
+ Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillUsapPool");
// Ensure that the pool properties have been fetched.
- fetchUsapPoolPolicyPropsWithMinInterval();
+ fetchUsapPoolPolicyPropsIfUnfetched();
int usapPoolCount = Zygote.getUsapPoolCount();
- int numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;
+ int numUsapsToSpawn;
- if (usapPoolCount < mUsapPoolSizeMin
- || numUsapsToSpawn >= mUsapPoolRefillThreshold) {
+ if (isPriorityRefill) {
+ // Refill to min
+ numUsapsToSpawn = mUsapPoolSizeMin - usapPoolCount;
- // Disable some VM functionality and reset some system values
- // before forking.
- ZygoteHooks.preFork();
- Zygote.resetNicePriority();
+ Log.i("zygote",
+ "Priority USAP Pool refill. New USAPs: " + numUsapsToSpawn);
+ } else {
+ // Refill up to max
+ numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;
- while (usapPoolCount++ < mUsapPoolSizeMax) {
- Runnable caller = Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs);
+ Log.i("zygote",
+ "Delayed USAP Pool refill. New USAPs: " + numUsapsToSpawn);
+ }
- if (caller != null) {
- return caller;
- }
- }
+ // Disable some VM functionality and reset some system values
+ // before forking.
+ ZygoteHooks.preFork();
- // Re-enable runtime services for the Zygote. Services for unspecialized app process
- // are re-enabled in specializeAppProcess.
- ZygoteHooks.postForkCommon();
+ while (--numUsapsToSpawn >= 0) {
+ Runnable caller =
+ Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs, isPriorityRefill);
- Log.i("zygote",
- "Filled the USAP pool. New USAPs: " + numUsapsToSpawn);
+ if (caller != null) {
+ return caller;
+ }
}
+ // Re-enable runtime services for the Zygote. Services for unspecialized app process
+ // are re-enabled in specializeAppProcess.
+ ZygoteHooks.postForkCommon();
+
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return null;
@@ -358,7 +397,7 @@ class ZygoteServer {
mUsapPoolEnabled = newStatus;
if (newStatus) {
- return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
+ return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }, false);
} else {
Zygote.emptyUsapPool();
return null;
@@ -377,6 +416,8 @@ class ZygoteServer {
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
+ long usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+
while (true) {
fetchUsapPoolPolicyPropsWithMinInterval();
@@ -428,140 +469,192 @@ class ZygoteServer {
}
}
+ int pollTimeoutMs;
+
+ if (usapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
+ pollTimeoutMs = -1;
+ } else {
+ int elapsedTimeMs =
+ (int) (System.currentTimeMillis() - usapPoolRefillTriggerTimestamp);
+
+ if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
+ // Normalize the poll timeout value when the time between one poll event and the
+ // next pushes us over the delay value. This prevents poll receiving a 0
+ // timeout value, which would result in it returning immediately.
+ pollTimeoutMs = -1;
+ } else {
+ pollTimeoutMs = mUsapPoolRefillDelayMs - elapsedTimeMs;
+ }
+ }
+
+ int pollReturnValue;
try {
- Os.poll(pollFDs, -1);
+ pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
- boolean usapPoolFDRead = false;
-
- while (--pollIndex >= 0) {
- if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
- continue;
- }
-
- if (pollIndex == 0) {
- // Zygote server socket
+ UsapPoolRefillAction usapPoolRefillAction = UsapPoolRefillAction.NONE;
+ if (pollReturnValue == 0) {
+ // The poll timeout has been exceeded. This only occurs when we have finished the
+ // USAP pool refill delay period.
- ZygoteConnection newPeer = acceptCommandPeer(abiList);
- peers.add(newPeer);
- socketFDs.add(newPeer.getFileDescriptor());
+ usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+ usapPoolRefillAction = UsapPoolRefillAction.DELAYED;
- } else if (pollIndex < usapPoolEventFDIndex) {
- // Session socket accepted from the Zygote server socket
+ } else {
+ boolean usapPoolFDRead = false;
- try {
- ZygoteConnection connection = peers.get(pollIndex);
- final Runnable command = connection.processOneCommand(this);
+ while (--pollIndex >= 0) {
+ if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
+ continue;
+ }
- // TODO (chriswailes): Is this extra check necessary?
- if (mIsForkChild) {
- // We're in the child. We should always have a command to run at this
- // stage if processOneCommand hasn't called "exec".
- if (command == null) {
- throw new IllegalStateException("command == null");
+ if (pollIndex == 0) {
+ // Zygote server socket
+
+ ZygoteConnection newPeer = acceptCommandPeer(abiList);
+ peers.add(newPeer);
+ socketFDs.add(newPeer.getFileDescriptor());
+
+ } else if (pollIndex < usapPoolEventFDIndex) {
+ // Session socket accepted from the Zygote server socket
+
+ try {
+ ZygoteConnection connection = peers.get(pollIndex);
+ final Runnable command = connection.processOneCommand(this);
+
+ // TODO (chriswailes): Is this extra check necessary?
+ if (mIsForkChild) {
+ // We're in the child. We should always have a command to run at
+ // this stage if processOneCommand hasn't called "exec".
+ if (command == null) {
+ throw new IllegalStateException("command == null");
+ }
+
+ return command;
+ } else {
+ // We're in the server - we should never have any commands to run.
+ if (command != null) {
+ throw new IllegalStateException("command != null");
+ }
+
+ // We don't know whether the remote side of the socket was closed or
+ // not until we attempt to read from it from processOneCommand. This
+ // shows up as a regular POLLIN event in our regular processing
+ // loop.
+ if (connection.isClosedByPeer()) {
+ connection.closeSocket();
+ peers.remove(pollIndex);
+ socketFDs.remove(pollIndex);
+ }
}
+ } catch (Exception e) {
+ if (!mIsForkChild) {
+ // We're in the server so any exception here is one that has taken
+ // place pre-fork while processing commands or reading / writing
+ // from the control socket. Make a loud noise about any such
+ // exceptions so that we know exactly what failed and why.
- return command;
- } else {
- // We're in the server - we should never have any commands to run.
- if (command != null) {
- throw new IllegalStateException("command != null");
- }
+ Slog.e(TAG, "Exception executing zygote command: ", e);
+
+ // Make sure the socket is closed so that the other end knows
+ // immediately that something has gone wrong and doesn't time out
+ // waiting for a response.
+ ZygoteConnection conn = peers.remove(pollIndex);
+ conn.closeSocket();
- // We don't know whether the remote side of the socket was closed or
- // not until we attempt to read from it from processOneCommand. This
- // shows up as a regular POLLIN event in our regular processing loop.
- if (connection.isClosedByPeer()) {
- connection.closeSocket();
- peers.remove(pollIndex);
socketFDs.remove(pollIndex);
+ } else {
+ // We're in the child so any exception caught here has happened post
+ // fork and before we execute ActivityThread.main (or any other
+ // main() method). Log the details of the exception and bring down
+ // the process.
+ Log.e(TAG, "Caught post-fork exception in child process.", e);
+ throw e;
}
+ } finally {
+ // Reset the child flag, in the event that the child process is a child-
+ // zygote. The flag will not be consulted this loop pass after the
+ // Runnable is returned.
+ mIsForkChild = false;
}
- } catch (Exception e) {
- if (!mIsForkChild) {
- // We're in the server so any exception here is one that has taken place
- // pre-fork while processing commands or reading / writing from the
- // control socket. Make a loud noise about any such exceptions so that
- // we know exactly what failed and why.
-
- Slog.e(TAG, "Exception executing zygote command: ", e);
-
- // Make sure the socket is closed so that the other end knows
- // immediately that something has gone wrong and doesn't time out
- // waiting for a response.
- ZygoteConnection conn = peers.remove(pollIndex);
- conn.closeSocket();
-
- socketFDs.remove(pollIndex);
- } else {
- // We're in the child so any exception caught here has happened post
- // fork and before we execute ActivityThread.main (or any other main()
- // method). Log the details of the exception and bring down the process.
- Log.e(TAG, "Caught post-fork exception in child process.", e);
- throw e;
- }
- } finally {
- // Reset the child flag, in the event that the child process is a child-
- // zygote. The flag will not be consulted this loop pass after the Runnable
- // is returned.
- mIsForkChild = false;
- }
- } else {
- // Either the USAP pool event FD or a USAP reporting pipe.
-
- // If this is the event FD the payload will be the number of USAPs removed.
- // If this is a reporting pipe FD the payload will be the PID of the USAP
- // that was just specialized.
- long messagePayload = -1;
-
- try {
- byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
- int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
-
- if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
- DataInputStream inputStream =
- new DataInputStream(new ByteArrayInputStream(buffer));
+ } else {
+ // Either the USAP pool event FD or a USAP reporting pipe.
+
+ // If this is the event FD the payload will be the number of USAPs removed.
+ // If this is a reporting pipe FD the payload will be the PID of the USAP
+ // that was just specialized. The `continue` statements below ensure that
+ // the messagePayload will always be valid if we complete the try block
+ // without an exception.
+ long messagePayload;
+
+ try {
+ byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
+ int readBytes =
+ Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
+
+ if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
+ DataInputStream inputStream =
+ new DataInputStream(new ByteArrayInputStream(buffer));
+
+ messagePayload = inputStream.readLong();
+ } else {
+ Log.e(TAG, "Incomplete read from USAP management FD of size "
+ + readBytes);
+ continue;
+ }
+ } catch (Exception ex) {
+ if (pollIndex == usapPoolEventFDIndex) {
+ Log.e(TAG, "Failed to read from USAP pool event FD: "
+ + ex.getMessage());
+ } else {
+ Log.e(TAG, "Failed to read from USAP reporting pipe: "
+ + ex.getMessage());
+ }
- messagePayload = inputStream.readLong();
- } else {
- Log.e(TAG, "Incomplete read from USAP management FD of size "
- + readBytes);
continue;
}
- } catch (Exception ex) {
- if (pollIndex == usapPoolEventFDIndex) {
- Log.e(TAG, "Failed to read from USAP pool event FD: "
- + ex.getMessage());
- } else {
- Log.e(TAG, "Failed to read from USAP reporting pipe: "
- + ex.getMessage());
+
+ if (pollIndex > usapPoolEventFDIndex) {
+ Zygote.removeUsapTableEntry((int) messagePayload);
}
- continue;
+ usapPoolFDRead = true;
}
+ }
- if (pollIndex > usapPoolEventFDIndex) {
- Zygote.removeUsapTableEntry((int) messagePayload);
- }
+ if (usapPoolFDRead) {
+ int usapPoolCount = Zygote.getUsapPoolCount();
- usapPoolFDRead = true;
+ if (usapPoolCount < mUsapPoolSizeMin) {
+ // Immediate refill
+ usapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
+ } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {
+ // Delayed refill
+ usapPoolRefillTriggerTimestamp = System.currentTimeMillis();
+ }
}
}
- // Check to see if the USAP pool needs to be refilled.
- if (usapPoolFDRead) {
+ if (usapPoolRefillAction != UsapPoolRefillAction.NONE) {
int[] sessionSocketRawFDs =
socketFDs.subList(1, socketFDs.size())
.stream()
.mapToInt(fd -> fd.getInt$())
.toArray();
- final Runnable command = fillUsapPool(sessionSocketRawFDs);
+ final boolean isPriorityRefill =
+ usapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;
+
+ final Runnable command =
+ fillUsapPool(sessionSocketRawFDs, isPriorityRefill);
if (command != null) {
return command;
+ } else if (isPriorityRefill) {
+ // Schedule a delayed refill to finish refilling the pool.
+ usapPoolRefillTriggerTimestamp = System.currentTimeMillis();
}
}
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 9a10210739e1..1a1615026bdf 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -126,7 +126,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// The height of a window which has not in DIP.
private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
- private static final int SCRIM_LIGHT = 0x99ffffff; // 60% white
+ private static final int SCRIM_LIGHT = 0xe6ffffff; // 90% white
public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8ff16912e932..5267427a6cd3 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -169,6 +169,15 @@ static int gUsapPoolEventFD = -1;
*/
static constexpr int USAP_POOL_SIZE_MAX_LIMIT = 100;
+/** The numeric value for the maximum priority a process may possess. */
+static constexpr int PROCESS_PRIORITY_MAX = -20;
+
+/** The numeric value for the minimum priority a process may possess. */
+static constexpr int PROCESS_PRIORITY_MIN = 19;
+
+/** The numeric value for the normal priority a process should have. */
+static constexpr int PROCESS_PRIORITY_DEFAULT = 0;
+
/**
* A helper class containing accounting information for USAPs.
*/
@@ -893,7 +902,8 @@ static void ClearUsapTable() {
// Utility routine to fork a process from the zygote.
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector<int>& fds_to_close,
- const std::vector<int>& fds_to_ignore) {
+ const std::vector<int>& fds_to_ignore,
+ bool is_priority_fork) {
SetSignalHandlers();
// Curry a failure function.
@@ -926,6 +936,12 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
pid_t pid = fork();
if (pid == 0) {
+ if (is_priority_fork) {
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+ } else {
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
+ }
+
// The child process.
PreApplicationInit();
@@ -1123,6 +1139,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);
+ // Reset the process priority to the default value.
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
+
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork hooks.");
}
@@ -1360,7 +1379,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
fds_to_ignore.push_back(gUsapPoolEventFD);
}
- pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore);
+ pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
@@ -1387,7 +1406,8 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
pid_t pid = ForkCommon(env, true,
fds_to_close,
- fds_to_ignore);
+ fds_to_ignore,
+ true);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permitted_capabilities, effective_capabilities,
@@ -1429,13 +1449,15 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
* zygote in managed code.
* @param managed_session_socket_fds A list of anonymous session sockets that must be ignored by
* the FD hygiene code and automatically "closed" in the new USAP.
+ * @param is_priority_fork Controls the nice level assigned to the newly created process
* @return
*/
static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env,
jclass,
jint read_pipe_fd,
jint write_pipe_fd,
- jintArray managed_session_socket_fds) {
+ jintArray managed_session_socket_fds,
+ jboolean is_priority_fork) {
std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
fds_to_ignore(fds_to_close);
@@ -1457,7 +1479,8 @@ static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env,
fds_to_ignore.push_back(write_pipe_fd);
fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end());
- pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore);
+ pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore,
+ is_priority_fork == JNI_TRUE);
if (usap_pid != 0) {
++gUsapPoolCount;
@@ -1678,6 +1701,10 @@ static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv*
return dl_iterate_phdr(disable_execute_only, nullptr) == 0;
}
+static void com_android_internal_os_Zygote_nativeBoostUsapPriority(JNIEnv* env, jclass) {
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+}
+
static const JNINativeMethod gMethods[] = {
{ "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
@@ -1690,7 +1717,7 @@ static const JNINativeMethod gMethods[] = {
(void *) com_android_internal_os_Zygote_nativePreApplicationInit },
{ "nativeInstallSeccompUidGidFilter", "(II)V",
(void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter },
- { "nativeForkUsap", "(II[I)I",
+ { "nativeForkUsap", "(II[IZ)I",
(void *) com_android_internal_os_Zygote_nativeForkUsap },
{ "nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
@@ -1708,7 +1735,9 @@ static const JNINativeMethod gMethods[] = {
{ "nativeEmptyUsapPool", "()V",
(void *) com_android_internal_os_Zygote_nativeEmptyUsapPool },
{ "nativeDisableExecuteOnly", "()Z",
- (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly }
+ (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly },
+ { "nativeBoostUsapPriority", "()V",
+ (void* ) com_android_internal_os_Zygote_nativeBoostUsapPriority }
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57b770455817..b634bb2a6e9e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4213,6 +4213,15 @@
android:description="@string/permdesc_bindCarrierServices"
android:protectionLevel="signature|privileged" />
+ <!--
+ Allows the holder to start the permission usage screen for an app.
+ <p>Protection level: signature|installer
+ -->
+ <permission android:name="android.permission.START_VIEW_PERMISSION_USAGE"
+ android:label="@string/permlab_startViewPermissionUsage"
+ android:description="@string/permdesc_startViewPermissionUsage"
+ android:protectionLevel="signature|installer" />
+
<!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
flag is set.
@hide -->
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index d90352426311..d4c3565ed0a6 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -26,17 +26,17 @@
android:id="@+id/autofill_save"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="32dp"
- android:paddingTop="16dp"
- android:elevation="32dp"
+ android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
+ android:paddingTop="@dimen/autofill_save_outer_top_padding"
+ android:elevation="@dimen/autofill_elevation"
android:background="?android:attr/colorBackground"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
+ android:paddingStart="@dimen/autofill_save_inner_padding"
+ android:paddingEnd="@dimen/autofill_save_inner_padding"
android:orientation="vertical">
<LinearLayout
@@ -47,17 +47,16 @@
<ImageView
android:id="@+id/autofill_save_icon"
android:scaleType="fitStart"
- android:layout_width="24dp"
- android:layout_height="24dp"/>
+ android:layout_width="@dimen/autofill_save_icon_size"
+ android:layout_height="@dimen/autofill_save_icon_size"/>
<TextView
android:id="@+id/autofill_save_title"
- android:paddingStart="8dp"
+ android:paddingStart="@dimen/autofill_save_title_start_padding"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/autofill_save_title"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Subhead"
android:layout_weight="1">
</TextView>
@@ -67,7 +66,7 @@
android:id="@+id/autofill_save_custom_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
+ android:layout_marginTop="@dimen/autofill_save_scroll_view_top_margin"
android:visibility="gone"/>
</LinearLayout>
@@ -76,7 +75,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
- android:padding="16dp"
+ android:padding="@dimen/autofill_save_button_bar_padding"
android:clipToPadding="false"
android:layout_weight="1"
android:orientation="horizontal">
diff --git a/core/res/res/layout/media_route_chooser_dialog.xml b/core/res/res/layout/media_route_chooser_dialog.xml
index d1c6267ea4e7..cd1c74fd7d7d 100644
--- a/core/res/res/layout/media_route_chooser_dialog.xml
+++ b/core/res/res/layout/media_route_chooser_dialog.xml
@@ -40,7 +40,7 @@
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:paddingLeft="16dp"
+ android:paddingStart="16dp"
android:text="@string/media_route_chooser_searching" />
</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item.xml b/core/res/res/layout/media_route_list_item.xml
index bdca433c1c58..e8460db69582 100644
--- a/core/res/res/layout/media_route_list_item.xml
+++ b/core/res/res/layout/media_route_list_item.xml
@@ -34,6 +34,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
+ android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceMedium"
android:duplicateParentState="true" />
@@ -42,6 +43,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
+ android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceSmall"
android:duplicateParentState="true" />
</LinearLayout>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 167e6727df3d..6f11432bc5aa 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -73,6 +73,11 @@
<dimen name="navigation_bar_width_car_mode">96dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">22dip</dimen>
+ <!-- Desired size of system icons in status bar. -->
+ <dimen name="status_bar_system_icon_size">15dp</dimen>
+ <!-- Intrinsic size of most system icons in status bar. This is the default value that
+ is used if a Drawable reports an intrinsic size of 0. -->
+ <dimen name="status_bar_system_icon_intrinsic_size">17dp</dimen>
<!-- Size of the giant number (unread count) in the notifications -->
<dimen name="status_bar_content_number_size">48sp</dimen>
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
@@ -668,6 +673,16 @@
<dimen name="autofill_dataset_picker_max_width">90%</dimen>
<dimen name="autofill_dataset_picker_max_height">90%</dimen>
+ <!-- Autofill save dialog padding -->
+ <dimen name="autofill_save_outer_top_margin">32dp</dimen>
+ <dimen name="autofill_save_outer_top_padding">16dp</dimen>
+ <dimen name="autofill_elevation">32dp</dimen>
+ <dimen name="autofill_save_inner_padding">16dp</dimen>
+ <dimen name="autofill_save_icon_size">24dp</dimen>
+ <dimen name="autofill_save_title_start_padding">8dp</dimen>
+ <dimen name="autofill_save_scroll_view_top_margin">4dp</dimen>
+ <dimen name="autofill_save_button_bar_padding">16dp</dimen>
+
<!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height -->
<dimen name="autofill_save_custom_subtitle_max_height">20%</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 33bc34197851..fb54566fc959 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2889,6 +2889,99 @@
<!-- ===============================================================
Resources added in version Q of the platform
+ =============================================================== -->
+ <eat-comment />
+
+ <public type="attr" name="packageType" id="0x01010587" />
+ <public type="attr" name="opticalInsetLeft" id="0x01010588" />
+ <public type="attr" name="opticalInsetTop" id="0x01010589" />
+ <public type="attr" name="opticalInsetRight" id="0x0101058a" />
+ <public type="attr" name="opticalInsetBottom" id="0x0101058b" />
+ <public type="attr" name="forceDarkAllowed" id="0x0101058c" />
+ <!-- @hide @SystemApi -->
+ <public type="attr" name="supportsAmbientMode" id="0x0101058d" />
+ <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
+ <public type="attr" name="usesNonSdkApi" id="0x0101058e" />
+ <public type="attr" name="nonInteractiveUiTimeout" id="0x0101058f" />
+ <public type="attr" name="isLightTheme" id="0x01010590" />
+ <public type="attr" name="isSplitRequired" id="0x01010591" />
+ <public type="attr" name="textLocale" id="0x01010592" />
+ <public type="attr" name="settingsSliceUri" id="0x01010593" />
+ <public type="attr" name="shell" id="0x01010594" />
+ <public type="attr" name="interactiveUiTimeout" id="0x01010595" />
+ <public type="attr" name="supportsMultipleDisplays" id="0x01010596" />
+ <public type="attr" name="useAppZygote" id="0x01010597" />
+ <public type="attr" name="selectionDividerHeight" id="0x01010598" />
+ <public type="attr" name="foregroundServiceType" id="0x01010599" />
+ <public type="attr" name="hasFragileUserData" id="0x0101059a" />
+ <public type="attr" name="minAspectRatio" id="0x0101059b" />
+ <public type="attr" name="inheritShowWhenLocked" id="0x0101059c" />
+ <public type="attr" name="zygotePreloadName" id="0x0101059d" />
+ <public type="attr" name="useEmbeddedDex" id="0x0101059e" />
+ <public type="attr" name="forceUriPermissions" id="0x0101059f" />
+ <!-- @hide @SystemApi -->
+ <public type="attr" name="allowClearUserDataOnFailedRestore" id="0x01010600" />
+ <public type="attr" name="allowAudioPlaybackCapture" id="0x01010601" />
+ <public type="attr" name="secureElementName" id="0x01010602" />
+ <public type="attr" name="requestLegacyExternalStorage" id="0x01010603" />
+ <public type="attr" name="enforceStatusBarContrast" id="0x01010604" />
+ <public type="attr" name="enforceNavigationBarContrast" id="0x01010605" />
+ <public type="attr" name="identifier" id="0x01010606" />
+
+ <!-- @hide @SystemApi -->
+ <public type="drawable" name="ic_info" id="0x010800b4" />
+
+ <!-- @hide @SystemApi -->
+ <public type="style" name="Theme.DeviceDefault.DocumentsUI" id="0x010302e2" />
+ <public type="style" name="Theme.DeviceDefault.DayNight" id="0x010302e3" />
+ <public type="style" name="ThemeOverlay.DeviceDefault.Accent.DayNight" id="0x010302e4" />
+
+ <public type="id" name="accessibilityActionPageUp" id="0x01020046" />
+ <public type="id" name="accessibilityActionPageDown" id="0x01020047" />
+ <public type="id" name="accessibilityActionPageLeft" id="0x01020048" />
+ <public type="id" name="accessibilityActionPageRight" id="0x01020049" />
+
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_helpPackageNameKey" id="0x0104001b" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_helpPackageNameValue" id="0x0104001c" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_helpIntentExtraKey" id="0x0104001d" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_helpIntentNameKey" id="0x0104001e" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_feedbackIntentExtraKey" id="0x0104001f" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_feedbackIntentNameKey" id="0x01040020" />
+ <!-- @hide @SystemApi @TestApi -->
+ <public type="string" name="config_defaultAssistant" id="0x01040021" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_defaultBrowser" id="0x01040022" />
+ <!-- @hide @SystemApi @TestApi -->
+ <public type="string" name="config_defaultDialer" id="0x01040023" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_defaultSms" id="0x01040024" />
+
+ <!-- @hide @SystemApi -->
+ <public type="bool" name="config_sendPackageName" id="0x01110000" />
+ <!-- @hide @SystemApi -->
+ <public type="bool" name="config_showDefaultAssistant" id="0x01110001" />
+ <!-- @hide @SystemApi -->
+ <public type="bool" name="config_showDefaultEmergency" id="0x01110002" />
+ <!-- @hide @SystemApi -->
+ <public type="bool" name="config_showDefaultHome" id="0x01110003" />
+ <!-- @hide @TestApi -->
+ <public type="bool" name="config_perDisplayFocusEnabled" id="0x01110004" />
+
+ <!-- @hide @SystemApi -->
+ <public type="dimen" name="config_restrictedIconSize" id="0x01050007" />
+
+ <!-- @hide @SystemApi -->
+ <public type="color" name="system_notification_accent_color" id="0x0106001c" />
+
+
+ <!-- ===============================================================
+ Resources added in version R of the platform
NOTE: add <public> elements within a <public-group> like so:
@@ -2904,113 +2997,28 @@
=============================================================== -->
<eat-comment />
- <public-group type="attr" first-id="0x01010587">
- <public name="packageType" />
- <public name="opticalInsetLeft" />
- <public name="opticalInsetTop" />
- <public name="opticalInsetRight" />
- <public name="opticalInsetBottom" />
- <public name="forceDarkAllowed" />
- <!-- @hide @SystemApi -->
- <public name="supportsAmbientMode" />
- <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
- <public name="usesNonSdkApi" />
- <public name="nonInteractiveUiTimeout" />
- <public name="isLightTheme" />
- <public name="isSplitRequired" />
- <public name="textLocale" />
- <public name="settingsSliceUri" />
- <public name="shell" />
- <public name="interactiveUiTimeout" />
- <public name="__removed6" />
- <public name="supportsMultipleDisplays" />
- <public name="useAppZygote" />
- <public name="__removed1" />
- <public name="__removed2" />
- <public name="__removed3" />
- <public name="__removed4" />
- <public name="__removed5" />
- <public name="selectionDividerHeight" />
- <public name="foregroundServiceType" />
- <public name="hasFragileUserData" />
- <public name="minAspectRatio" />
- <public name="inheritShowWhenLocked" />
- <public name="zygotePreloadName" />
- <public name="useEmbeddedDex" />
- <public name="forceUriPermissions" />
- <!-- @hide @SystemApi -->
- <public name="allowClearUserDataOnFailedRestore"/>
- <public name="allowAudioPlaybackCapture"/>
- <public name="secureElementName" />
- <public name="requestLegacyExternalStorage"/>
- <public name="enforceStatusBarContrast" />
- <public name="enforceNavigationBarContrast" />
- <public name="identifier" />
+ <public-group type="attr" first-id="0x01010607">
</public-group>
- <public-group type="drawable" first-id="0x010800b4">
- <!-- @hide @SystemApi -->
- <public name="ic_info" />
+ <public-group type="drawable" first-id="0x010800b5">
</public-group>
- <public-group type="style" first-id="0x010302e2">
- <!-- @hide @SystemApi -->
- <public name="Theme.DeviceDefault.DocumentsUI" />
- <public name="Theme.DeviceDefault.DayNight" />
- <public name="ThemeOverlay.DeviceDefault.Accent.DayNight" />
+ <public-group type="style" first-id="0x010302e5">
</public-group>
- <public-group type="id" first-id="0x01020046">
- <public name="accessibilityActionPageUp" />
- <public name="accessibilityActionPageDown" />
- <public name="accessibilityActionPageLeft" />
- <public name="accessibilityActionPageRight" />
+ <public-group type="id" first-id="0x0102004a">
</public-group>
- <public-group type="string" first-id="0x0104001b">
- <!-- @hide @SystemApi -->
- <public name="config_helpPackageNameKey" />
- <!-- @hide @SystemApi -->
- <public name="config_helpPackageNameValue" />
- <!-- @hide @SystemApi -->
- <public name="config_helpIntentExtraKey" />
- <!-- @hide @SystemApi -->
- <public name="config_helpIntentNameKey" />
- <!-- @hide @SystemApi -->
- <public name="config_feedbackIntentExtraKey" />
- <!-- @hide @SystemApi -->
- <public name="config_feedbackIntentNameKey" />
- <!-- @hide @SystemApi @TestApi -->
- <public name="config_defaultAssistant" />
- <!-- @hide @SystemApi -->
- <public name="config_defaultBrowser" />
- <!-- @hide @SystemApi @TestApi -->
- <public name="config_defaultDialer" />
- <!-- @hide @SystemApi -->
- <public name="config_defaultSms" />
+ <public-group type="string" first-id="0x01040025">
</public-group>
- <public-group type="bool" first-id="0x01110000">
- <!-- @hide @SystemApi -->
- <public name="config_sendPackageName" />
- <!-- @hide @SystemApi -->
- <public name="config_showDefaultAssistant" />
- <!-- @hide @SystemApi -->
- <public name="config_showDefaultEmergency" />
- <!-- @hide @SystemApi -->
- <public name="config_showDefaultHome" />
- <!-- @hide @TestApi -->
- <public name="config_perDisplayFocusEnabled" />
+ <public-group type="bool" first-id="0x01110005">
</public-group>
- <public-group type="dimen" first-id="0x01050007">
- <!-- @hide @SystemApi -->
- <public name="config_restrictedIconSize" />
+ <public-group type="dimen" first-id="0x01050008">
</public-group>
- <public-group type="color" first-id="0x0106001c">
- <!-- @hide @SystemApi -->
- <public name="system_notification_accent_color" />
+ <public-group type="color" first-id="0x0106001d">
</public-group>
<!-- ===============================================================
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5652c85cf28f..37678dd42512 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1533,7 +1533,7 @@
<!-- Message shown during face acquisition when only the left part of the user's face was detected [CHAR LIMIT=50] -->
<string name="face_acquired_too_left">Move phone to the right.</string>
<!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_poor_gaze">Look at the screen with your eyes open.</string>
+ <string name="face_acquired_poor_gaze">Please look more directly at your device.</string>
<!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] -->
<string name="face_acquired_not_detected">Can\u2019t see your face. Look at the phone.</string>
<!-- Message shown during face acquisition when the device is not steady [CHAR LIMIT=50] -->
@@ -1561,7 +1561,7 @@
<!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=69] -->
<string name="face_error_hw_not_available">Can\u2019t verify face. Hardware not available.</string>
<!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
- <string name="face_error_timeout">Face timeout reached. Try again.</string>
+ <string name="face_error_timeout">Try face authentication again.</string>
<!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=69] -->
<string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string>
<!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
@@ -1726,6 +1726,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_access_notification_policy">Allows the app to read and write Do Not Disturb configuration.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_startViewPermissionUsage">start view permission usage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_startViewPermissionUsage">Allows the holder to start the permission usage for an app. Should never be needed for normal apps.</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1ef2eb4a0435..3a348f05de48 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2279,6 +2279,8 @@
<java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
<java-symbol type="dimen" name="status_bar_icon_size" />
+ <java-symbol type="dimen" name="status_bar_system_icon_size" />
+ <java-symbol type="dimen" name="status_bar_system_icon_intrinsic_size" />
<java-symbol type="drawable" name="list_selector_pressed_holo_dark" />
<java-symbol type="drawable" name="scrubber_control_disabled_holo" />
<java-symbol type="drawable" name="scrubber_control_selector_holo" />
diff --git a/data/etc/com.android.dialer.xml b/data/etc/com.android.dialer.xml
index ccdb21fa5040..405279f8b1a4 100644
--- a/data/etc/com.android.dialer.xml
+++ b/data/etc/com.android.dialer.xml
@@ -24,5 +24,7 @@
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
</privapp-permissions>
</permissions>
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 11d635eaba12..297153d09eca 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -637,8 +637,7 @@ public class GradientDrawable extends Drawable {
* @see #setOrientation(Orientation)
*/
public Orientation getOrientation() {
- updateGradientStateOrientation();
- return mGradientState.mOrientation;
+ return mGradientState.getOrientation();
}
/**
@@ -654,10 +653,7 @@ public class GradientDrawable extends Drawable {
* @see #getOrientation()
*/
public void setOrientation(Orientation orientation) {
- // Update the angle here so that subsequent attempts to obtain the orientation
- // from the angle overwrite previously configured values during inflation
- mGradientState.mAngle = getAngleFromOrientation(orientation);
- mGradientState.mOrientation = orientation;
+ mGradientState.setOrientation(orientation);
mGradientIsDirty = true;
invalidateSelf();
}
@@ -1246,76 +1242,6 @@ public class GradientDrawable extends Drawable {
}
/**
- * Update the orientation of the gradient based on the given angle only if the type is
- * {@link #LINEAR_GRADIENT}
- */
- private void updateGradientStateOrientation() {
- if (mGradientState.mGradient == LINEAR_GRADIENT) {
- int angle = mGradientState.mAngle;
- if (angle % 45 != 0) {
- throw new IllegalArgumentException("Linear gradient requires 'angle' attribute to "
- + "be a multiple of 45");
- }
-
- Orientation orientation;
- switch (angle) {
- case 0:
- orientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- orientation = Orientation.BL_TR;
- break;
- case 90:
- orientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- orientation = Orientation.BR_TL;
- break;
- case 180:
- orientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- orientation = Orientation.TR_BL;
- break;
- case 270:
- orientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- orientation = Orientation.TL_BR;
- break;
- default:
- // Should not get here as exception is thrown above if angle is not multiple
- // of 45 degrees
- orientation = Orientation.LEFT_RIGHT;
- break;
- }
- mGradientState.mOrientation = orientation;
- }
- }
-
- private int getAngleFromOrientation(Orientation orientation) {
- switch (orientation) {
- default:
- case LEFT_RIGHT:
- return 0;
- case BL_TR:
- return 45;
- case BOTTOM_TOP:
- return 90;
- case BR_TL:
- return 135;
- case RIGHT_LEFT:
- return 180;
- case TR_BL:
- return 225;
- case TOP_BOTTOM:
- return 270;
- case TL_BR:
- return 315;
- }
- }
-
- /**
* This checks mGradientIsDirty, and if it is true, recomputes both our drawing
* rectangle (mRect) and the gradient itself, since it depends on our
* rectangle too.
@@ -1344,8 +1270,7 @@ public class GradientDrawable extends Drawable {
if (st.mGradient == LINEAR_GRADIENT) {
final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
- updateGradientStateOrientation();
- switch (st.mOrientation) {
+ switch (st.getOrientation()) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
x1 = x0; y1 = level * r.bottom;
@@ -2056,7 +1981,7 @@ public class GradientDrawable extends Drawable {
int[] mAttrPadding;
public GradientState(Orientation orientation, int[] gradientColors) {
- mOrientation = orientation;
+ setOrientation(orientation);
setGradientColors(gradientColors);
}
@@ -2259,6 +2184,93 @@ public class GradientDrawable extends Drawable {
mCenterY = y;
}
+ public void setOrientation(Orientation orientation) {
+ // Update the angle here so that subsequent attempts to obtain the orientation
+ // from the angle overwrite previously configured values during inflation
+ mAngle = getAngleFromOrientation(orientation);
+ mOrientation = orientation;
+ }
+
+ @NonNull
+ public Orientation getOrientation() {
+ updateGradientStateOrientation();
+ return mOrientation;
+ }
+
+ /**
+ * Update the orientation of the gradient based on the given angle only if the type is
+ * {@link #LINEAR_GRADIENT}
+ */
+ private void updateGradientStateOrientation() {
+ if (mGradient == LINEAR_GRADIENT) {
+ int angle = mAngle;
+ if (angle % 45 != 0) {
+ throw new IllegalArgumentException("Linear gradient requires 'angle' attribute "
+ + "to be a multiple of 45");
+ }
+
+ Orientation orientation;
+ switch (angle) {
+ case 0:
+ orientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ orientation = Orientation.BL_TR;
+ break;
+ case 90:
+ orientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ orientation = Orientation.BR_TL;
+ break;
+ case 180:
+ orientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ orientation = Orientation.TR_BL;
+ break;
+ case 270:
+ orientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ orientation = Orientation.TL_BR;
+ break;
+ default:
+ // Should not get here as exception is thrown above if angle is not multiple
+ // of 45 degrees
+ orientation = Orientation.LEFT_RIGHT;
+ break;
+ }
+ mOrientation = orientation;
+ }
+ }
+
+ private int getAngleFromOrientation(@Nullable Orientation orientation) {
+ if (orientation != null) {
+ switch (orientation) {
+ default:
+ case LEFT_RIGHT:
+ return 0;
+ case BL_TR:
+ return 45;
+ case BOTTOM_TOP:
+ return 90;
+ case BR_TL:
+ return 135;
+ case RIGHT_LEFT:
+ return 180;
+ case TR_BL:
+ return 225;
+ case TOP_BOTTOM:
+ return 270;
+ case TL_BR:
+ return 315;
+ }
+ } else {
+ return 0;
+ }
+ }
+
public void setGradientColors(@Nullable int[] colors) {
mGradientColors = colors;
mSolidColors = null;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8551234afa35..f326ce8d23e9 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -153,8 +153,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) {
mNativeSurface = nullptr;
}
- if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f &&
- !mFixedRenderAhead) {
+ if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
mFixedRenderAhead = false;
mRenderAheadCapacity = 1;
} else {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 62fd48940870..5173f638068d 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -167,8 +167,6 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
LOG_ALWAYS_FATAL_IF(physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0));
mDriverVersion = physDeviceProperties.driverVersion;
- mIsQualcomm = physDeviceProperties.vendorID == 20803;
-
// query to get the initial queue props size
uint32_t queueCount;
mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 31de8030c4c1..dd3c6d0dba81 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -179,13 +179,6 @@ private:
SwapBehavior mSwapBehavior = SwapBehavior::Discard;
GrVkExtensions mExtensions;
uint32_t mDriverVersion = 0;
-
- // TODO: Remove once fix has landed. Temporaryly needed for workaround for setting up AHB
- // surfaces on Qualcomm. Currently if you don't use VkSwapchain Qualcomm is not setting
- // reporting that we need to use one of their private vendor usage bits which greatly effects
- // performance if it is not used.
- bool mIsQualcomm = false;
- bool isQualcomm() const { return mIsQualcomm; }
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index df6b9ed2cdcb..b2cc23e76b8a 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -297,11 +297,6 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
native_window_get_consumer_usage(window, &consumerUsage);
windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
- if (vkManager.isQualcomm()) {
- windowInfo.windowUsageFlags =
- windowInfo.windowUsageFlags | AHARDWAREBUFFER_USAGE_VENDOR_0;
- }
-
/*
* Now we attempt to modify the window!
*/
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 360e8d3e8c71..60d03180c09d 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -123,6 +123,7 @@ public:
size_t size(); // Get the size of the serialized protobuf.
sp<ProtoReader> data(); // Get the reader apis of the data.
bool flush(int fd); // Flush data directly to a file descriptor.
+ bool serializeToString(std::string* out); // Serializes the proto to a string.
/**
* Clears the ProtoOutputStream so the buffer can be reused instead of deallocation/allocation again.
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index ccbb83b2d342..98a68c6482b5 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -448,6 +448,23 @@ ProtoOutputStream::flush(int fd)
return true;
}
+bool
+ProtoOutputStream::serializeToString(std::string* out)
+{
+ if (out == nullptr) return false;
+ if (!compact()) return false;
+
+
+ sp<ProtoReader> reader = mBuffer->read();
+ out->reserve(reader->size());
+ while (reader->hasNext()) {
+ out->append(static_cast<const char*>(static_cast<const void*>(reader->readBuffer())),
+ reader->currentToRead());
+ reader->move(reader->currentToRead());
+ }
+ return true;
+}
+
sp<ProtoReader>
ProtoOutputStream::data()
{
diff --git a/libs/protoutil/tests/EncodedBuffer_test.cpp b/libs/protoutil/tests/EncodedBuffer_test.cpp
index 398af609c083..f895154c4983 100644
--- a/libs/protoutil/tests/EncodedBuffer_test.cpp
+++ b/libs/protoutil/tests/EncodedBuffer_test.cpp
@@ -29,101 +29,101 @@ static void expectPointer(EncodedBuffer::Pointer* p, size_t pos) {
}
TEST(EncodedBufferTest, WriteSimple) {
- EncodedBuffer buffer(TEST_CHUNK_SIZE);
- EXPECT_EQ(buffer.size(), 0UL);
- expectPointer(buffer.wp(), 0);
- EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_SIZE);
+ sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
+ EXPECT_EQ(buffer->size(), 0UL);
+ expectPointer(buffer->wp(), 0);
+ EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_SIZE);
for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
- buffer.writeRawByte(50 + i);
+ buffer->writeRawByte(50 + i);
}
- EXPECT_EQ(buffer.size(), TEST_CHUNK_HALF_SIZE);
- expectPointer(buffer.wp(), TEST_CHUNK_HALF_SIZE);
- EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer->size(), TEST_CHUNK_HALF_SIZE);
+ expectPointer(buffer->wp(), TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_HALF_SIZE);
for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
- buffer.writeRawByte(80 + i);
+ buffer->writeRawByte(80 + i);
}
- EXPECT_EQ(buffer.size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
- expectPointer(buffer.wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
- EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer->size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+ expectPointer(buffer->wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_HALF_SIZE);
// verifies the buffer's data
- expectPointer(buffer.ep(), 0);
+ expectPointer(buffer->ep(), 0);
for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
- EXPECT_EQ(buffer.readRawByte(), 50 + i);
+ EXPECT_EQ(buffer->readRawByte(), 50 + i);
}
for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
- EXPECT_EQ(buffer.readRawByte(), 80 + i);
+ EXPECT_EQ(buffer->readRawByte(), 80 + i);
}
// clears the buffer
- buffer.clear();
- EXPECT_EQ(buffer.size(), 0UL);
- expectPointer(buffer.wp(), 0);
+ buffer->clear();
+ EXPECT_EQ(buffer->size(), 0UL);
+ expectPointer(buffer->wp(), 0);
}
TEST(EncodedBufferTest, WriteVarint) {
- EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
size_t expected_buffer_size = 0;
- EXPECT_EQ(buffer.writeRawVarint32(13), 1);
+ EXPECT_EQ(buffer->writeRawVarint32(13), 1);
expected_buffer_size += 1;
- EXPECT_EQ(buffer.size(), expected_buffer_size);
- EXPECT_EQ(buffer.writeRawVarint32(UINT32_C(-1)), 5);
+ EXPECT_EQ(buffer->size(), expected_buffer_size);
+ EXPECT_EQ(buffer->writeRawVarint32(UINT32_C(-1)), 5);
expected_buffer_size += 5;
- EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer->size(), expected_buffer_size);
- EXPECT_EQ(buffer.writeRawVarint64(200), 2);
+ EXPECT_EQ(buffer->writeRawVarint64(200), 2);
expected_buffer_size += 2;
- EXPECT_EQ(buffer.size(), expected_buffer_size);
- EXPECT_EQ(buffer.writeRawVarint64(UINT64_C(-1)), 10);
+ EXPECT_EQ(buffer->size(), expected_buffer_size);
+ EXPECT_EQ(buffer->writeRawVarint64(UINT64_C(-1)), 10);
expected_buffer_size += 10;
- EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer->size(), expected_buffer_size);
- buffer.writeRawFixed32(UINT32_C(-1));
+ buffer->writeRawFixed32(UINT32_C(-1));
expected_buffer_size += 4;
- EXPECT_EQ(buffer.size(), expected_buffer_size);
- buffer.writeRawFixed64(UINT64_C(-1));
+ EXPECT_EQ(buffer->size(), expected_buffer_size);
+ buffer->writeRawFixed64(UINT64_C(-1));
expected_buffer_size += 8;
- EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer->size(), expected_buffer_size);
- EXPECT_EQ(buffer.writeHeader(32, 2), 2);
+ EXPECT_EQ(buffer->writeHeader(32, 2), 2);
expected_buffer_size += 2;
- EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer->size(), expected_buffer_size);
// verify data are correctly written to the buffer.
- expectPointer(buffer.ep(), 0);
- EXPECT_EQ(buffer.readRawVarint(), UINT32_C(13));
- EXPECT_EQ(buffer.readRawVarint(), UINT32_C(-1));
- EXPECT_EQ(buffer.readRawVarint(), UINT64_C(200));
- EXPECT_EQ(buffer.readRawVarint(), UINT64_C(-1));
- EXPECT_EQ(buffer.readRawFixed32(), UINT32_C(-1));
- EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(-1));
- EXPECT_EQ(buffer.readRawVarint(), UINT64_C((32 << 3) + 2));
- expectPointer(buffer.ep(), expected_buffer_size);
+ expectPointer(buffer->ep(), 0);
+ EXPECT_EQ(buffer->readRawVarint(), UINT32_C(13));
+ EXPECT_EQ(buffer->readRawVarint(), UINT32_C(-1));
+ EXPECT_EQ(buffer->readRawVarint(), UINT64_C(200));
+ EXPECT_EQ(buffer->readRawVarint(), UINT64_C(-1));
+ EXPECT_EQ(buffer->readRawFixed32(), UINT32_C(-1));
+ EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(-1));
+ EXPECT_EQ(buffer->readRawVarint(), UINT64_C((32 << 3) + 2));
+ expectPointer(buffer->ep(), expected_buffer_size);
}
TEST(EncodedBufferTest, Edit) {
- EncodedBuffer buffer(TEST_CHUNK_SIZE);
- buffer.writeRawFixed64(0xdeadbeefdeadbeef);
- EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef));
+ sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
+ buffer->writeRawFixed64(0xdeadbeefdeadbeef);
+ EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef));
- buffer.editRawFixed32(4, 0x12345678);
+ buffer->editRawFixed32(4, 0x12345678);
// fixed 64 is little endian order.
- buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
- EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678deadbeef));
-
- buffer.wp()->rewind();
- expectPointer(buffer.wp(), 0);
- buffer.copy(4, 3);
- buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
- EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678de345678));
+ buffer->ep()->rewind(); // rewind ep for readRawFixed64 from 0
+ EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0x12345678deadbeef));
+
+ buffer->wp()->rewind();
+ expectPointer(buffer->wp(), 0);
+ buffer->copy(4, 3);
+ buffer->ep()->rewind(); // rewind ep for readRawFixed64 from 0
+ EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0x12345678de345678));
}
TEST(EncodedBufferTest, ReadSimple) {
- EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
for (size_t i = 0; i < TEST_CHUNK_3X_SIZE; i++) {
- buffer.writeRawByte(i);
+ buffer->writeRawByte(i);
}
- sp<ProtoReader> reader1 = buffer.read();
+ sp<ProtoReader> reader1 = buffer->read();
EXPECT_EQ(reader1->size(), TEST_CHUNK_3X_SIZE);
EXPECT_EQ(reader1->bytesRead(), 0);
@@ -132,7 +132,7 @@ TEST(EncodedBufferTest, ReadSimple) {
}
EXPECT_EQ(reader1->bytesRead(), TEST_CHUNK_3X_SIZE);
- sp<ProtoReader> reader2 = buffer.read();
+ sp<ProtoReader> reader2 = buffer->read();
uint8_t val = 0;
while (reader2->hasNext()) {
EXPECT_EQ(reader2->next(), val);
@@ -143,10 +143,10 @@ TEST(EncodedBufferTest, ReadSimple) {
}
TEST(EncodedBufferTest, ReadVarint) {
- EncodedBuffer buffer;
+ sp<EncodedBuffer> buffer = new EncodedBuffer();
uint64_t val = UINT64_C(1522865904593);
- size_t len = buffer.writeRawVarint64(val);
- sp<ProtoReader> reader = buffer.read();
+ size_t len = buffer->writeRawVarint64(val);
+ sp<ProtoReader> reader = buffer->read();
EXPECT_EQ(reader->size(), len);
EXPECT_EQ(reader->readRawVarint(), val);
}
diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
index 9d357f3c3363..6282fd553eae 100644
--- a/libs/protoutil/tests/ProtoOutputStream_test.cpp
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -88,6 +88,50 @@ TEST(ProtoOutputStreamTest, Primitives) {
EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
}
+TEST(ProtoOutputStreamTest, SerializeToStringPrimitives) {
+ std::string s = "hello";
+ const char b[5] = { 'a', 'p', 'p', 'l', 'e' };
+
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2));
+
+ PrimitiveProto primitives;
+ std::string serialized;
+ ASSERT_TRUE(proto.serializeToString(&serialized));
+ ASSERT_TRUE(primitives.ParseFromString(serialized));
+ EXPECT_EQ(primitives.val_int32(), 123);
+ EXPECT_EQ(primitives.val_int64(), -1);
+ EXPECT_EQ(primitives.val_float(), -23.5f);
+ EXPECT_EQ(primitives.val_double(), 324.5f);
+ EXPECT_EQ(primitives.val_uint32(), 3424);
+ EXPECT_EQ(primitives.val_uint64(), 57);
+ EXPECT_EQ(primitives.val_fixed32(), -20);
+ EXPECT_EQ(primitives.val_fixed64(), -37);
+ EXPECT_EQ(primitives.val_bool(), true);
+ EXPECT_THAT(primitives.val_string(), StrEq(s.c_str()));
+ EXPECT_THAT(primitives.val_bytes(), StrEq("apple"));
+ EXPECT_EQ(primitives.val_sfixed32(), 63);
+ EXPECT_EQ(primitives.val_sfixed64(), -54);
+ EXPECT_EQ(primitives.val_sint32(), -533);
+ EXPECT_EQ(primitives.val_sint64(), -61224762453LL);
+ EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
+}
+
TEST(ProtoOutputStreamTest, Complex) {
std::string name1 = "cat";
std::string name2 = "dog";
@@ -127,6 +171,47 @@ TEST(ProtoOutputStreamTest, Complex) {
EXPECT_THAT(log2.data(), StrEq("food"));
}
+TEST(ProtoOutputStreamTest, SerializeToStringComplex) {
+ std::string name1 = "cat";
+ std::string name2 = "dog";
+ const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' };
+ const char data2[4] = { 'f', 'o', 'o', 'd' };
+
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72));
+ uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1));
+ // specify the length to test the write(id, bytes, length) function.
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5));
+ proto.end(token1);
+ uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4));
+ proto.end(token2);
+
+ ComplexProto complex;
+ std::string serialized;
+ ASSERT_TRUE(proto.serializeToString(&serialized));
+ ASSERT_TRUE(complex.ParseFromString(serialized));
+ EXPECT_EQ(complex.ints_size(), 3);
+ EXPECT_EQ(complex.ints(0), 23);
+ EXPECT_EQ(complex.ints(1), 101);
+ EXPECT_EQ(complex.ints(2), -72);
+ EXPECT_EQ(complex.logs_size(), 2);
+ ComplexProto::Log log1 = complex.logs(0);
+ EXPECT_EQ(log1.id(), 12);
+ EXPECT_THAT(log1.name(), StrEq(name1.c_str()));
+ EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!'
+ ComplexProto::Log log2 = complex.logs(1);
+ EXPECT_EQ(log2.id(), 98);
+ EXPECT_THAT(log2.name(), StrEq(name2.c_str()));
+ EXPECT_THAT(log2.data(), StrEq("food"));
+}
+
TEST(ProtoOutputStreamTest, Reusability) {
ProtoOutputStream proto;
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32));
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 68c0a22b30c7..435d8d766149 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -509,35 +509,16 @@ public class RingtoneManager {
* @return The position of the {@link Uri}, or -1 if it cannot be found.
*/
public int getRingtonePosition(Uri ringtoneUri) {
-
if (ringtoneUri == null) return -1;
+ final long ringtoneId = ContentUris.parseId(ringtoneUri);
final Cursor cursor = getCursor();
- final int cursorCount = cursor.getCount();
-
- if (!cursor.moveToFirst()) {
- return -1;
- }
-
- // Only create Uri objects when the actual URI changes
- Uri currentUri = null;
- String previousUriString = null;
- for (int i = 0; i < cursorCount; i++) {
- String uriString = cursor.getString(URI_COLUMN_INDEX);
- if (currentUri == null || !uriString.equals(previousUriString)) {
- currentUri = Uri.parse(uriString);
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
+ return cursor.getPosition();
}
-
- if (ringtoneUri.equals(ContentUris.withAppendedId(currentUri, cursor
- .getLong(ID_COLUMN_INDEX)))) {
- return i;
- }
-
- cursor.move(1);
-
- previousUriString = uriString;
}
-
return -1;
}
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 5de56c718570..b3c2bb78862a 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -52,6 +52,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
import java.util.function.ToIntFunction;
/**
@@ -369,10 +370,12 @@ public class ThumbnailUtils {
// If we're okay with something larger than native format, just
// return a frame without up-scaling it
if (size.getWidth() > width && size.getHeight() > height) {
- return mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC);
+ return Objects.requireNonNull(
+ mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC));
} else {
- return mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
- size.getWidth(), size.getHeight());
+ return Objects.requireNonNull(
+ mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
+ size.getWidth(), size.getHeight()));
}
} catch (RuntimeException e) {
throw new IOException("Failed to create thumbnail", e);
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
index a345091aa71e..1f6c2aef657c 100644
--- a/packages/CaptivePortalLogin/Android.bp
+++ b/packages/CaptivePortalLogin/Android.bp
@@ -14,12 +14,11 @@
// limitations under the License.
//
-android_app {
- name: "CaptivePortalLogin",
+java_defaults {
+ name: "CaptivePortalLoginDefaults",
srcs: ["src/**/*.java"],
sdk_version: "system_current",
min_sdk_version: "28",
- certificate: "networkstack",
static_libs: [
"androidx.legacy_legacy-support-v4",
"metrics-constants-protos",
@@ -27,3 +26,18 @@ android_app {
],
manifest: "AndroidManifest.xml",
}
+
+android_app {
+ name: "CaptivePortalLogin",
+ defaults: ["CaptivePortalLoginDefaults"],
+ certificate: "networkstack",
+}
+
+// Alternative CaptivePortalLogin signed with the platform cert, to use
+// with InProcessNetworkStack.
+android_app {
+ name: "PlatformCaptivePortalLogin",
+ defaults: ["CaptivePortalLoginDefaults"],
+ certificate: "platform",
+ overrides: ["CaptivePortalLogin"],
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 835cedf9cdd9..9b6ab06c4708 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -131,6 +131,8 @@ public class CarStatusBar extends StatusBar implements
// The container for the notifications.
private CarNotificationView mNotificationView;
private RecyclerView mNotificationList;
+ // The controller for the notification view.
+ private NotificationViewController mNotificationViewController;
// The state of if the notification list is currently showing the bottom.
private boolean mNotificationListAtBottom;
// Was the notification list at the bottom when the user first touched the screen
@@ -312,6 +314,7 @@ public class CarStatusBar extends StatusBar implements
public void showKeyguard() {
super.showKeyguard();
updateNavBarForKeyguardContent();
+ dismissKeyguardWhenUserSwitcherNotDisplayed();
}
/**
@@ -544,7 +547,7 @@ public class CarStatusBar extends StatusBar implements
}
});
- NotificationViewController mNotificationViewController = new NotificationViewController(
+ mNotificationViewController = new NotificationViewController(
mNotificationView,
PreprocessingManager.getInstance(mContext),
carNotificationListener,
@@ -651,9 +654,11 @@ public class CarStatusBar extends StatusBar implements
mStatusBarWindowController.setPanelVisible(false);
mNotificationView.setVisibility(View.INVISIBLE);
mNotificationList.setClipBounds(null);
+ mNotificationViewController.setIsInForeground(false);
// let the status bar know that the panel is closed
setPanelExpanded(false);
} else {
+ mNotificationViewController.setIsInForeground(true);
// let the status bar know that the panel is open
mNotificationView.setVisibleNotificationsAsSeen();
setPanelExpanded(true);
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 43d7d8fd0ac4..e731b45010a4 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -70,6 +70,10 @@ public class DynamicSystemInstallationService extends Service
private static final String TAG = "DynSystemInstallationService";
+
+ // TODO (b/131866826): This is currently for test only. Will move this to System API.
+ static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
+
/*
* Intent actions
*/
@@ -122,6 +126,9 @@ public class DynamicSystemInstallationService extends Service
private long mInstalledSize;
private boolean mJustCancelledByUser;
+ // This is for testing only now
+ private boolean mEnableWhenCompleted;
+
private InstallationAsyncTask mInstallTask;
@@ -178,6 +185,11 @@ public class DynamicSystemInstallationService extends Service
public void onResult(int result, Throwable detail) {
if (result == RESULT_OK) {
postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED, null);
+
+ // For testing: enable DSU and restart the device when install completed
+ if (mEnableWhenCompleted) {
+ executeRebootToDynSystemCommand();
+ }
return;
}
@@ -224,6 +236,7 @@ public class DynamicSystemInstallationService extends Service
String url = intent.getDataString();
mSystemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
mUserdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
+ mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
mInstallTask = new InstallationAsyncTask(
url, mSystemSize, mUserdataSize, this, mDynSystem, this);
@@ -275,7 +288,7 @@ public class DynamicSystemInstallationService extends Service
private void executeRebootToDynSystemCommand() {
boolean enabled = false;
- if (mInstallTask != null && mInstallTask.getStatus() == FINISHED) {
+ if (mInstallTask != null && mInstallTask.getResult() == RESULT_OK) {
enabled = mInstallTask.commit();
} else if (isDynamicSystemInstalled()) {
enabled = mDynSystem.setEnable(true);
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index b1c09381823b..8a2948b04e3e 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -92,6 +92,8 @@ public class VerificationActivity extends Activity {
Uri url = callingIntent.getData();
long systemSize = callingIntent.getLongExtra(KEY_SYSTEM_SIZE, 0);
long userdataSize = callingIntent.getLongExtra(KEY_USERDATA_SIZE, 0);
+ boolean enableWhenCompleted = callingIntent.getBooleanExtra(
+ DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, false);
sVerifiedUrl = url.toString();
@@ -101,6 +103,8 @@ public class VerificationActivity extends Activity {
intent.setAction(DynamicSystemClient.ACTION_START_INSTALL);
intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
+ intent.putExtra(
+ DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, enableWhenCompleted);
Log.d(TAG, "Starting Installation Service");
startServiceAsUser(intent, UserHandle.SYSTEM);
diff --git a/packages/NetworkPermissionConfig/Android.bp b/packages/NetworkPermissionConfig/Android.bp
index d0d3276c0e32..6e50459a1dd3 100644
--- a/packages/NetworkPermissionConfig/Android.bp
+++ b/packages/NetworkPermissionConfig/Android.bp
@@ -14,15 +14,28 @@
// limitations under the License.
//
-// Stub APK to define permissions for NetworkStack
-android_app {
- name: "NetworkPermissionConfig",
+java_defaults {
+ name: "NetworkPermissionConfigDefaults",
// TODO: mark app as hasCode=false in manifest once soong stops complaining about apps without
// a classes.dex.
srcs: ["src/**/*.java"],
platform_apis: true,
min_sdk_version: "28",
- certificate: "networkstack",
privileged: true,
manifest: "AndroidManifest.xml",
}
+
+// Stub APK to define permissions for NetworkStack
+android_app {
+ name: "NetworkPermissionConfig",
+ defaults: ["NetworkPermissionConfigDefaults"],
+ certificate: "networkstack",
+}
+
+// Alternative stub APK signed with platform certificate. To use with InProcessNetworkStack.
+android_app {
+ name: "PlatformNetworkPermissionConfig",
+ defaults: ["NetworkPermissionConfigDefaults"],
+ certificate: "platform",
+ overrides: ["NetworkPermissionConfig"],
+}
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 62de2ba45455..e15526a571f5 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -98,8 +98,6 @@ java_defaults {
optimize: {
proguard_flags_files: ["proguard.flags"],
},
- // The permission configuration *must* be included to ensure security of the device
- required: ["NetworkPermissionConfig"],
}
// Non-updatable network stack running in the system server process for devices not using the module
@@ -108,6 +106,10 @@ android_app {
defaults: ["NetworkStackAppCommon"],
certificate: "platform",
manifest: "AndroidManifest_InProcess.xml",
+ // InProcessNetworkStack is a replacement for NetworkStack
+ overrides: ["NetworkStack"],
+ // The permission configuration *must* be included to ensure security of the device
+ required: ["PlatformNetworkPermissionConfig"],
}
// Updatable network stack packaged as an application
@@ -116,6 +118,9 @@ android_app {
defaults: ["NetworkStackAppCommon"],
certificate: "networkstack",
manifest: "AndroidManifest.xml",
+ use_embedded_native_libs: true,
+ // The permission configuration *must* be included to ensure security of the device
+ required: ["NetworkPermissionConfig"],
}
genrule {
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 252b90fea840..4c4448482e03 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -41,7 +41,9 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!-- Signature permission defined in NetworkStackStub -->
<uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
- <application>
+ <application
+ android:extractNativeLibs="false"
+ android:persistent="true">
<service android:name="com.android.server.NetworkStackService">
<intent-filter>
<action android:name="android.net.INetworkStackConnector"/>
diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml
index 3da566f88659..d00a55143605 100644
--- a/packages/NetworkStack/AndroidManifestBase.xml
+++ b/packages/NetworkStack/AndroidManifestBase.xml
@@ -24,7 +24,6 @@
android:label="NetworkStack"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
- android:persistent="true"
android:usesCleartextTraffic="true">
<service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 359c85983a94..f05431968684 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -155,7 +155,8 @@ public class ApfFilter {
DROPPED_ETHERTYPE_BLACKLISTED,
DROPPED_ARP_REPLY_SPA_NO_HOST,
DROPPED_IPV4_KEEPALIVE_ACK,
- DROPPED_IPV6_KEEPALIVE_ACK;
+ DROPPED_IPV6_KEEPALIVE_ACK,
+ DROPPED_IPV4_NATT_KEEPALIVE;
// Returns the negative byte offset from the end of the APF data segment for
// a given counter.
@@ -857,12 +858,104 @@ public class ApfFilter {
}
}
- // A class to hold keepalive ack information.
- private abstract static class TcpKeepaliveAck {
+ // TODO: Refactor these subclasses to avoid so much repetition.
+ private abstract static class KeepalivePacket {
// Note that the offset starts from IP header.
// These must be added ether header length when generating program.
static final int IP_HEADER_OFFSET = 0;
+ static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
+ // Append a filter for this keepalive ack to {@code gen}.
+ // Jump to drop if it matches the keepalive ack.
+ // Jump to the next filter if packet doesn't match the keepalive ack.
+ abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+ }
+
+ // A class to hold NAT-T keepalive ack information.
+ private class NattKeepaliveResponse extends KeepalivePacket {
+ static final int UDP_LENGTH_OFFSET = 4;
+ static final int UDP_HEADER_LEN = 8;
+
+ protected class NattKeepaliveResponseData {
+ public final byte[] srcAddress;
+ public final int srcPort;
+ public final byte[] dstAddress;
+ public final int dstPort;
+
+ NattKeepaliveResponseData(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
+ srcAddress = sentKeepalivePacket.dstAddress;
+ srcPort = sentKeepalivePacket.dstPort;
+ dstAddress = sentKeepalivePacket.srcAddress;
+ dstPort = sentKeepalivePacket.srcPort;
+ }
+ }
+
+ protected final NattKeepaliveResponseData mPacket;
+ protected final byte[] mSrcDstAddr;
+ protected final byte[] mPortFingerprint;
+ // NAT-T keepalive packet
+ protected final byte[] mPayload = {(byte) 0xff};
+
+ NattKeepaliveResponse(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
+ mPacket = new NattKeepaliveResponseData(sentKeepalivePacket);
+ mSrcDstAddr = concatArrays(mPacket.srcAddress, mPacket.dstAddress);
+ mPortFingerprint = generatePortFingerprint(mPacket.srcPort, mPacket.dstPort);
+ }
+
+ byte[] generatePortFingerprint(int srcPort, int dstPort) {
+ final ByteBuffer fp = ByteBuffer.allocate(4);
+ fp.order(ByteOrder.BIG_ENDIAN);
+ fp.putShort((short) srcPort);
+ fp.putShort((short) dstPort);
+ return fp.array();
+ }
+
+ @Override
+ void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked();
+
+ gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
+
+ // A NAT-T keepalive packet contains 1 byte payload with the value 0xff
+ // Check payload length is 1
+ gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addAdd(UDP_HEADER_LEN);
+ gen.addSwap();
+ gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET);
+ gen.addNeg(Register.R1);
+ gen.addAddR1();
+ gen.addJumpIfR0NotEquals(1, nextFilterLabel);
+
+ // Check that the ports match
+ gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addAdd(ETH_HEADER_LEN);
+ gen.addJumpIfBytesNotEqual(Register.R0, mPortFingerprint, nextFilterLabel);
+
+ // Payload offset = R0 + UDP header length
+ gen.addAdd(UDP_HEADER_LEN);
+ gen.addJumpIfBytesNotEqual(Register.R0, mPayload, nextFilterLabel);
+
+ maybeSetupCounter(gen, Counter.DROPPED_IPV4_NATT_KEEPALIVE);
+ gen.addJump(mCountAndDropLabel);
+ gen.defineLabel(nextFilterLabel);
+ }
+
+ public String toString() {
+ try {
+ return String.format("%s -> %s",
+ NetworkStackUtils.addressAndPortToString(
+ InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
+ NetworkStackUtils.addressAndPortToString(
+ InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort));
+ } catch (UnknownHostException e) {
+ return "Unknown host";
+ }
+ }
+ }
+
+ // A class to hold TCP keepalive ack information.
+ private abstract static class TcpKeepaliveAck extends KeepalivePacket {
protected static class TcpKeepaliveAckData {
public final byte[] srcAddress;
public final int srcPort;
@@ -870,6 +963,7 @@ public class ApfFilter {
public final int dstPort;
public final int seq;
public final int ack;
+
// Create the characteristics of the ack packet from the sent keepalive packet.
TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
srcAddress = sentKeepalivePacket.dstAddress;
@@ -902,28 +996,18 @@ public class ApfFilter {
return fp.array();
}
- static byte[] concatArrays(final byte[]... arr) {
- int size = 0;
- for (byte[] a : arr) {
- size += a.length;
- }
- final byte[] result = new byte[size];
- int offset = 0;
- for (byte[] a : arr) {
- System.arraycopy(a, 0, result, offset, a.length);
- offset += a.length;
- }
- return result;
- }
-
public String toString() {
- return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d",
- mPacket.srcAddress,
- mPacket.srcPort,
- mPacket.dstAddress,
- mPacket.dstPort,
- mPacket.seq,
- mPacket.ack);
+ try {
+ return String.format("%s -> %s , seq=%d, ack=%d",
+ NetworkStackUtils.addressAndPortToString(
+ InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
+ NetworkStackUtils.addressAndPortToString(
+ InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort),
+ Integer.toUnsignedLong(mPacket.seq),
+ Integer.toUnsignedLong(mPacket.ack));
+ } catch (UnknownHostException e) {
+ return "Unknown host";
+ }
}
// Append a filter for this keepalive ack to {@code gen}.
@@ -933,7 +1017,6 @@ public class ApfFilter {
}
private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
- private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
this(new TcpKeepaliveAckData(sentKeepalivePacket));
@@ -987,7 +1070,7 @@ public class ApfFilter {
@Override
void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet");
+ throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet");
}
}
@@ -997,7 +1080,7 @@ public class ApfFilter {
@GuardedBy("this")
private ArrayList<Ra> mRas = new ArrayList<>();
@GuardedBy("this")
- private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>();
+ private SparseArray<KeepalivePacket> mKeepalivePackets = new SparseArray<>();
// There is always some marginal benefit to updating the installed APF program when an RA is
// seen because we can extend the program's lifetime slightly, but there is some cost to
@@ -1171,9 +1254,12 @@ public class ApfFilter {
gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
}
- // If any keepalive filter matches, drop
+ // If any TCP keepalive filter matches, drop
generateV4KeepaliveFilters(gen);
+ // If any NAT-T keepalive filter matches, drop
+ generateV4NattKeepaliveFilters(gen);
+
// Otherwise, this is an IPv4 unicast, pass
// If L2 broadcast packet, drop.
// TODO: can we invert this condition to fall through to the common pass case below?
@@ -1184,6 +1270,7 @@ public class ApfFilter {
gen.addJump(mCountAndDropLabel);
} else {
generateV4KeepaliveFilters(gen);
+ generateV4NattKeepaliveFilters(gen);
}
// Otherwise, pass
@@ -1191,25 +1278,36 @@ public class ApfFilter {
gen.addJump(mCountAndPassLabel);
}
- private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
- final String skipV4KeepaliveFilter = "skip_v4_keepalive_filter";
- final boolean haveV4KeepaliveAcks = NetworkStackUtils.any(mKeepaliveAcks,
- ack -> ack instanceof TcpKeepaliveAckV4);
+ private void generateKeepaliveFilters(ApfGenerator gen, Class<?> filterType, int proto,
+ int offset, String label) throws IllegalInstructionException {
+ final boolean haveKeepaliveResponses = NetworkStackUtils.any(mKeepalivePackets,
+ ack -> filterType.isInstance(ack));
- // If no keepalive acks
- if (!haveV4KeepaliveAcks) return;
+ // If no keepalive packets of this type
+ if (!haveKeepaliveResponses) return;
- // If not tcp, skip keepalive filters
- gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
- gen.addJumpIfR0NotEquals(IPPROTO_TCP, skipV4KeepaliveFilter);
+ // If not the right proto, skip keepalive filters
+ gen.addLoad8(Register.R0, offset);
+ gen.addJumpIfR0NotEquals(proto, label);
- // Drop IPv4 Keepalive acks
- for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
- final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
- if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen);
+ // Drop Keepalive responses
+ for (int i = 0; i < mKeepalivePackets.size(); ++i) {
+ final KeepalivePacket response = mKeepalivePackets.valueAt(i);
+ if (filterType.isInstance(response)) response.generateFilterLocked(gen);
}
- gen.defineLabel(skipV4KeepaliveFilter);
+ gen.defineLabel(label);
+ }
+
+ private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
+ generateKeepaliveFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET,
+ "skip_v4_keepalive_filter");
+ }
+
+ private void generateV4NattKeepaliveFilters(ApfGenerator gen)
+ throws IllegalInstructionException {
+ generateKeepaliveFilters(gen, NattKeepaliveResponse.class,
+ IPPROTO_UDP, IPV4_PROTOCOL_OFFSET, "skip_v4_nattkeepalive_filter");
}
/**
@@ -1294,24 +1392,8 @@ public class ApfFilter {
}
private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
- final String skipV6KeepaliveFilter = "skip_v6_keepalive_filter";
- final boolean haveV6KeepaliveAcks = NetworkStackUtils.any(mKeepaliveAcks,
- ack -> ack instanceof TcpKeepaliveAckV6);
-
- // If no keepalive acks
- if (!haveV6KeepaliveAcks) return;
-
- // If not tcp, skip keepalive filters
- gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
- gen.addJumpIfR0NotEquals(IPPROTO_TCP, skipV6KeepaliveFilter);
-
- // Drop IPv6 Keepalive acks
- for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
- final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
- if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen);
- }
-
- gen.defineLabel(skipV6KeepaliveFilter);
+ generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET,
+ "skip_v6_keepalive_filter");
}
/**
@@ -1701,26 +1783,34 @@ public class ApfFilter {
public synchronized void addTcpKeepalivePacketFilter(final int slot,
final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
log("Adding keepalive ack(" + slot + ")");
- if (null != mKeepaliveAcks.get(slot)) {
+ if (null != mKeepalivePackets.get(slot)) {
throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
}
final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
- mKeepaliveAcks.put(slot, (ipVersion == 4)
+ mKeepalivePackets.put(slot, (ipVersion == 4)
? new TcpKeepaliveAckV4(sentKeepalivePacket)
: new TcpKeepaliveAckV6(sentKeepalivePacket));
installNewProgramLocked();
}
/**
- * Add NATT keepalive packet filter.
- * This will add a filter to drop NATT keepalive packet which is passed as an argument.
+ * Add NAT-T keepalive packet filter.
+ * This will add a filter to drop NAT-T keepalive packet which is passed as an argument.
*
* @param slot The index used to access the filter.
* @param sentKeepalivePacket The attributes of the sent keepalive packet.
*/
public synchronized void addNattKeepalivePacketFilter(final int slot,
final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
- Log.e(TAG, "APF add NATT keepalive filter is not implemented");
+ log("Adding NAT-T keepalive packet(" + slot + ")");
+ if (null != mKeepalivePackets.get(slot)) {
+ throw new IllegalArgumentException("NAT-T Keepalive slot " + slot + " is occupied");
+ }
+ if (sentKeepalivePacket.srcAddress.length != 4) {
+ throw new IllegalArgumentException("NAT-T keepalive is only supported on IPv4");
+ }
+ mKeepalivePackets.put(slot, new NattKeepaliveResponse(sentKeepalivePacket));
+ installNewProgramLocked();
}
/**
@@ -1729,7 +1819,8 @@ public class ApfFilter {
* @param slot The index used to access the filter.
*/
public synchronized void removeKeepalivePacketFilter(int slot) {
- mKeepaliveAcks.remove(slot);
+ log("Removing keepalive packet(" + slot + ")");
+ mKeepalivePackets.remove(slot);
installNewProgramLocked();
}
@@ -1785,14 +1876,29 @@ public class ApfFilter {
}
pw.decreaseIndent();
- pw.println("Keepalive filters:");
+ pw.println("TCP Keepalive filters:");
pw.increaseIndent();
- for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
- final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i);
- pw.print("Slot ");
- pw.print(mKeepaliveAcks.keyAt(i));
- pw.print(" : ");
- pw.println(keepaliveAck);
+ for (int i = 0; i < mKeepalivePackets.size(); ++i) {
+ final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
+ if (keepalivePacket instanceof TcpKeepaliveAck) {
+ pw.print("Slot ");
+ pw.print(mKeepalivePackets.keyAt(i));
+ pw.print(": ");
+ pw.println(keepalivePacket);
+ }
+ }
+ pw.decreaseIndent();
+
+ pw.println("NAT-T Keepalive filters:");
+ pw.increaseIndent();
+ for (int i = 0; i < mKeepalivePackets.size(); ++i) {
+ final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
+ if (keepalivePacket instanceof NattKeepaliveResponse) {
+ pw.print("Slot ");
+ pw.print(mKeepalivePackets.keyAt(i));
+ pw.print(": ");
+ pw.println(keepalivePacket);
+ }
}
pw.decreaseIndent();
@@ -1858,4 +1964,18 @@ public class ApfFilter {
+ (uint8(bytes[2]) << 8)
+ (uint8(bytes[3]));
}
+
+ private static byte[] concatArrays(final byte[]... arr) {
+ int size = 0;
+ for (byte[] a : arr) {
+ size += a.length;
+ }
+ final byte[] result = new byte[size];
+ int offset = 0;
+ for (byte[] a : arr) {
+ System.arraycopy(a, 0, result, offset, a.length);
+ offset += a.length;
+ }
+ return result;
+ }
}
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index 2934e1cf0c82..9bf1b967e397 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -24,6 +24,8 @@ import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.SocketException;
import java.util.List;
import java.util.function.Predicate;
@@ -228,4 +230,13 @@ public class NetworkStackUtils {
private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
FileDescriptor fd) throws IOException;
+
+ /**
+ * Return IP address and port in a string format.
+ */
+ public static String addressAndPortToString(InetAddress address, int port) {
+ return String.format(
+ (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d",
+ address.getHostAddress(), port);
+ }
}
diff --git a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java b/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
index 85f94e17a088..4767d5574a00 100644
--- a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
+++ b/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
@@ -55,12 +55,23 @@ public class DnsUtils {
throws UnknownHostException {
final List<InetAddress> result = new ArrayList<InetAddress>();
- result.addAll(Arrays.asList(
- getAllByName(dnsResolver, network, host, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
- timeout)));
- result.addAll(Arrays.asList(
- getAllByName(dnsResolver, network, host, TYPE_A, FLAG_NO_CACHE_LOOKUP,
- timeout)));
+ try {
+ result.addAll(Arrays.asList(
+ getAllByName(dnsResolver, network, host, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+ timeout)));
+ } catch (UnknownHostException e) {
+ // Might happen if the host is v4-only, still need to query TYPE_A
+ }
+ try {
+ result.addAll(Arrays.asList(
+ getAllByName(dnsResolver, network, host, TYPE_A, FLAG_NO_CACHE_LOOKUP,
+ timeout)));
+ } catch (UnknownHostException e) {
+ // Might happen if the host is v6-only, still need to return AAAA answers
+ }
+ if (result.size() == 0) {
+ throw new UnknownHostException(host);
+ }
return result.toArray(new InetAddress[0]);
}
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index 93ab3be28fc7..8f2b96807860 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -40,6 +40,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NattKeepalivePacketDataParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
@@ -998,47 +999,54 @@ public class ApfTest {
}
}
- private static final int ETH_HEADER_LEN = 14;
- private static final int ETH_DEST_ADDR_OFFSET = 0;
- private static final int ETH_ETHERTYPE_OFFSET = 12;
+ private static final int ETH_HEADER_LEN = 14;
+ private static final int ETH_DEST_ADDR_OFFSET = 0;
+ private static final int ETH_ETHERTYPE_OFFSET = 12;
private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
- private static final int IPV4_HEADER_LEN = 20;
- private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
+ private static final int IPV4_HEADER_LEN = 20;
+ private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2;
- private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
- private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
- private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
- private static final int IPV4_TCP_HEADER_LEN = 20;
- private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
- private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0;
- private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2;
- private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
- private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
+ private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+ private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
+ private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+
+ private static final int IPV4_TCP_HEADER_LEN = 20;
+ private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
+ private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0;
+ private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2;
+ private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
+ private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
- private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
+ private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
+
+ private static final int IPV4_UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;;
+ private static final int IPV4_UDP_SRC_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 0;
+ private static final int IPV4_UDP_DEST_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 2;
+ private static final int IPV4_UDP_LENGTH_OFFSET = IPV4_UDP_HEADER_OFFSET + 4;
+ private static final int IPV4_UDP_PAYLOAD_OFFSET = IPV4_UDP_HEADER_OFFSET + 8;
private static final byte[] IPV4_BROADCAST_ADDRESS =
{(byte) 255, (byte) 255, (byte) 255, (byte) 255};
- private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
- private static final int IPV6_HEADER_LEN = 40;
- private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
- private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
- private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
- private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0;
- private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2;
- private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4;
- private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8;
+ private static final int IPV6_HEADER_LEN = 40;
+ private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
+ private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
+ private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0;
+ private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2;
+ private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4;
+ private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8;
// The IPv6 all nodes address ff02::1
- private static final byte[] IPV6_ALL_NODES_ADDRESS =
+ private static final byte[] IPV6_ALL_NODES_ADDRESS =
{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
private static final byte[] IPV6_ALL_ROUTERS_ADDRESS =
{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
- private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
- private static final int ICMP6_ROUTER_SOLICITATION = 133;
- private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
+ private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ private static final int ICMP6_ROUTER_SOLICITATION = 133;
+ private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
@@ -1050,9 +1058,9 @@ public class ApfTest {
private static final int ICMP6_RA_OPTION_OFFSET =
ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
- private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
- private static final int ICMP6_PREFIX_OPTION_LEN = 32;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
+ private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
+ private static final int ICMP6_PREFIX_OPTION_LEN = 32;
+ private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
// From RFC6106: Recursive DNS Server option
@@ -1063,17 +1071,17 @@ public class ApfTest {
// From RFC4191: Route Information option
private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
// Above three options all have the same format:
- private static final int ICMP6_4_BYTE_OPTION_LEN = 8;
+ private static final int ICMP6_4_BYTE_OPTION_LEN = 8;
private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
- private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
+ private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
- private static final int UDP_HEADER_LEN = 8;
+ private static final int UDP_HEADER_LEN = 8;
private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 22;
- private static final int DHCP_CLIENT_PORT = 68;
+ private static final int DHCP_CLIENT_PORT = 68;
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
- private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
+ private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
private static final byte[] ARP_IPV4_REQUEST_HEADER = {
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
@@ -1714,6 +1722,83 @@ public class ApfTest {
return packet.array();
}
+ @Test
+ public void testApfFilterNattKeepalivePacket() throws Exception {
+ final MockIpClientCallback cb = new MockIpClientCallback();
+ final ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
+ byte[] program;
+ final int srcPort = 1024;
+ final int dstPort = 4500;
+ final int slot1 = 1;
+ // NAT-T keepalive
+ final byte[] kaPayload = {(byte) 0xff};
+ final byte[] nonKaPayload = {(byte) 0xfe};
+
+ // src: 10.0.0.5, port: 1024
+ // dst: 10.0.0.6, port: 4500
+ InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
+ InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
+
+ final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
+ parcel.srcAddress = srcAddr.getAddress();
+ parcel.srcPort = srcPort;
+ parcel.dstAddress = dstAddr.getAddress();
+ parcel.dstPort = dstPort;
+
+ apfFilter.addNattKeepalivePacketFilter(slot1, parcel);
+ program = cb.getApfProgram();
+
+ // Verify IPv4 keepalive packet is dropped
+ // src: 10.0.0.6, port: 4500
+ // dst: 10.0.0.5, port: 1024
+ byte[] pkt = ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR,
+ IPV4_KEEPALIVE_SRC_ADDR, dstPort, srcPort, 1 /* dataLength */);
+ System.arraycopy(kaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, kaPayload.length);
+ assertDrop(program, pkt);
+
+ // Verify a packet with payload length 1 byte but it is not 0xff will pass the filter.
+ System.arraycopy(nonKaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, nonKaPayload.length);
+ assertPass(program, pkt);
+
+ // Verify IPv4 non-keepalive response packet from the same source address is passed
+ assertPass(program,
+ ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, 10 /* dataLength */));
+
+ // Verify IPv4 non-keepalive response packet from other source address is passed
+ assertPass(program,
+ ipv4UdpPacket(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, 10 /* dataLength */));
+
+ apfFilter.removeKeepalivePacketFilter(slot1);
+ apfFilter.shutdown();
+ }
+
+ private static byte[] ipv4UdpPacket(byte[] sip, byte[] dip, int sport,
+ int dport, int dataLength) {
+ final int totalLength = dataLength + IPV4_HEADER_LEN + UDP_HEADER_LEN;
+ final int udpLength = UDP_HEADER_LEN + dataLength;
+ ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
+
+ // ether type
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+
+ // IPv4 header
+ packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+ packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+ packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_UDP);
+ put(packet, IPV4_SRC_ADDR_OFFSET, sip);
+ put(packet, IPV4_DEST_ADDR_OFFSET, dip);
+ packet.putShort(IPV4_UDP_SRC_PORT_OFFSET, (short) sport);
+ packet.putShort(IPV4_UDP_DEST_PORT_OFFSET, (short) dport);
+ packet.putShort(IPV4_UDP_LENGTH_OFFSET, (short) udpLength);
+
+ return packet.array();
+ }
+
// Verify that the last program pushed to the IpClient.Callback properly filters the
// given packet for the given lifetime.
private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index abfb9c8ae282..26186751c282 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -297,9 +297,10 @@ public class NetworkMonitorTest {
setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL);
setFallbackSpecs(null); // Test with no fallback spec by default
when(mRandom.nextInt()).thenReturn(0);
-
+ // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise,
+ // it will fail the test because of timeout expired for querying AAAA and A sequentially.
when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
- .thenReturn(500);
+ .thenReturn(200);
doAnswer((invocation) -> {
URL url = invocation.getArgument(0);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1d351a5e53b6..9a95288a69ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -826,11 +826,22 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
/**
- * @return resource for string that discribes the connection state of this device.
- * case 1: idle or playing media, show "Active" on the only one A2DP active device.
- * case 2: in phone call, show "Active" on the only one HFP active device
+ * Return full summary that describes connection state of this device
+ *
+ * @see #getConnectionSummary(boolean shortSummary)
*/
public String getConnectionSummary() {
+ return getConnectionSummary(false /* shortSummary */);
+ }
+
+ /**
+ * Return summary that describes connection state of this device. Summary depends on:
+ * 1. Whether device has battery info
+ * 2. Whether device is in active usage(or in phone call)
+ *
+ * @param shortSummary {@code true} if need to return short version summary
+ */
+ public String getConnectionSummary(boolean shortSummary) {
boolean profileConnected = false; // Updated as long as BluetoothProfile is connected
boolean a2dpConnected = true; // A2DP is connected
boolean hfpConnected = true; // HFP is connected
@@ -909,9 +920,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
if ((mIsActiveDeviceHearingAid)
|| (mIsActiveDeviceHeadset && isOnCall)
|| (mIsActiveDeviceA2dp && !isOnCall)) {
- if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
+ if (isTwsBatteryAvailable(leftBattery, rightBattery) && !shortSummary) {
stringRes = R.string.bluetooth_active_battery_level_untethered;
- } else if (batteryLevelPercentageString != null) {
+ } else if (batteryLevelPercentageString != null && !shortSummary) {
stringRes = R.string.bluetooth_active_battery_level;
} else {
stringRes = R.string.bluetooth_active_no_battery_level;
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index b8e1251dd79a..6fd874989c35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -18,11 +18,11 @@ package com.android.settingslib.location;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.format.DateUtils;
@@ -48,10 +48,15 @@ public class RecentLocationApps {
private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
@VisibleForTesting
- static final int[] LOCATION_OPS = new int[] {
+ static final int[] LOCATION_REQUEST_OPS = new int[]{
AppOpsManager.OP_MONITOR_LOCATION,
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
};
+ @VisibleForTesting
+ static final int[] LOCATION_PERMISSION_OPS = new int[]{
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_COARSE_LOCATION,
+ };
private final PackageManager mPackageManager;
private final Context mContext;
@@ -67,11 +72,13 @@ public class RecentLocationApps {
* Fills a list of applications which queried location recently within specified time.
* Apps are sorted by recency. Apps with more recent location requests are in the front.
*/
- public List<Request> getAppList() {
+ public List<Request> getAppList(boolean showSystemApps) {
+ // Retrieve a location usage list from AppOps
+ PackageManager pm = mContext.getPackageManager();
// Retrieve a location usage list from AppOps
AppOpsManager aoManager =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+ List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_REQUEST_OPS);
final int appOpsCount = appOps != null ? appOps.size() : 0;
@@ -83,26 +90,58 @@ public class RecentLocationApps {
for (int i = 0; i < appOpsCount; ++i) {
AppOpsManager.PackageOps ops = appOps.get(i);
- // Don't show the Android System in the list - it's not actionable for the user.
- // Also don't show apps belonging to background users except managed users.
String packageName = ops.getPackageName();
int uid = ops.getUid();
- int userId = UserHandle.getUserId(uid);
- boolean isAndroidOs =
- (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
- if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+ final UserHandle user = UserHandle.getUserHandleForUid(uid);
+
+ // Don't show apps belonging to background users except managed users.
+ if (!profiles.contains(user)) {
continue;
}
- Request request = getRequestFromOps(now, ops);
- if (request != null) {
- requests.add(request);
+
+ // Don't show apps that do not have user sensitive location permissions
+ boolean showApp = true;
+ if (!showSystemApps) {
+ for (int op : LOCATION_PERMISSION_OPS) {
+ final String permission = AppOpsManager.opToPermission(op);
+ final int permissionFlags = pm.getPermissionFlags(permission, packageName,
+ user);
+ if (PermissionChecker.checkPermission(mContext, permission, -1, uid,
+ packageName)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ if ((permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)
+ == 0) {
+ showApp = false;
+ break;
+ }
+ } else {
+ if ((permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0) {
+ showApp = false;
+ break;
+ }
+ }
+ }
+ }
+ if (showApp) {
+ Request request = getRequestFromOps(now, ops);
+ if (request != null) {
+ requests.add(request);
+ }
}
}
return requests;
}
- public List<Request> getAppListSorted() {
- List<Request> requests = getAppList();
+ /**
+ * Gets a list of apps that requested for location recently, sorting by recency.
+ *
+ * @param showSystemApps whether includes system apps in the list.
+ * @return the list of apps that recently requested for location.
+ */
+ public List<Request> getAppListSorted(boolean showSystemApps) {
+ List<Request> requests = getAppList(showSystemApps);
// Sort the list of Requests by recency. Most recent request first.
Collections.sort(requests, Collections.reverseOrder(new Comparator<Request>() {
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 2bfcc91bbc29..f30de130f616 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -175,9 +175,7 @@ public class DataUsageController {
private long getUsageLevel(NetworkTemplate template, long start, long end) {
try {
- final Bucket bucket = mNetworkStatsManager.querySummaryForDevice(
- getNetworkType(template), getActiveSubscriberId(),
- start, end);
+ final Bucket bucket = mNetworkStatsManager.querySummaryForDevice(template, start, end);
if (bucket != null) {
return bucket.getRxBytes() + bucket.getTxBytes();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
index ec5a0b5cc4cd..787dc55e60f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
@@ -37,14 +37,14 @@ public class NetworkCycleChartDataLoader
private NetworkCycleChartDataLoader(Builder builder) {
super(builder);
- mData = new ArrayList<NetworkCycleChartData>();
+ mData = new ArrayList<>();
}
@Override
void recordUsage(long start, long end) {
try {
final NetworkStats.Bucket bucket = mNetworkStatsManager.querySummaryForDevice(
- mNetworkType, mSubId, start, end);
+ mNetworkTemplate, start, end);
final long total = bucket == null ? 0L : bucket.getRxBytes() + bucket.getTxBytes();
if (total > 0L) {
final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
@@ -81,7 +81,7 @@ public class NetworkCycleChartDataLoader
long usage = 0L;
try {
final NetworkStats.Bucket bucket = mNetworkStatsManager.querySummaryForDevice(
- mNetworkType, mSubId, bucketStart, bucketEnd);
+ mNetworkTemplate, bucketStart, bucketEnd);
if (bucket != null) {
usage = bucket.getRxBytes() + bucket.getTxBytes();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
index bd9a636883c2..43c05b8b64d6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -23,11 +23,11 @@ import android.app.usage.NetworkStats;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.VisibleForTesting;
-
/**
* Loader for network data usage history. It returns a list of usage data per billing cycle for the
* specific Uid(s).
@@ -44,7 +44,7 @@ public class NetworkCycleDataForUidLoader extends
super(builder);
mUids = builder.mUids;
mRetrieveDetail = builder.mRetrieveDetail;
- mData = new ArrayList<NetworkCycleDataForUid>();
+ mData = new ArrayList<>();
}
@Override
@@ -54,7 +54,7 @@ public class NetworkCycleDataForUidLoader extends
long totalForeground = 0L;
for (int uid : mUids) {
final NetworkStats stats = mNetworkStatsManager.queryDetailsForUid(
- mNetworkType, mSubId, start, end, uid);
+ mNetworkTemplate, start, end, uid);
final long usage = getTotalUsage(stats);
if (usage > 0L) {
totalUsage += usage;
@@ -100,7 +100,7 @@ public class NetworkCycleDataForUidLoader extends
private long getForegroundUsage(long start, long end, int uid) {
final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState(
- mNetworkType, mSubId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ mNetworkTemplate, start, end, uid, TAG_NONE, STATE_FOREGROUND);
return getTotalUsage(stats);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index dd6d563b3197..3e95b01824cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -49,18 +49,14 @@ import java.util.Iterator;
public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
private static final String TAG = "NetworkCycleDataLoader";
protected final NetworkStatsManager mNetworkStatsManager;
- protected final String mSubId;
- protected final int mNetworkType;
+ protected final NetworkTemplate mNetworkTemplate;
private final NetworkPolicy mPolicy;
- private final NetworkTemplate mNetworkTemplate;
private final ArrayList<Long> mCycles;
@VisibleForTesting
final INetworkStatsService mNetworkStatsService;
protected NetworkCycleDataLoader(Builder<?> builder) {
super(builder.mContext);
- mSubId = builder.mSubId;
- mNetworkType = builder.mNetworkType;
mNetworkTemplate = builder.mNetworkTemplate;
mCycles = builder.mCycles;
mNetworkStatsManager = (NetworkStatsManager)
@@ -180,8 +176,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
public static abstract class Builder<T extends NetworkCycleDataLoader> {
private final Context mContext;
- private String mSubId;
- private int mNetworkType;
private NetworkTemplate mNetworkTemplate;
private ArrayList<Long> mCycles;
@@ -189,14 +183,8 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
mContext = context;
}
- public Builder<T> setSubscriberId(String subId) {
- mSubId = subId;
- return this;
- }
-
public Builder<T> setNetworkTemplate(NetworkTemplate template) {
mNetworkTemplate = template;
- mNetworkType = DataUsageController.getNetworkType(template);
return this;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
index 34e6097ea46e..ed093629686c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
@@ -16,9 +16,10 @@
package com.android.settingslib.net;
-import android.app.usage.NetworkStatsManager;
import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
+import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.util.Log;
@@ -33,15 +34,13 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
private final NetworkStatsManager mNetworkStatsManager;
private final long mStart;
private final long mEnd;
- private final String mSubId;
- private final int mNetworkType;
+ private final NetworkTemplate mNetworkTemplate;
private NetworkStatsSummaryLoader(Builder builder) {
super(builder.mContext);
mStart = builder.mStart;
mEnd = builder.mEnd;
- mSubId = builder.mSubId;
- mNetworkType = builder.mNetworkType;
+ mNetworkTemplate = builder.mNetworkTemplate;
mNetworkStatsManager = (NetworkStatsManager)
builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
}
@@ -55,7 +54,7 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
@Override
public NetworkStats loadInBackground() {
try {
- return mNetworkStatsManager.querySummary(mNetworkType, mSubId, mStart, mEnd);
+ return mNetworkStatsManager.querySummary(mNetworkTemplate, mStart, mEnd);
} catch (RemoteException e) {
Log.e(TAG, "Exception querying network detail.", e);
return null;
@@ -78,8 +77,7 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
private final Context mContext;
private long mStart;
private long mEnd;
- private String mSubId;
- private int mNetworkType;
+ private NetworkTemplate mNetworkTemplate;
public Builder(Context context) {
mContext = context;
@@ -95,13 +93,11 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
return this;
}
- public Builder setSubscriberId(String subId) {
- mSubId = subId;
- return this;
- }
-
- public Builder setNetworkType(int networkType) {
- mNetworkType = networkType;
+ /**
+ * Set {@link NetworkTemplate} for builder
+ */
+ public Builder setNetworkTemplate(NetworkTemplate template) {
+ mNetworkTemplate = template;
return this;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 05af4e1f80b3..e28c612453b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -180,7 +180,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
public static final int SECURITY_SAE = 5;
public static final int SECURITY_EAP_SUITE_B = 6;
public static final int SECURITY_PSK_SAE_TRANSITION = 7;
- public static final int SECURITY_MAX_VAL = 8; // Has to be the last
+ public static final int SECURITY_OWE_TRANSITION = 8;
+ public static final int SECURITY_MAX_VAL = 9; // Has to be the last
private static final int PSK_UNKNOWN = 0;
private static final int PSK_WPA = 1;
@@ -869,6 +870,12 @@ public class AccessPoint implements Comparable<AccessPoint> {
return concise ? context.getString(R.string.wifi_security_short_sae) :
context.getString(R.string.wifi_security_sae);
}
+ case SECURITY_OWE_TRANSITION:
+ if (mConfig != null && getSecurity(mConfig) == SECURITY_OWE) {
+ return concise ? context.getString(R.string.wifi_security_short_owe) :
+ context.getString(R.string.wifi_security_owe);
+ }
+ return concise ? "" : context.getString(R.string.wifi_security_none);
case SECURITY_OWE:
return concise ? context.getString(R.string.wifi_security_short_owe) :
context.getString(R.string.wifi_security_owe);
@@ -1179,7 +1186,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
* Can only be called for unsecured networks.
*/
public void generateOpenNetworkConfig() {
- if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) {
+ if ((security != SECURITY_NONE) && (security != SECURITY_OWE)
+ && (security != SECURITY_OWE_TRANSITION)) {
throw new IllegalStateException();
}
if (mConfig != null)
@@ -1187,7 +1195,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
mConfig = new WifiConfiguration();
mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
- if (security == SECURITY_NONE) {
+ if (security == SECURITY_NONE || !getWifiManager().isEasyConnectSupported()) {
mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
} else {
mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
@@ -1229,6 +1237,9 @@ public class AccessPoint implements Comparable<AccessPoint> {
private static final String sPskSuffix = "," + String.valueOf(SECURITY_PSK);
private static final String sSaeSuffix = "," + String.valueOf(SECURITY_SAE);
private static final String sPskSaeSuffix = "," + String.valueOf(SECURITY_PSK_SAE_TRANSITION);
+ private static final String sOweSuffix = "," + String.valueOf(SECURITY_OWE);
+ private static final String sOpenSuffix = "," + String.valueOf(SECURITY_NONE);
+ private static final String sOweTransSuffix = "," + String.valueOf(SECURITY_OWE_TRANSITION);
private boolean isKeyEqual(String compareTo) {
if (mKey == null) {
@@ -1243,6 +1254,14 @@ public class AccessPoint implements Comparable<AccessPoint> {
compareTo.substring(0, compareTo.lastIndexOf(',')));
}
}
+ if (compareTo.endsWith(sOpenSuffix) || compareTo.endsWith(sOweSuffix)) {
+ if (mKey.endsWith(sOweTransSuffix)) {
+ // Special handling for OWE/Open networks. If AP advertises OWE in transition mode
+ // and we have an Open network saved, allow this connection to be established.
+ return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
+ compareTo.substring(0, compareTo.lastIndexOf(',')));
+ }
+ }
return mKey.equals(compareTo);
}
@@ -1579,10 +1598,11 @@ public class AccessPoint implements Comparable<AccessPoint> {
return SECURITY_EAP_SUITE_B;
} else if (result.capabilities.contains("EAP")) {
return SECURITY_EAP;
+ } else if (result.capabilities.contains("OWE_TRANSITION")) {
+ return SECURITY_OWE_TRANSITION;
} else if (result.capabilities.contains("OWE")) {
return SECURITY_OWE;
}
-
return SECURITY_NONE;
}
@@ -1628,6 +1648,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
return "OWE";
} else if (security == SECURITY_PSK_SAE_TRANSITION) {
return "PSK+SAE";
+ } else if (security == SECURITY_OWE_TRANSITION) {
+ return "OWE_TRANSITION";
}
return "NONE";
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 6269a717b333..dae546497aba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -201,7 +201,8 @@ public class AccessPointPreference extends Preference {
return;
}
if ((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
- && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) {
+ && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)
+ && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
mFrictionSld.setState(STATE_SECURED);
} else if (mAccessPoint.isMetered()) {
mFrictionSld.setState(STATE_METERED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index c0a1f11a0a87..93dcbfeab172 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -192,6 +192,27 @@ public class CachedBluetoothDeviceTest {
}
@Test
+ public void getConnectionSummary_shortSummary_returnShortSummary() {
+ // Test without battery level
+ // Set A2DP profile to be connected and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isNull();
+
+ // Set device as Active for A2DP and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isEqualTo("Active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isEqualTo(
+ "Active");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
public void getConnectionSummary_testA2dpBatteryInactive_returnBattery() {
// Arrange:
// 1. Profile: {A2DP, CONNECTED, Inactive}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 8bd5fd2163ae..7a553fc91ff6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -16,8 +16,8 @@ import android.content.res.Resources;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-
import android.util.LongSparseLongArray;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,7 +75,8 @@ public class RecentLocationAppsTest {
long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
- when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps);
+ when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_REQUEST_OPS)).thenReturn(
+ appOps);
mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
mRecentLocationApps = new RecentLocationApps(mContext);
@@ -83,7 +84,7 @@ public class RecentLocationAppsTest {
@Test
public void testGetAppList_shouldFilterRecentApps() {
- List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList();
+ List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList(true);
// Only two of the apps have requested location within 15 min.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
@@ -107,11 +108,12 @@ public class RecentLocationAppsTest {
{ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
appOps.add(androidSystemPackageOps);
- when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps);
+ when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_REQUEST_OPS)).thenReturn(
+ appOps);
mockTestApplicationInfos(
Process.SYSTEM_UID, RecentLocationApps.ANDROID_SYSTEM_PACKAGE_NAME);
- List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList();
+ List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList(true);
// Android OS shouldn't show up in the list of apps.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
@@ -133,7 +135,7 @@ public class RecentLocationAppsTest {
private List<PackageOps> createTestPackageOpsList(String[] packageNameList, long[] time) {
List<PackageOps> packageOpsList = new ArrayList<>();
- for (int i = 0; i < packageNameList.length ; i++) {
+ for (int i = 0; i < packageNameList.length; i++) {
PackageOps packageOps = createPackageOps(
packageNameList[i],
TEST_UID,
@@ -156,11 +158,11 @@ public class RecentLocationAppsTest {
private OpEntry createOpEntryWithTime(int op, long time, int duration) {
final LongSparseLongArray accessTimes = new LongSparseLongArray();
accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
- AppOpsManager.OP_FLAG_SELF), time);
+ AppOpsManager.OP_FLAG_SELF), time);
final LongSparseLongArray durations = new LongSparseLongArray();
durations.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
- AppOpsManager.OP_FLAG_SELF), duration);
+ AppOpsManager.OP_FLAG_SELF), duration);
return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes,
- null /*rejectTimes*/, durations, null /* proxyUids */, null /* proxyPackages */);
+ null /*rejectTimes*/, durations, null /* proxyUids */, null /* proxyPackages */);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index a28bb6ce44c5..3da5e766c389 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -31,7 +31,6 @@ import static org.mockito.Mockito.when;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
@@ -52,6 +51,7 @@ import org.robolectric.shadows.ShadowSubscriptionManager;
public class DataUsageControllerTest {
private static final String SUB_ID = "Test Subscriber";
+ private static final String SUB_ID_2 = "Test Subscriber 2";
@Mock
private INetworkStatsSession mSession;
@@ -63,6 +63,9 @@ public class DataUsageControllerTest {
private NetworkStatsManager mNetworkStatsManager;
@Mock
private Context mContext;
+ private NetworkTemplate mNetworkTemplate;
+ private NetworkTemplate mNetworkTemplate2;
+ private NetworkTemplate mWifiNetworkTemplate;
private DataUsageController mController;
private NetworkStatsHistory mNetworkStatsHistory;
@@ -83,24 +86,27 @@ public class DataUsageControllerTest {
.when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
ShadowSubscriptionManager.setDefaultDataSubscriptionId(mDefaultSubscriptionId);
doReturn(SUB_ID).when(mTelephonyManager).getSubscriberId();
+
+ mNetworkTemplate = NetworkTemplate.buildTemplateMobileAll(SUB_ID);
+ mNetworkTemplate2 = NetworkTemplate.buildTemplateMobileAll(SUB_ID_2);
+ mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifiWildcard();
}
@Test
public void getHistoricalUsageLevel_shouldQuerySummaryForDevice() throws Exception {
+ mController.getHistoricalUsageLevel(mWifiNetworkTemplate);
- mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
-
- verify(mNetworkStatsManager).querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
- eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */);
+ verify(mNetworkStatsManager).querySummaryForDevice(eq(mWifiNetworkTemplate),
+ eq(0L) /* startTime */, anyLong() /* endTime */);
}
@Test
public void getHistoricalUsageLevel_noUsageData_shouldReturn0() throws Exception {
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
- eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */))
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mWifiNetworkTemplate),
+ eq(0L) /* startTime */, anyLong() /* endTime */))
.thenReturn(mock(NetworkStats.Bucket.class));
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
- .isEqualTo(0L);
+ assertThat(mController.getHistoricalUsageLevel(mWifiNetworkTemplate))
+ .isEqualTo(0L);
}
@Test
@@ -110,10 +116,10 @@ public class DataUsageControllerTest {
final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
when(bucket.getRxBytes()).thenReturn(receivedBytes);
when(bucket.getTxBytes()).thenReturn(transmittedBytes);
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
- eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mWifiNetworkTemplate),
+ eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ assertThat(mController.getHistoricalUsageLevel(mWifiNetworkTemplate))
.isEqualTo(receivedBytes + transmittedBytes);
}
@@ -126,9 +132,8 @@ public class DataUsageControllerTest {
final NetworkStats.Bucket defaultSubscriberBucket = mock(NetworkStats.Bucket.class);
when(defaultSubscriberBucket.getRxBytes()).thenReturn(defaultSubRx);
when(defaultSubscriberBucket.getTxBytes()).thenReturn(defaultSubTx);
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_MOBILE),
- eq(SUB_ID), eq(0L)/* startTime */, anyLong() /* endTime */)).thenReturn(
- defaultSubscriberBucket);
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mNetworkTemplate), eq(0L)/* startTime */,
+ anyLong() /* endTime */)).thenReturn(defaultSubscriberBucket);
// Now setup a stats bucket for a different, non-default subscription / subscriber ID.
final long nonDefaultSubRx = 7654321L;
@@ -137,25 +142,21 @@ public class DataUsageControllerTest {
when(nonDefaultSubscriberBucket.getRxBytes()).thenReturn(nonDefaultSubRx);
when(nonDefaultSubscriberBucket.getTxBytes()).thenReturn(nonDefaultSubTx);
final int explicitSubscriptionId = 55;
- final String subscriberId2 = "Test Subscriber 2";
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_MOBILE),
- eq(subscriberId2), eq(0L)/* startTime */, anyLong() /* endTime */)).thenReturn(
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mNetworkTemplate2),
+ eq(0L)/* startTime */, anyLong() /* endTime */)).thenReturn(
nonDefaultSubscriberBucket);
- doReturn(subscriberId2).when(mTelephonyManager).getSubscriberId();
+ doReturn(SUB_ID_2).when(mTelephonyManager).getSubscriberId();
// Now verify that when we're asking for stats on the non-default subscription, we get
// the data back for that subscription and *not* the default one.
mController.setSubscriptionId(explicitSubscriptionId);
- assertThat(mController.getHistoricalUsageLevel(
- NetworkTemplate.buildTemplateMobileAll(subscriberId2))).isEqualTo(
+ assertThat(mController.getHistoricalUsageLevel(mNetworkTemplate2)).isEqualTo(
nonDefaultSubRx + nonDefaultSubTx);
-
- verify(mTelephonyManager).createForSubscriptionId(explicitSubscriptionId);
}
@Test
- public void getTelephonyManager_shouldCreateWithExplicitSubId() throws Exception {
+ public void getTelephonyManager_shouldCreateWithExplicitSubId() {
int explicitSubId = 1;
TelephonyManager tmForSub1 = new TelephonyManager(mContext, explicitSubId);
when(mTelephonyManager.createForSubscriptionId(eq(explicitSubId))).thenReturn(tmForSub1);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 011f234ab4f1..c3e161320a1f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -21,9 +21,9 @@ import static org.mockito.Mockito.when;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.text.format.DateUtils;
@@ -43,6 +43,8 @@ public class NetworkCycleChartDataLoaderTest {
private NetworkPolicyManager mNetworkPolicyManager;
@Mock
private Context mContext;
+ @Mock
+ private NetworkTemplate mNetworkTemplate;
private NetworkCycleChartDataLoader mLoader;
@@ -60,13 +62,12 @@ public class NetworkCycleChartDataLoaderTest {
public void recordUsage_shouldQueryNetworkSummaryForDevice() throws RemoteException {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
mLoader = NetworkCycleChartDataLoader.builder(mContext)
- .setSubscriberId(subId).build();
+ .setNetworkTemplate(mNetworkTemplate)
+ .build();
mLoader.recordUsage(start, end);
- verify(mNetworkStatsManager).querySummaryForDevice(networkType, subId, start, end);
+ verify(mNetworkStatsManager).querySummaryForDevice(mNetworkTemplate, start, end);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index aafb46a1dbf1..877eb615b196 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -28,9 +28,9 @@ import static org.mockito.Mockito.when;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
import android.text.format.DateUtils;
import org.junit.Before;
@@ -42,6 +42,7 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataForUidLoaderTest {
+ private static final String SUB_ID = "Test Subscriber";
@Mock
private NetworkStatsManager mNetworkStatsManager;
@@ -49,6 +50,7 @@ public class NetworkCycleDataForUidLoaderTest {
private NetworkPolicyManager mNetworkPolicyManager;
@Mock
private Context mContext;
+ private NetworkTemplate mNetworkTemplate;
private NetworkCycleDataForUidLoader mLoader;
@@ -56,64 +58,62 @@ public class NetworkCycleDataForUidLoaderTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
- .thenReturn(mNetworkStatsManager);
+ .thenReturn(mNetworkStatsManager);
when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
- .thenReturn(mNetworkPolicyManager);
+ .thenReturn(mNetworkPolicyManager);
when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+ mNetworkTemplate = NetworkTemplate.buildTemplateMobileAll(SUB_ID);
}
@Test
public void recordUsage_shouldQueryNetworkDetailsForUidAndForegroundState() {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
final int uid = 1;
mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
- .addUid(uid).setSubscriberId(subId).build());
+ .addUid(uid)
+ .setNetworkTemplate(mNetworkTemplate)
+ .build());
doReturn(1024L).when(mLoader).getTotalUsage(any());
mLoader.recordUsage(start, end);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, uid);
verify(mNetworkStatsManager).queryDetailsForUidTagState(
- networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ mNetworkTemplate, start, end, uid, TAG_NONE, STATE_FOREGROUND);
}
@Test
public void recordUsage_retrieveDetailIsFalse_shouldNotQueryNetworkForegroundState() {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
final int uid = 1;
mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
- .setRetrieveDetail(false).addUid(uid).setSubscriberId(subId).build());
+ .setRetrieveDetail(false).addUid(uid).build());
doReturn(1024L).when(mLoader).getTotalUsage(any());
mLoader.recordUsage(start, end);
verify(mNetworkStatsManager, never()).queryDetailsForUidTagState(
- networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ mNetworkTemplate, start, end, uid, TAG_NONE, STATE_FOREGROUND);
}
@Test
public void recordUsage_multipleUids_shouldQueryNetworkDetailsForEachUid() {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
- .addUid(1)
- .addUid(2)
- .addUid(3)
- .setSubscriberId(subId).build());
+ .addUid(1)
+ .addUid(2)
+ .addUid(3)
+ .setNetworkTemplate(mNetworkTemplate)
+ .build());
doReturn(1024L).when(mLoader).getTotalUsage(any());
mLoader.recordUsage(start, end);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, 1);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, 2);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, 3);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 1);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 2);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 3);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index c5f54bb0f0d9..74b91510cf3f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -126,8 +126,6 @@ public class NetworkCycleDataLoaderTest {
when(mIterator.next()).thenReturn(cycle);
mLoader = spy(new NetworkCycleDataTestLoader(mContext));
ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
- ReflectionHelpers.setField(mLoader, "mNetworkType", networkType);
- ReflectionHelpers.setField(mLoader, "mSubId", subId);
mLoader.loadPolicyData();
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2495b586144..94259416d274 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -58,6 +58,7 @@ android_library {
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
"androidx.dynamicanimation_dynamicanimation",
+ "iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
"dagger2-2.19",
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
index b7bb751c1582..a150de95fcf0 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
@@ -662,11 +662,17 @@ public class LegacyRecentsImpl implements RecentsImplementation {
public final void onBusEvent(ExpandPipEvent event) {
PipUI pipUi = getComponent(PipUI.class);
+ if (pipUi == null) {
+ return;
+ }
pipUi.expandPip();
}
public final void onBusEvent(HidePipMenuEvent event) {
PipUI pipUi = getComponent(PipUI.class);
+ if (pipUi == null) {
+ return;
+ }
event.getAnimationTrigger().increment();
pipUi.hidePipMenu(() -> {
event.getAnimationTrigger().increment();
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index 29376ce01e4f..796123db7c79 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -38,6 +38,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:padding="0dp"
+ android:fitsSystemWindows="true"
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
diff --git a/packages/SystemUI/res/drawable/bubble_flyout.xml b/packages/SystemUI/res/drawable/bubble_flyout.xml
deleted file mode 100644
index afe5372d38d8..000000000000
--- a/packages/SystemUI/res/drawable/bubble_flyout.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- ~ Copyright (C) 2019 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
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item>
- <shape android:shape="rectangle">
- <solid android:color="?android:attr/colorBackgroundFloating" />
- <corners
- android:bottomLeftRadius="?android:attr/dialogCornerRadius"
- android:topLeftRadius="?android:attr/dialogCornerRadius"
- android:bottomRightRadius="?android:attr/dialogCornerRadius"
- android:topRightRadius="?android:attr/dialogCornerRadius" />
- <padding
- android:left="@dimen/bubble_flyout_pointer_size"
- android:right="@dimen/bubble_flyout_pointer_size" />
- </shape>
- </item>
-</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml
index 511910ea2f61..4619430d6b47 100644
--- a/packages/SystemUI/res/layout-land/global_actions_grid.xml
+++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml
@@ -5,19 +5,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:clipToPadding="false"
android:theme="@style/qs_theme"
- android:paddingLeft="@dimen/global_actions_top_padding"
- android:gravity="right"
+ android:gravity="right | center_vertical"
android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingRight="@dimen/global_actions_grid_container_shadow_offset"
+ android:layout_marginRight="@dimen/global_actions_grid_container_negative_shadow_offset"
>
<LinearLayout
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:gravity="top|right"
- android:padding="0dp"
android:orientation="vertical"
android:layout_marginRight="@dimen/global_actions_grid_container_bottom_margin"
+ android:clipChildren="false"
+ android:clipToPadding="false"
>
<!-- Grid of action items -->
<com.android.systemui.globalactions.ListGridLayout
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
index ff2a7d8d6faa..4ece03b9b8e3 100644
--- a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
+++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
@@ -5,18 +5,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:clipToPadding="false"
android:theme="@style/qs_theme"
- android:gravity="left"
- android:paddingRight="@dimen/global_actions_top_padding"
+ android:gravity="left | center_vertical"
android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingLeft="@dimen/global_actions_grid_container_shadow_offset"
+ android:layout_marginLeft="@dimen/global_actions_grid_container_negative_shadow_offset"
>
<LinearLayout
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:gravity="bottom|left"
android:padding="0dp"
android:orientation="vertical"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_marginLeft="@dimen/global_actions_grid_container_bottom_margin"
>
<!-- For separated items-->
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 1abb8735ddab..c560d7e8f126 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -37,7 +37,8 @@
android:id="@+id/space"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:contentDescription="@string/biometric_dialog_empty_space_description"/>
<ScrollView
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/bubble_flyout.xml b/packages/SystemUI/res/layout/bubble_flyout.xml
index 0e4d2985e775..5f773f462deb 100644
--- a/packages/SystemUI/res/layout/bubble_flyout.xml
+++ b/packages/SystemUI/res/layout/bubble_flyout.xml
@@ -13,18 +13,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/bubble_flyout_pointer_size"
- android:paddingRight="@dimen/bubble_flyout_pointer_size">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<FrameLayout
- android:id="@+id/bubble_flyout"
+ android:id="@+id/bubble_flyout_text_container"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:background="@drawable/bubble_flyout"
+ android:clipToPadding="false"
android:paddingLeft="@dimen/bubble_flyout_padding_x"
android:paddingRight="@dimen/bubble_flyout_padding_x"
android:paddingTop="@dimen/bubble_flyout_padding_y"
@@ -41,4 +36,4 @@
</FrameLayout>
-</FrameLayout> \ No newline at end of file
+</merge> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml
index 3f10b388fdd5..3928062e43d2 100644
--- a/packages/SystemUI/res/layout/global_actions_grid.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid.xml
@@ -5,17 +5,19 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:clipToPadding="false"
android:theme="@style/qs_theme"
- android:gravity="bottom"
+ android:gravity="bottom | center_horizontal"
android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
+ android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset"
>
<LinearLayout
android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:gravity="bottom | right"
- android:padding="0dp"
+ android:layout_width="wrap_content"
android:layoutDirection="ltr"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
>
<!-- For separated items-->
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index cdef09d73f77..2792a019f8d8 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -31,23 +31,36 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
android:layout_gravity="bottom|center_horizontal"
- android:orientation="vertical">
+ android:orientation="horizontal">
- <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_enterprise_disclosure"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
- android:visibility="gone" />
+ <include layout="@layout/left_docked_overlay" />
- <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text"
- android:layout_width="match_parent"
+ <LinearLayout
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center"
- android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
- android:accessibilityLiveRegion="polite" />
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:orientation="vertical">
+
+ <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+ android:id="@+id/keyguard_indication_enterprise_disclosure"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+ android:visibility="gone" />
+
+ <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+ android:id="@+id/keyguard_indication_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+ android:accessibilityLiveRegion="polite" />
+
+ </LinearLayout>
+
+ <include layout="@layout/right_docked_overlay" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/left_docked_overlay.xml b/packages/SystemUI/res/layout/left_docked_overlay.xml
new file mode 100644
index 000000000000..430143ca3bc2
--- /dev/null
+++ b/packages/SystemUI/res/layout/left_docked_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.
+ -->
+
+<!-- empty stub -->
+<merge />
diff --git a/packages/SystemUI/res/layout/right_docked_overlay.xml b/packages/SystemUI/res/layout/right_docked_overlay.xml
new file mode 100644
index 000000000000..430143ca3bc2
--- /dev/null
+++ b/packages/SystemUI/res/layout/right_docked_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.
+ -->
+
+<!-- empty stub -->
+<merge />
diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml
index 8f27f45c5396..4390d35e2ad2 100644
--- a/packages/SystemUI/res/values-sw320dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw320dp/dimens.xml
@@ -16,8 +16,6 @@
-->
<resources>
<!-- Global actions grid -->
- <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen>
-
<dimen name="global_actions_grid_vertical_padding">0dp</dimen>
<dimen name="global_actions_grid_horizontal_padding">3dp</dimen>
diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml
index 6fa6692c4d90..2557ff4bb578 100644
--- a/packages/SystemUI/res/values-sw392dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw392dp/dimens.xml
@@ -16,8 +16,6 @@
-->
<resources>
<!-- Global actions grid -->
- <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen>
-
<dimen name="global_actions_grid_vertical_padding">0dp</dimen>
<dimen name="global_actions_grid_horizontal_padding">3dp</dimen>
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
index fea1ef11e139..6780dca130b8 100644
--- a/packages/SystemUI/res/values-sw410dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -23,8 +23,6 @@
<dimen name="qs_detail_items_padding_top">16dp</dimen>
<!-- Global actions grid -->
- <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen>
-
<dimen name="global_actions_grid_vertical_padding">8dp</dimen>
<dimen name="global_actions_grid_horizontal_padding">4dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fbb439af7c51..e00e5f23dea8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -41,10 +41,10 @@
<!-- Size of the nav bar edge panels, should be greater to the
edge sensitivity + the drag threshold -->
- <dimen name="navigation_edge_panel_width">76dp</dimen>
+ <dimen name="navigation_edge_panel_width">70dp</dimen>
<!-- Padding at the end of the navigation panel to allow the arrow not to be clipped off -->
- <dimen name="navigation_edge_panel_padding">24dp</dimen>
- <dimen name="navigation_edge_panel_height">84dp</dimen>
+ <dimen name="navigation_edge_panel_padding">8dp</dimen>
+ <dimen name="navigation_edge_panel_height">96dp</dimen>
<!-- The threshold to drag to trigger the edge action -->
<dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
<!-- The minimum display position of the arrow on the screen -->
@@ -89,7 +89,7 @@
<dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen>
<!-- Size of the view displaying the wifi signal icon in the status bar. -->
- <dimen name="status_bar_wifi_signal_size">15dp</dimen>
+ <dimen name="status_bar_wifi_signal_size">@*android:dimen/status_bar_system_icon_size</dimen>
<!-- Spacing before the airplane mode icon if there are any icons preceding it. -->
<dimen name="status_bar_airplane_spacer_width">4dp</dimen>
@@ -252,6 +252,9 @@
<!-- size at which Notification icons will be drawn on Ambient Display -->
<dimen name="status_bar_icon_drawing_size_dark">@*android:dimen/notification_header_icon_size_ambient</dimen>
+ <!-- size of notification icons on AOD -->
+ <dimen name="dark_shelf_icon_size">16dp</dimen>
+
<!-- opacity at which Notification icons will be drawn in the status bar -->
<item type="dimen" name="status_bar_icon_drawing_alpha">90%</item>
@@ -941,6 +944,12 @@
<!-- Global actions grid layout -->
<dimen name="global_actions_grid_side_margin">4dp</dimen>
+ <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen>
+
+ <!-- Used to workaround a bug where shadows are clipped during animations by expanding
+ the bounds of the parent view. -->
+ <dimen name="global_actions_grid_container_shadow_offset">20dp</dimen>
+ <dimen name="global_actions_grid_container_negative_shadow_offset">-20dp</dimen>
<!-- The maximum offset in either direction that elements are moved horizontally to prevent
burn-in on AOD. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 26432210e233..e01e6a84e9d5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -288,8 +288,18 @@
<!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_confirm">Confirm</string>
- <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR_LIMIT=30] -->
+ <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_try_again">Try again</string>
+ <!-- Content description for empty spaces that are not taken by the biometric dialog. Clicking on these areas will cancel authentication and dismiss the biometric dialog [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_empty_space_description">Empty region, tap to cancel authentication</string>
+ <!-- Content description for the face icon when the device is not authenticating anymore [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_idle">Please try again</string>
+ <!-- Content description for the face icon when the device is authenticating [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_authenticating">Looking for your face</string>
+ <!-- Content description for the face icon when the user has been authenticated [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_authenticated">Face authenticated</string>
+ <!-- Content description for the face icon when the user has been authenticated and the confirm button has been pressed [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_confirmed">Confirmed</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 670980420a57..577e3bbefdad 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -134,9 +134,4 @@ oneway interface IOverviewProxy {
* Sent when some system ui state changes.
*/
void onSystemUiStateChanged(int stateFlags) = 16;
-
- /**
- * Sent when the scrim colors (based on wallpaper) change.
- */
- void onScrimColorsChanged(int color, int type) = 17;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 55499dab05f3..0e91e4109ec0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -68,6 +68,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
+ private final int[] mTmpPosition = new int[2];
+ private final Rect mTempRect = new Rect();
+ private final Rect mLockPatternScreenBounds = new Rect();
private CountDownTimer mCountdownTimer = null;
private LockPatternUtils mLockPatternUtils;
@@ -92,7 +95,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
mLockPatternView.clearPattern();
}
};
- private Rect mTempRect = new Rect();
@VisibleForTesting
KeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
@@ -199,6 +201,15 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
}
@Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mLockPatternView.getLocationOnScreen(mTmpPosition);
+ mLockPatternScreenBounds.set(mTmpPosition[0], mTmpPosition[1],
+ mTmpPosition[0] + mLockPatternView.getWidth(),
+ mTmpPosition[1] + mLockPatternView.getHeight());
+ }
+
+ @Override
public void reset() {
// reset lock pattern
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
@@ -233,9 +244,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
@Override
public boolean disallowInterceptTouch(MotionEvent event) {
- mTempRect.set(mLockPatternView.getLeft(), mLockPatternView.getTop(),
- mLockPatternView.getRight(), mLockPatternView.getBottom());
- return mTempRect.contains((int) event.getX(), (int) event.getY());
+ return mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY());
}
/** TODO: hook this up */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index d051defc1f25..0914fb8330be 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -20,6 +20,7 @@ import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.UserHandle;
import android.util.AttributeSet;
@@ -139,7 +140,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
getSecurityView(mCurrentSecuritySelection).onResume(reason);
}
updateBiometricRetry();
- updatePaddings();
}
@Override
@@ -180,7 +180,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
int index = event.findPointerIndex(mActivePointerId);
int touchSlop = mViewConfiguration.getScaledTouchSlop();
- if (mCurrentSecurityView != null
+ if (mCurrentSecurityView != null && index != -1
&& mStartTouchY - event.getY(index) > touchSlop) {
mIsDragging = true;
return true;
@@ -319,17 +319,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- updatePaddings();
- }
-
- private void updatePaddings() {
- int bottomPadding = getRootWindowInsets().getSystemWindowInsets().bottom;
- if (getPaddingBottom() == bottomPadding) {
- return;
- }
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottomPadding);
+ protected boolean fitSystemWindows(Rect insets) {
+ // Consume bottom insets because we're setting the padding locally (for IME and navbar.)
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), insets.bottom);
+ insets.bottom = 0;
+ return false;
}
private void showDialog(String title, String message) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index dd6ccb2b3a88..417cc68bb632 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -238,6 +238,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private boolean mIsDreaming;
private final DevicePolicyManager mDevicePolicyManager;
private boolean mLogoutEnabled;
+ // If the user long pressed the lock icon, disabling face auth for the current session.
+ private boolean mLockIconPressed;
/**
* Short delay before restarting biometric authentication after a successful try
@@ -1384,6 +1386,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
private void handleScreenTurnedOff() {
+ mLockIconPressed = false;
mHardwareFingerprintUnavailableRetryCount = 0;
mHardwareFaceUnavailableRetryCount = 0;
final int count = mCallbacks.size();
@@ -1625,10 +1628,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant())
&& !mSwitchingUser && !getUserCanSkipBouncer(user) && !isFaceDisabled(user)
- && !mKeyguardGoingAway && mFaceSettingEnabledForUser
+ && !mKeyguardGoingAway && mFaceSettingEnabledForUser && !mLockIconPressed
&& mUserManager.isUserUnlocked(user) && mIsPrimaryUser;
}
+ /**
+ * Whenever the lock icon is long pressed, disabling trust agents.
+ * This means that we cannot auth passively (face) until the user presses power.
+ */
+ public void onLockIconPressed() {
+ mLockIconPressed = true;
+ mUserFaceAuthenticated.put(getCurrentUser(), false);
+ updateFaceListeningState();
+ }
private void startListeningForFingerprint() {
if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
@@ -1982,7 +1994,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
boolean becameAbsent = false;
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.w(TAG, "invalid subId in handleSimStateChange()");
- /* Only handle No SIM(ABSENT) due to handleServiceStateChange() handle other case */
+ /* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to
+ * handleServiceStateChange() handle other case */
if (state == State.ABSENT) {
updateTelephonyCapable(true);
// Even though the subscription is not valid anymore, we need to notify that the
@@ -1995,6 +2008,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
data.simState = State.ABSENT;
}
}
+ } else if (state == State.CARD_IO_ERROR) {
+ updateTelephonyCapable(true);
} else {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 9dfcf7d01e31..e66a8fa96298 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -31,7 +31,9 @@ import android.util.Log;
import android.view.WindowManager;
import com.android.internal.os.SomeArgs;
+import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
/**
@@ -58,6 +60,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
private IBiometricServiceReceiverInternal mReceiver;
private boolean mDialogShowing;
private Callback mCallback = new Callback();
+ private WakefulnessLifecycle mWakefulnessLifecycle;
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -133,6 +136,16 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
}
}
+ final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onStartedGoingToSleep() {
+ if (mDialogShowing) {
+ if (DEBUG) Log.d(TAG, "User canceled due to screen off");
+ mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
+ }
+ }
+ };
+
@Override
public void start() {
final PackageManager pm = mContext.getPackageManager();
@@ -141,6 +154,8 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
|| pm.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
getComponent(CommandQueue.class).addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
}
}
@@ -325,7 +340,6 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
private void handleTryAgainPressed() {
try {
- mCurrentDialog.clearTemporaryMessage();
mReceiver.onTryAgainPressed();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when handling try again", e);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index f99587b6cdc2..5717a54fd8a0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -33,7 +33,6 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -224,13 +223,11 @@ public abstract class BiometricDialogView extends LinearLayout {
});
mTryAgainButton.setOnClickListener((View v) -> {
+ handleResetMessage();
updateState(STATE_AUTHENTICATING);
showTryAgainButton(false /* show */);
mCallback.onTryAgainPressed();
});
-
- mLayout.setFocusableInTouchMode(true);
- mLayout.requestFocus();
}
public void onSaveState(Bundle bundle) {
@@ -269,6 +266,7 @@ public abstract class BiometricDialogView extends LinearLayout {
if (mRestoredState == null) {
updateState(STATE_AUTHENTICATING);
mErrorText.setText(getHintStringResourceId());
+ mErrorText.setContentDescription(mContext.getString(getHintStringResourceId()));
mErrorText.setVisibility(View.VISIBLE);
} else {
updateState(mState);
@@ -278,7 +276,6 @@ public abstract class BiometricDialogView extends LinearLayout {
mTitleText.setVisibility(View.VISIBLE);
mTitleText.setText(titleText);
- mTitleText.setSelected(true);
final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
if (TextUtils.isEmpty(subtitleText)) {
@@ -323,11 +320,10 @@ public abstract class BiometricDialogView extends LinearLayout {
private void setDismissesDialog(View v) {
v.setClickable(true);
- v.setOnTouchListener((View view, MotionEvent event) -> {
+ v.setOnClickListener(v1 -> {
if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
mCallback.onUserCanceled();
}
- return true;
});
}
@@ -421,11 +417,6 @@ public abstract class BiometricDialogView extends LinearLayout {
BiometricPrompt.HIDE_DIALOG_DELAY);
}
- public void clearTemporaryMessage() {
- mHandler.removeMessages(MSG_RESET_MESSAGE);
- mHandler.obtainMessage(MSG_RESET_MESSAGE).sendToTarget();
- }
-
/**
* Transient help message (acquire) is received, dialog stays showing. Sensor stays in
* "authenticating" state.
@@ -484,6 +475,7 @@ public abstract class BiometricDialogView extends LinearLayout {
mPositiveButton.setVisibility(bundle.getInt(KEY_CONFIRM_VISIBILITY));
mState = bundle.getInt(KEY_STATE);
mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+ mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
mErrorText.setVisibility(bundle.getInt(KEY_ERROR_TEXT_VISIBILITY));
mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index dbbb71c93c03..8f26f1847779 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -288,6 +288,7 @@ public class FaceDialogView extends BiometricDialogView {
@Override
protected void handleResetMessage() {
mErrorText.setText(getHintStringResourceId());
+ mErrorText.setContentDescription(mContext.getString(getHintStringResourceId()));
mErrorText.setTextColor(mTextColor);
if (getState() == STATE_AUTHENTICATING) {
mErrorText.setVisibility(View.VISIBLE);
@@ -406,13 +407,21 @@ public class FaceDialogView extends BiometricDialogView {
} else {
mIconController.showIcon(R.drawable.face_dialog_pulse_dark_to_light);
}
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticating));
} else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_confirmed));
} else if (oldState == STATE_ERROR && newState == STATE_IDLE) {
mIconController.animateOnce(R.drawable.face_dialog_error_to_idle);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_idle));
} else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) {
mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_ERROR) {
// It's easier to only check newState and gate showing the animation on the
// mErrorToIdleAnimationRunnable as a proxy, than add a ton of extra state. For example,
@@ -426,11 +435,17 @@ public class FaceDialogView extends BiometricDialogView {
}
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_PENDING_CONFIRMATION) {
mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
mIconController.animateOnce(R.drawable.face_dialog_wink_from_dark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_IDLE) {
mIconController.showStatic(R.drawable.face_dialog_idle_static);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_idle));
} else {
Log.w(TAG, "Unknown animation from " + oldState + " -> " + newState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
index 845b08483064..74ad0faca6d3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
@@ -18,12 +18,15 @@ package com.android.systemui.bubbles;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
+import com.android.systemui.R;
+
// XXX: Mostly opied from launcher code / can we share?
/**
* Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
@@ -32,20 +35,31 @@ public class BadgeRenderer {
private static final String TAG = "BadgeRenderer";
- // The badge sizes are defined as percentages of the app icon size.
+ /** The badge sizes are defined as percentages of the app icon size. */
private static final float SIZE_PERCENTAGE = 0.38f;
- // Extra scale down of the dot
+ /** Extra scale down of the dot. */
private static final float DOT_SCALE = 0.6f;
private final float mDotCenterOffset;
private final float mCircleRadius;
private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
- public BadgeRenderer(int iconSizePx) {
- mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
- int size = (int) (DOT_SCALE * mDotCenterOffset);
- mCircleRadius = size / 2f;
+ public BadgeRenderer(Context context) {
+ mDotCenterOffset = getDotCenterOffset(context);
+ mCircleRadius = getDotRadius(mDotCenterOffset);
+ }
+
+ /** Space between the center of the dot and the top or left of the bubble stack. */
+ static float getDotCenterOffset(Context context) {
+ final int iconSizePx =
+ context.getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
+ return SIZE_PERCENTAGE * iconSizePx;
+ }
+
+ static float getDotRadius(float dotCenterOffset) {
+ int size = (int) (DOT_SCALE * dotCenterOffset);
+ return size / 2f;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index f15e8e47649c..783780f8819c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -57,7 +57,7 @@ public class BadgedImageView extends ImageView {
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mIconSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
- mDotRenderer = new BadgeRenderer(mIconSize);
+ mDotRenderer = new BadgeRenderer(getContext());
TypedArray ta = context.obtainStyledAttributes(
new int[] {android.R.attr.colorBackgroundFloating});
@@ -83,6 +83,10 @@ public class BadgedImageView extends ImageView {
invalidate();
}
+ public boolean getDotPosition() {
+ return mOnLeft;
+ }
+
/**
* Set whether the dot should show or not.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index ac4a93ba7fb0..f60e95e32600 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -18,6 +18,9 @@ package com.android.systemui.bubbles;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.view.LayoutInflater;
@@ -37,6 +40,7 @@ class Bubble {
private final String mKey;
private final String mGroupId;
+ private String mAppName;
private final BubbleExpandedView.OnBubbleBlockedListener mListener;
private boolean mInflated;
@@ -45,6 +49,7 @@ class Bubble {
BubbleExpandedView expandedView;
private long mLastUpdated;
private long mLastAccessed;
+ private PackageManager mPm;
public static String groupId(NotificationEntry entry) {
UserHandle user = entry.notification.getUser();
@@ -53,16 +58,33 @@ class Bubble {
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
- Bubble(NotificationEntry e) {
- this (e, null);
+ Bubble(Context context, NotificationEntry e) {
+ this (context, e, null);
}
- Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) {
+ Bubble(Context context, NotificationEntry e,
+ BubbleExpandedView.OnBubbleBlockedListener listener) {
entry = e;
mKey = e.key;
mLastUpdated = e.notification.getPostTime();
mGroupId = groupId(e);
mListener = listener;
+
+ mPm = context.getPackageManager();
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ entry.notification.getPackageName(),
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ mAppName = String.valueOf(mPm.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException unused) {
+ mAppName = entry.notification.getPackageName();
+ }
}
public String getKey() {
@@ -77,13 +99,17 @@ class Bubble {
return entry.notification.getPackageName();
}
+ public String getAppName() {
+ return mAppName;
+ }
+
boolean isInflated() {
return mInflated;
}
public void updateDotVisibility() {
if (iconView != null) {
- iconView.updateDotVisibility();
+ iconView.updateDotVisibility(true /* animate */);
}
}
@@ -97,9 +123,9 @@ class Bubble {
expandedView = (BubbleExpandedView) inflater.inflate(
R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
- expandedView.setEntry(entry, stackView);
-
+ expandedView.setEntry(entry, stackView, mAppName);
expandedView.setOnBlockedListener(mListener);
+
mInflated = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 48edf67a3ed4..7bfd168eea25 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,9 @@
package com.android.systemui.bubbles;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -40,6 +43,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
@@ -48,6 +52,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenModeConfig;
import android.util.Log;
import android.view.Display;
import android.view.IPinnedStackController;
@@ -74,6 +79,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -139,6 +145,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Bubbles get added to the status bar view
private final StatusBarWindowController mStatusBarWindowController;
+ private final ZenModeController mZenModeController;
private StatusBarStateListener mStatusBarStateListener;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
@@ -193,24 +200,38 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (shouldCollapse) {
collapseStack();
}
- updateVisibility();
+ updateStack();
}
}
@Inject
public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
BubbleData data, ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider) {
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController) {
this(context, statusBarWindowController, data, null /* synchronizer */,
- configurationController, interruptionStateProvider);
+ configurationController, interruptionStateProvider, zenModeController);
}
public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider) {
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController) {
mContext = context;
mNotificationInterruptionStateProvider = interruptionStateProvider;
+ mZenModeController = zenModeController;
+ mZenModeController.addCallback(new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(int zen) {
+ updateStackViewForZenConfig();
+ }
+
+ @Override
+ public void onConfigChanged(ZenModeConfig config) {
+ updateStackViewForZenConfig();
+ }
+ });
configurationController.addCallback(this /* configurationListener */);
@@ -256,6 +277,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
}
+
+ updateStackViewForZenConfig();
}
}
@@ -381,6 +404,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* @param notif the notification associated with this bubble.
*/
void updateBubble(NotificationEntry notif) {
+ // If this is an interruptive notif, mark that it's interrupted
+ if (notif.importance >= NotificationManager.IMPORTANCE_HIGH) {
+ notif.setInterruption();
+ }
mBubbleData.notificationEntryUpdated(notif);
}
@@ -534,10 +561,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
+ // Runs on state change.
@Override
public void apply() {
mNotificationEntryManager.updateNotifications();
- updateVisibility();
+ updateStack();
if (DEBUG) {
Log.d(TAG, "[BubbleData]");
@@ -554,34 +582,53 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
};
/**
- * Lets any listeners know if bubble state has changed.
+ * Updates the stack view's suppression flags from the latest config from the zen (do not
+ * disturb) controller.
*/
- private void updateBubblesShowing() {
- if (mStackView == null) {
- return;
- }
+ private void updateStackViewForZenConfig() {
+ final int suppressedEffects = mZenModeController.getConfig().suppressedVisualEffects;
+ final boolean hideNotificationDotsSelected =
+ (suppressedEffects & SUPPRESSED_EFFECT_BADGE) != 0;
+ final boolean dontPopNotifsOnScreenSelected =
+ (suppressedEffects & SUPPRESSED_EFFECT_PEEK) != 0;
+ final boolean hideFromPullDownShadeSelected =
+ (suppressedEffects & SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
- boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
- boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
- mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
- if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
- mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
- }
+ final boolean dndEnabled = mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;
+
+ mStackView.setSuppressNewDot(
+ dndEnabled && hideNotificationDotsSelected);
+ mStackView.setSuppressFlyout(
+ dndEnabled && (dontPopNotifsOnScreenSelected || hideFromPullDownShadeSelected));
}
/**
+ * Lets any listeners know if bubble state has changed.
* Updates the visibility of the bubbles based on current state.
- * Does not un-bubble, just hides or un-hides. Will notify any
- * {@link BubbleStateChangeListener}s if visibility changes.
+ * Does not un-bubble, just hides or un-hides. Notifies any
+ * {@link BubbleStateChangeListener}s of visibility changes.
+ * Updates stack description for TalkBack focus.
*/
- public void updateVisibility() {
+ public void updateStack() {
+ if (mStackView == null) {
+ return;
+ }
if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) {
// Bubbles only appear in unlocked shade
mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
} else if (mStackView != null) {
mStackView.setVisibility(INVISIBLE);
}
- updateBubblesShowing();
+
+ // Let listeners know if bubble state changed.
+ boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
+ boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
+ mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
+ if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
+ mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
+ }
+
+ mStackView.updateContentDescription();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 9156e06fe54e..1858244d13bc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -178,7 +178,7 @@ public class BubbleData {
Bubble bubble = getBubbleWithKey(entry.key);
if (bubble == null) {
// Create a new bubble
- bubble = new Bubble(entry, this::onBubbleBlocked);
+ bubble = new Bubble(mContext, entry, this::onBubbleBlocked);
doAdd(bubble);
trim();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 39867c3a0bdb..fa137a1f6207 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -244,9 +244,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
/**
* Sets the notification entry used to populate this view.
*/
- public void setEntry(NotificationEntry entry, BubbleStackView stackView) {
+ public void setEntry(NotificationEntry entry, BubbleStackView stackView, String appName) {
mStackView = stackView;
mEntry = entry;
+ mAppName = appName;
ApplicationInfo info;
try {
@@ -257,12 +258,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
if (info != null) {
- mAppName = String.valueOf(mPm.getApplicationLabel(info));
mAppIcon = mPm.getApplicationIcon(info);
}
} catch (PackageManager.NameNotFoundException e) {
- // Ahh... just use package name
- mAppName = entry.notification.getPackageName();
+ // Do nothing.
}
if (mAppIcon == null) {
mAppIcon = mPm.getDefaultActivityIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
new file mode 100644
index 000000000000..71f68c16bd8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2019 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.bubbles;
+
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import android.animation.ArgbEvaluator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.graphics.drawable.ShapeDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringAnimation;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
+ * transform into the 'new' dot, which is used during flyout dismiss animations/gestures.
+ */
+public class BubbleFlyoutView extends FrameLayout {
+ /** Max width of the flyout, in terms of percent of the screen width. */
+ private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
+
+ private final int mFlyoutPadding;
+ private final int mFlyoutSpaceFromBubble;
+ private final int mPointerSize;
+ private final int mBubbleSize;
+ private final int mFlyoutElevation;
+ private final int mBubbleElevation;
+ private final int mFloatingBackgroundColor;
+ private final float mCornerRadius;
+
+ private final ViewGroup mFlyoutTextContainer;
+ private final TextView mFlyoutText;
+ /** Spring animation for the flyout. */
+ private final SpringAnimation mFlyoutSpring =
+ new SpringAnimation(this, DynamicAnimation.TRANSLATION_X);
+
+ /** Values related to the 'new' dot which we use to figure out where to collapse the flyout. */
+ private final float mNewDotRadius;
+ private final float mNewDotSize;
+ private final float mNewDotOffsetFromBubbleBounds;
+
+ /**
+ * The paint used to draw the background, whose color changes as the flyout transitions to the
+ * tinted 'new' dot.
+ */
+ private final Paint mBgPaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
+ private final ArgbEvaluator mArgbEvaluator = new ArgbEvaluator();
+
+ /**
+ * Triangular ShapeDrawables used for the triangle that points from the flyout to the bubble
+ * stack (a chat-bubble effect).
+ */
+ private final ShapeDrawable mLeftTriangleShape;
+ private final ShapeDrawable mRightTriangleShape;
+
+ /** Whether the flyout arrow is on the left (pointing left) or right (pointing right). */
+ private boolean mArrowPointingLeft = true;
+
+ /** Color of the 'new' dot that the flyout will transform into. */
+ private int mDotColor;
+
+ /** The outline of the triangle, used for elevation shadows. */
+ private final Outline mTriangleOutline = new Outline();
+
+ /** The bounds of the flyout background, kept up to date as it transitions to the 'new' dot. */
+ private final RectF mBgRect = new RectF();
+
+ /**
+ * Percent progress in the transition from flyout to 'new' dot. These two values are the inverse
+ * of each other (if we're 40% transitioned to the dot, we're 60% flyout), but it makes the code
+ * much more readable.
+ */
+ private float mPercentTransitionedToDot = 1f;
+ private float mPercentStillFlyout = 0f;
+
+ /**
+ * The difference in values between the flyout and the dot. These differences are gradually
+ * added over the course of the animation to transform the flyout into the 'new' dot.
+ */
+ private float mFlyoutToDotWidthDelta = 0f;
+ private float mFlyoutToDotHeightDelta = 0f;
+ private float mFlyoutToDotCornerRadiusDelta;
+
+ /** The translation values when the flyout is completely transitioned into the dot. */
+ private float mTranslationXWhenDot = 0f;
+ private float mTranslationYWhenDot = 0f;
+
+ /**
+ * The current translation values applied to the flyout background as it transitions into the
+ * 'new' dot.
+ */
+ private float mBgTranslationX;
+ private float mBgTranslationY;
+
+ /** The flyout's X translation when at rest (not animating or dragging). */
+ private float mRestingTranslationX = 0f;
+
+ /** Callback to run when the flyout is hidden. */
+ private Runnable mOnHide;
+
+ public BubbleFlyoutView(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
+
+ mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
+ mFlyoutText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+
+ final Resources res = getResources();
+ mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
+ mFlyoutSpaceFromBubble = res.getDimensionPixelSize(R.dimen.bubble_flyout_space_from_bubble);
+ mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size);
+ mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+ mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
+ mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation);
+ mNewDotOffsetFromBubbleBounds = BadgeRenderer.getDotCenterOffset(context);
+ mNewDotRadius = BadgeRenderer.getDotRadius(mNewDotOffsetFromBubbleBounds);
+ mNewDotSize = mNewDotRadius * 2f;
+
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {
+ android.R.attr.colorBackgroundFloating,
+ android.R.attr.dialogCornerRadius});
+ mFloatingBackgroundColor = ta.getColor(0, Color.WHITE);
+ mCornerRadius = ta.getDimensionPixelSize(1, 0);
+ mFlyoutToDotCornerRadiusDelta = mNewDotRadius - mCornerRadius;
+ ta.recycle();
+
+ // Add padding for the pointer on either side, onDraw will draw it in this space.
+ setPadding(mPointerSize, 0, mPointerSize, 0);
+ setWillNotDraw(false);
+ setClipChildren(false);
+ setTranslationZ(mFlyoutElevation);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ BubbleFlyoutView.this.getOutline(outline);
+ }
+ });
+
+ mBgPaint.setColor(mFloatingBackgroundColor);
+
+ mLeftTriangleShape =
+ new ShapeDrawable(TriangleShape.createHorizontal(
+ mPointerSize, mPointerSize, true /* isPointingLeft */));
+ mLeftTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
+ mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+
+ mRightTriangleShape =
+ new ShapeDrawable(TriangleShape.createHorizontal(
+ mPointerSize, mPointerSize, false /* isPointingLeft */));
+ mRightTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
+ mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ renderBackground(canvas);
+ invalidateOutline();
+ super.onDraw(canvas);
+ }
+
+ /** Configures the flyout and animates it in. */
+ void showFlyout(
+ CharSequence updateMessage, PointF stackPos, float parentWidth,
+ boolean arrowPointingLeft, int dotColor, Runnable onHide) {
+ mArrowPointingLeft = arrowPointingLeft;
+ mDotColor = dotColor;
+ mOnHide = onHide;
+
+ setCollapsePercent(0f);
+ setAlpha(0f);
+ setVisibility(VISIBLE);
+
+ // Set the flyout TextView's max width in terms of percent, and then subtract out the
+ // padding so that the entire flyout view will be the desired width (rather than the
+ // TextView being the desired width + extra padding).
+ mFlyoutText.setMaxWidth(
+ (int) (parentWidth * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2);
+ mFlyoutText.setText(updateMessage);
+
+ // Wait for the TextView to lay out so we know its line count.
+ post(() -> {
+ // Multi line flyouts get top-aligned to the bubble.
+ if (mFlyoutText.getLineCount() > 1) {
+ setTranslationY(stackPos.y);
+ } else {
+ // Single line flyouts are vertically centered with respect to the bubble.
+ setTranslationY(
+ stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f);
+ }
+
+ // Calculate the translation required to position the flyout next to the bubble stack,
+ // with the desired padding.
+ mRestingTranslationX = mArrowPointingLeft
+ ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble
+ : stackPos.x - getWidth() - mFlyoutSpaceFromBubble;
+
+ // Translate towards the stack slightly.
+ setTranslationX(
+ mRestingTranslationX + (arrowPointingLeft ? -mBubbleSize : mBubbleSize));
+
+ // Fade in the entire flyout and spring it to its normal position.
+ animate().alpha(1f);
+ mFlyoutSpring.animateToFinalPosition(mRestingTranslationX);
+
+ // Calculate the difference in size between the flyout and the 'dot' so that we can
+ // transform into the dot later.
+ mFlyoutToDotWidthDelta = getWidth() - mNewDotSize;
+ mFlyoutToDotHeightDelta = getHeight() - mNewDotSize;
+
+ // Calculate the translation values needed to be in the correct 'new dot' position.
+ final float distanceFromFlyoutLeftToDotCenterX =
+ mFlyoutSpaceFromBubble + mNewDotOffsetFromBubbleBounds / 2;
+ if (mArrowPointingLeft) {
+ mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX - mNewDotRadius;
+ } else {
+ mTranslationXWhenDot =
+ getWidth() + distanceFromFlyoutLeftToDotCenterX - mNewDotRadius;
+ }
+
+ mTranslationYWhenDot =
+ getHeight() / 2f
+ - mNewDotRadius
+ - mBubbleSize / 2f
+ + mNewDotOffsetFromBubbleBounds / 2;
+ });
+ }
+
+ /**
+ * Hides the flyout and runs the optional callback passed into showFlyout. The flyout has been
+ * animated into the 'new' dot by the time we call this, so no animations are needed.
+ */
+ void hideFlyout() {
+ if (mOnHide != null) {
+ mOnHide.run();
+ mOnHide = null;
+ }
+
+ setVisibility(GONE);
+ }
+
+ /** Sets the percentage that the flyout should be collapsed into dot form. */
+ void setCollapsePercent(float percentCollapsed) {
+ mPercentTransitionedToDot = Math.max(0f, Math.min(percentCollapsed, 1f));
+ mPercentStillFlyout = (1f - mPercentTransitionedToDot);
+
+ // Move and fade out the text.
+ mFlyoutText.setTranslationX(
+ (mArrowPointingLeft ? -getWidth() : getWidth()) * mPercentTransitionedToDot);
+ mFlyoutText.setAlpha(clampPercentage(
+ (mPercentStillFlyout - (1f - BubbleStackView.FLYOUT_DRAG_PERCENT_DISMISS))
+ / BubbleStackView.FLYOUT_DRAG_PERCENT_DISMISS));
+
+ // Reduce the elevation towards that of the topmost bubble.
+ setTranslationZ(
+ mFlyoutElevation
+ - (mFlyoutElevation - mBubbleElevation) * mPercentTransitionedToDot);
+ invalidate();
+ }
+
+ /** Return the flyout's resting X translation (translation when not dragging or animating). */
+ float getRestingTranslationX() {
+ return mRestingTranslationX;
+ }
+
+ /** Clamps a float to between 0 and 1. */
+ private float clampPercentage(float percent) {
+ return Math.min(1f, Math.max(0f, percent));
+ }
+
+ /**
+ * Renders the background, which is either the rounded 'chat bubble' flyout, or some state
+ * between that and the 'new' dot over the bubbles.
+ */
+ private void renderBackground(Canvas canvas) {
+ // Calculate the width, height, and corner radius of the flyout given the current collapsed
+ // percentage.
+ final float width = getWidth() - (mFlyoutToDotWidthDelta * mPercentTransitionedToDot);
+ final float height = getHeight() - (mFlyoutToDotHeightDelta * mPercentTransitionedToDot);
+ final float cornerRadius = mCornerRadius
+ - (mFlyoutToDotCornerRadiusDelta * mPercentTransitionedToDot);
+
+ // Translate the flyout background towards the collapsed 'dot' state.
+ mBgTranslationX = mTranslationXWhenDot * mPercentTransitionedToDot;
+ mBgTranslationY = mTranslationYWhenDot * mPercentTransitionedToDot;
+
+ // Set the bounds of the rounded rectangle that serves as either the flyout background or
+ // the collapsed 'dot'. These bounds will also be used to provide the outline for elevation
+ // shadows. In the expanded flyout state, the left and right bounds leave space for the
+ // pointer triangle - as the flyout collapses, this space is reduced since the triangle
+ // retracts into the flyout.
+ mBgRect.set(
+ mPointerSize * mPercentStillFlyout /* left */,
+ 0 /* top */,
+ width - mPointerSize * mPercentStillFlyout /* right */,
+ height /* bottom */);
+
+ mBgPaint.setColor(
+ (int) mArgbEvaluator.evaluate(
+ mPercentTransitionedToDot, mFloatingBackgroundColor, mDotColor));
+
+ canvas.save();
+ canvas.translate(mBgTranslationX, mBgTranslationY);
+ renderPointerTriangle(canvas, width, height);
+ canvas.drawRoundRect(mBgRect, cornerRadius, cornerRadius, mBgPaint);
+ canvas.restore();
+ }
+
+ /** Renders the 'pointer' triangle that points from the flyout to the bubble stack. */
+ private void renderPointerTriangle(
+ Canvas canvas, float currentFlyoutWidth, float currentFlyoutHeight) {
+ canvas.save();
+
+ // Translation to apply for the 'retraction' effect as the flyout collapses.
+ final float retractionTranslationX =
+ (mArrowPointingLeft ? 1 : -1) * (mPercentTransitionedToDot * mPointerSize * 2f);
+
+ // Place the arrow either at the left side, or the far right, depending on whether the
+ // flyout is on the left or right side.
+ final float arrowTranslationX =
+ mArrowPointingLeft
+ ? retractionTranslationX
+ : currentFlyoutWidth - mPointerSize + retractionTranslationX;
+
+ // Vertically center the arrow at all times.
+ final float arrowTranslationY = currentFlyoutHeight / 2f - mPointerSize / 2f;
+
+ // Draw the appropriate direction of arrow.
+ final ShapeDrawable relevantTriangle =
+ mArrowPointingLeft ? mLeftTriangleShape : mRightTriangleShape;
+ canvas.translate(arrowTranslationX, arrowTranslationY);
+ relevantTriangle.setAlpha((int) (255f * mPercentStillFlyout));
+ relevantTriangle.draw(canvas);
+
+ // Save the triangle's outline for use in the outline provider, offsetting it to reflect its
+ // current position.
+ relevantTriangle.getOutline(mTriangleOutline);
+ mTriangleOutline.offset((int) arrowTranslationX, (int) arrowTranslationY);
+
+ canvas.restore();
+ }
+
+ /** Builds an outline that includes the transformed flyout background and triangle. */
+ private void getOutline(Outline outline) {
+ if (!mTriangleOutline.isEmpty()) {
+ // Draw the rect into the outline as a path so we can merge the triangle path into it.
+ final Path rectPath = new Path();
+ rectPath.addRoundRect(mBgRect, mCornerRadius, mCornerRadius, Path.Direction.CW);
+ outline.setConvexPath(rectPath);
+
+ // Get rid of the triangle path once it has disappeared behind the flyout.
+ if (mPercentStillFlyout > 0.5f) {
+ outline.mPath.addPath(mTriangleOutline.mPath);
+ }
+
+ // Translate the outline to match the background's position.
+ final Matrix outlineMatrix = new Matrix();
+ outlineMatrix.postTranslate(getLeft() + mBgTranslationX, getTop() + mBgTranslationY);
+
+ // At the very end, retract the outline into the bubble so the shadow will be pulled
+ // into the flyout-dot as it (visually) becomes part of the bubble. We can't do this by
+ // animating translationZ to zero since then it'll go under the bubbles, which have
+ // elevation.
+ if (mPercentTransitionedToDot > 0.98f) {
+ final float percentBetween99and100 = (mPercentTransitionedToDot - 0.98f) / .02f;
+ final float percentShadowVisible = 1f - percentBetween99and100;
+
+ // Keep it centered.
+ outlineMatrix.postTranslate(
+ mNewDotRadius * percentBetween99and100,
+ mNewDotRadius * percentBetween99and100);
+ outlineMatrix.preScale(percentShadowVisible, percentShadowVisible);
+ }
+
+ outline.mPath.transform(outlineMatrix);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 2b1742592fba..6391070fe45d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -23,10 +23,9 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Outline;
@@ -35,8 +34,6 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -56,11 +53,11 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
-import android.widget.TextView;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
@@ -70,7 +67,6 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
-import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.math.BigDecimal;
@@ -86,12 +82,21 @@ public class BubbleStackView extends FrameLayout {
private static final String TAG = "BubbleStackView";
private static final boolean DEBUG = false;
+ /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
+ static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f;
+
+ /** Velocity required to dismiss the flyout via drag. */
+ private static final float FLYOUT_DISMISS_VELOCITY = 2000f;
+
+ /**
+ * Factor for attenuating translation when the flyout is overscrolled (8f = flyout moves 1 pixel
+ * for every 8 pixels overscrolled).
+ */
+ private static final float FLYOUT_OVERSCROLL_ATTENUATION_FACTOR = 8f;
+
/** Duration of the flyout alpha animations. */
private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
- /** Max width of the flyout, in terms of percent of the screen width. */
- private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
-
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
@@ -152,17 +157,9 @@ public class BubbleStackView extends FrameLayout {
private FrameLayout mExpandedViewContainer;
- private FrameLayout mFlyoutContainer;
- private FrameLayout mFlyout;
- private TextView mFlyoutText;
- private ShapeDrawable mLeftFlyoutTriangle;
- private ShapeDrawable mRightFlyoutTriangle;
- /** Spring animation for the flyout. */
- private SpringAnimation mFlyoutSpring;
+ private BubbleFlyoutView mFlyout;
/** Runnable that fades out the flyout and then sets it to GONE. */
- private Runnable mHideFlyout =
- () -> mFlyoutContainer.animate().alpha(0f).withEndAction(
- () -> mFlyoutContainer.setVisibility(GONE));
+ private Runnable mHideFlyout = () -> animateFlyoutCollapsed(true, 0 /* velX */);
/** Layout change listener that moves the stack to the nearest valid position on rotation. */
private OnLayoutChangeListener mMoveStackToValidPositionOnLayoutListener;
@@ -176,9 +173,6 @@ public class BubbleStackView extends FrameLayout {
private int mBubbleSize;
private int mBubblePadding;
- private int mFlyoutPadding;
- private int mFlyoutSpaceFromBubble;
- private int mPointerSize;
private int mExpandedAnimateXDistance;
private int mExpandedAnimateYDistance;
private int mStatusBarHeight;
@@ -189,8 +183,11 @@ public class BubbleStackView extends FrameLayout {
private boolean mIsExpanded;
private boolean mImeVisible;
- /** Whether the stack is currently being dragged. */
- private boolean mIsDragging = false;
+ /** Whether the stack is currently on the left side of the screen, or animating there. */
+ private boolean mStackOnLeftOrWillBe = false;
+
+ /** Whether a touch gesture, such as a stack/bubble drag or flyout drag, is in progress. */
+ private boolean mIsGestureInProgress = false;
private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
@@ -249,11 +246,48 @@ public class BubbleStackView extends FrameLayout {
}
};
+ /** Float property that 'drags' the flyout. */
+ private final FloatPropertyCompat mFlyoutCollapseProperty =
+ new FloatPropertyCompat("FlyoutCollapseSpring") {
+ @Override
+ public float getValue(Object o) {
+ return mFlyoutDragDeltaX;
+ }
+
+ @Override
+ public void setValue(Object o, float v) {
+ onFlyoutDragged(v);
+ }
+ };
+
+ /** SpringAnimation that springs the flyout collapsed via onFlyoutDragged. */
+ private final SpringAnimation mFlyoutTransitionSpring =
+ new SpringAnimation(this, mFlyoutCollapseProperty);
+
+ /** Distance the flyout has been dragged in the X axis. */
+ private float mFlyoutDragDeltaX = 0f;
+
+ /**
+ * End listener for the flyout spring that either posts a runnable to hide the flyout, or hides
+ * it immediately.
+ */
+ private final DynamicAnimation.OnAnimationEndListener mAfterFlyoutTransitionSpring =
+ (dynamicAnimation, b, v, v1) -> {
+ if (mFlyoutDragDeltaX == 0) {
+ mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
+ } else {
+ mFlyout.hideFlyout();
+ }
+ };
+
@NonNull private final SurfaceSynchronizer mSurfaceSynchronizer;
private BubbleDismissView mDismissContainer;
private Runnable mAfterMagnet;
+ private boolean mSuppressNewDot = false;
+ private boolean mSuppressFlyout = false;
+
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer) {
super(context);
@@ -267,9 +301,6 @@ public class BubbleStackView extends FrameLayout {
Resources res = getResources();
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
- mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
- mFlyoutSpaceFromBubble = res.getDimensionPixelSize(R.dimen.bubble_flyout_space_from_bubble);
- mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size);
mExpandedAnimateXDistance =
res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
mExpandedAnimateYDistance =
@@ -307,17 +338,24 @@ public class BubbleStackView extends FrameLayout {
mExpandedViewContainer.setClipChildren(false);
addView(mExpandedViewContainer);
- mFlyoutContainer = (FrameLayout) mInflater.inflate(R.layout.bubble_flyout, this, false);
- mFlyoutContainer.setVisibility(GONE);
- mFlyoutContainer.setClipToPadding(false);
- mFlyoutContainer.setClipChildren(false);
- mFlyoutContainer.animate()
+ mFlyout = new BubbleFlyoutView(context);
+ mFlyout.setVisibility(GONE);
+ mFlyout.animate()
.setDuration(FLYOUT_ALPHA_ANIMATION_DURATION)
.setInterpolator(new AccelerateDecelerateInterpolator());
+ addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mFlyout = mFlyoutContainer.findViewById(R.id.bubble_flyout);
- addView(mFlyoutContainer);
- setupFlyout();
+ mFlyoutTransitionSpring.setSpring(new SpringForce()
+ .setStiffness(SpringForce.STIFFNESS_MEDIUM)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
+ mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
+
+ mDismissContainer = new BubbleDismissView(mContext);
+ mDismissContainer.setLayoutParams(new FrameLayout.LayoutParams(
+ MATCH_PARENT,
+ getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height),
+ Gravity.BOTTOM));
+ addView(mDismissContainer);
mDismissContainer = new BubbleDismissView(mContext);
mDismissContainer.setLayoutParams(new FrameLayout.LayoutParams(
@@ -519,6 +557,43 @@ public class BubbleStackView extends FrameLayout {
return false;
}
+ /**
+ * Update content description for a11y TalkBack.
+ */
+ public void updateContentDescription() {
+ if (mBubbleData.getBubbles().isEmpty()) {
+ return;
+ }
+ Bubble topBubble = mBubbleData.getBubbles().get(0);
+ String appName = topBubble.getAppName();
+ Notification notification = topBubble.entry.notification.getNotification();
+ CharSequence titleCharSeq = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
+ String titleStr = getResources().getString(R.string.stream_notification);
+ if (titleCharSeq != null) {
+ titleStr = titleCharSeq.toString();
+ }
+ int moreCount = mBubbleContainer.getChildCount() - 1;
+
+ // Example: Title from app name.
+ String singleDescription = getResources().getString(
+ R.string.bubble_content_description_single, titleStr, appName);
+
+ // Example: Title from app name and 4 more.
+ String stackDescription = getResources().getString(
+ R.string.bubble_content_description_stack, titleStr, appName, moreCount);
+
+ if (mIsExpanded) {
+ // TODO(b/129522932) - update content description for each bubble in expanded view.
+ } else {
+ // Collapsed stack.
+ if (moreCount > 0) {
+ mBubbleContainer.setContentDescription(stackDescription);
+ } else {
+ mBubbleContainer.setContentDescription(singleDescription);
+ }
+ }
+ }
+
private void updateSystemGestureExcludeRects() {
// Exclude the region occupied by the first BubbleView in the stack
Rect excludeZone = mSystemGestureExclusionRects.get(0);
@@ -612,6 +687,9 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.addView(bubble.iconView, 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubble.iconView, true, mClippingParameters);
+ if (bubble.iconView != null) {
+ bubble.iconView.setSuppressDot(mSuppressNewDot, false /* animate */);
+ }
animateInFlyoutForBubble(bubble);
requestUpdate();
logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
@@ -742,7 +820,7 @@ public class BubbleStackView extends FrameLayout {
}
// Outside parts of view we care about.
return null;
- } else if (mFlyoutContainer.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
+ } else if (mFlyout.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
return mFlyout;
}
@@ -931,7 +1009,6 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.setController(mStackAnimationController);
hideFlyoutImmediate();
- mIsDragging = true;
mDraggingInDismissTarget = false;
}
@@ -948,20 +1025,87 @@ public class BubbleStackView extends FrameLayout {
if (DEBUG) {
Log.d(TAG, "onDragFinish");
}
- // TODO: Add fling to bottom to dismiss.
- mIsDragging = false;
if (mIsExpanded || mIsExpansionAnimating) {
return;
}
- mStackAnimationController.flingStackThenSpringToEdge(x, velX, velY);
+ final float newStackX = mStackAnimationController.flingStackThenSpringToEdge(x, velX, velY);
logBubbleEvent(null /* no bubble associated with bubble stack move */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
+ mStackOnLeftOrWillBe = newStackX <= 0;
+ updateBubbleShadowsAndDotPosition(true /* animate */);
springOutDismissTargetAndHideCircle();
}
+ void onFlyoutDragStart() {
+ mFlyout.removeCallbacks(mHideFlyout);
+ }
+
+ void onFlyoutDragged(float deltaX) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ mFlyoutDragDeltaX = deltaX;
+
+ final float collapsePercent =
+ onLeft ? -deltaX / mFlyout.getWidth() : deltaX / mFlyout.getWidth();
+ mFlyout.setCollapsePercent(Math.min(1f, Math.max(0f, collapsePercent)));
+
+ // Calculate how to translate the flyout if it has been dragged too far in etiher direction.
+ float overscrollTranslation = 0f;
+ if (collapsePercent < 0f || collapsePercent > 1f) {
+ // Whether we are more than 100% transitioned to the dot.
+ final boolean overscrollingPastDot = collapsePercent > 1f;
+
+ // Whether we are overscrolling physically to the left - this can either be pulling the
+ // flyout away from the stack (if the stack is on the right) or pushing it to the left
+ // after it has already become the dot.
+ final boolean overscrollingLeft =
+ (onLeft && collapsePercent > 1f) || (!onLeft && collapsePercent < 0f);
+
+ overscrollTranslation =
+ (overscrollingPastDot ? collapsePercent - 1f : collapsePercent * -1)
+ * (overscrollingLeft ? -1 : 1)
+ * (mFlyout.getWidth() / (FLYOUT_OVERSCROLL_ATTENUATION_FACTOR
+ // Attenuate the smaller dot less than the larger flyout.
+ / (overscrollingPastDot ? 2 : 1)));
+ }
+
+ mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation);
+ }
+
+ /**
+ * Called when the flyout drag has finished, and returns true if the gesture successfully
+ * dismissed the flyout.
+ */
+ void onFlyoutDragFinished(float deltaX, float velX) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ final boolean metRequiredVelocity =
+ onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY;
+ final boolean metRequiredDeltaX =
+ onLeft
+ ? deltaX < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS
+ : deltaX > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS;
+ final boolean isCancelFling = onLeft ? velX > 0 : velX < 0;
+ final boolean shouldDismiss = metRequiredVelocity || (metRequiredDeltaX && !isCancelFling);
+
+ mFlyout.removeCallbacks(mHideFlyout);
+ animateFlyoutCollapsed(shouldDismiss, velX);
+ }
+
+ /**
+ * Called when the first touch event of a gesture (stack drag, bubble drag, flyout drag, etc.)
+ * is received.
+ */
+ void onGestureStart() {
+ mIsGestureInProgress = true;
+ }
+
+ /** Called when a gesture is completed or cancelled. */
+ void onGestureFinished() {
+ mIsGestureInProgress = false;
+ }
+
/** Prepares and starts the desaturate/darken animation on the bubble stack. */
private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
mDesaturateAndDarkenTargetView = targetView;
@@ -1119,12 +1263,22 @@ public class BubbleStackView extends FrameLayout {
mShowingDismiss = false;
}
-
/** Whether the location of the given MotionEvent is within the dismiss target area. */
- public boolean isInDismissTarget(MotionEvent ev) {
+ boolean isInDismissTarget(MotionEvent ev) {
return isIntersecting(mDismissContainer.getDismissTarget(), ev.getRawX(), ev.getRawY());
}
+ /** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */
+ private void animateFlyoutCollapsed(boolean collapsed, float velX) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ mFlyoutTransitionSpring
+ .setStartValue(mFlyoutDragDeltaX)
+ .setStartVelocity(velX)
+ .animateToFinalPosition(collapsed
+ ? (onLeft ? -mFlyout.getWidth() : mFlyout.getWidth())
+ : 0f);
+ }
+
/**
* Calculates how large the expanded view of the bubble can be. This takes into account the
* y position when the bubbles are expanded as well as the bounds of the dismiss target.
@@ -1153,6 +1307,29 @@ public class BubbleStackView extends FrameLayout {
}
}
+ /** Sets whether all bubbles in the stack should not show the 'new' dot. */
+ void setSuppressNewDot(boolean suppressNewDot) {
+ mSuppressNewDot = suppressNewDot;
+
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
+ bv.setSuppressDot(suppressNewDot, true /* animate */);
+ }
+ }
+
+ /**
+ * Sets whether the flyout should not appear, even if the notif otherwise would generate one.
+ */
+ void setSuppressFlyout(boolean suppressFlyout) {
+ mSuppressFlyout = suppressFlyout;
+ }
+
+ /**
+ * Callback to run after the flyout hides. Also called if a new flyout is shown before the
+ * previous one animates out.
+ */
+ private Runnable mAfterFlyoutHides;
+
/**
* Animates in the flyout for the given bubble, if available, and then hides it after some time.
*/
@@ -1161,63 +1338,61 @@ public class BubbleStackView extends FrameLayout {
final CharSequence updateMessage = bubble.entry.getUpdateMessage(getContext());
// Show the message if one exists, and we're not expanded or animating expansion.
- if (updateMessage != null && !isExpanded() && !mIsExpansionAnimating && !mIsDragging) {
- final PointF stackPos = mStackAnimationController.getStackPosition();
-
- // Set the flyout TextView's max width in terms of percent, and then subtract out the
- // padding so that the entire flyout view will be the desired width (rather than the
- // TextView being the desired width + extra padding).
- mFlyoutText.setMaxWidth(
- (int) (getWidth() * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2);
-
- mFlyoutContainer.setAlpha(0f);
- mFlyoutContainer.setVisibility(VISIBLE);
-
- mFlyoutText.setText(updateMessage);
-
- final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
-
- if (onLeft) {
- mLeftFlyoutTriangle.setAlpha(255);
- mRightFlyoutTriangle.setAlpha(0);
- } else {
- mLeftFlyoutTriangle.setAlpha(0);
- mRightFlyoutTriangle.setAlpha(255);
- }
-
- mFlyoutContainer.post(() -> {
- // Multi line flyouts get top-aligned to the bubble.
- if (mFlyoutText.getLineCount() > 1) {
- mFlyoutContainer.setTranslationY(stackPos.y);
- } else {
- // Single line flyouts are vertically centered with respect to the bubble.
- mFlyoutContainer.setTranslationY(
- stackPos.y + (mBubbleSize - mFlyout.getHeight()) / 2f);
+ if (updateMessage != null
+ && !isExpanded()
+ && !mIsExpansionAnimating
+ && !mIsGestureInProgress
+ && !mSuppressFlyout) {
+ if (bubble.iconView != null) {
+ // Temporarily suppress the dot while the flyout is visible.
+ bubble.iconView.setSuppressDot(
+ true /* suppressDot */, false /* animate */);
+
+ mFlyoutDragDeltaX = 0f;
+ mFlyout.setAlpha(0f);
+
+ if (mAfterFlyoutHides != null) {
+ mAfterFlyoutHides.run();
}
- final float destinationX = onLeft
- ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble
- : stackPos.x - mFlyoutContainer.getWidth() - mFlyoutSpaceFromBubble;
-
- // Translate towards the stack slightly, then spring out from the stack.
- mFlyoutContainer.setTranslationX(
- destinationX + (onLeft ? -mBubblePadding : mBubblePadding));
+ mAfterFlyoutHides = () -> {
+ // If we're going to suppress the dot, make it visible first so it'll
+ // visibly animate away.
+ if (mSuppressNewDot) {
+ bubble.iconView.setSuppressDot(
+ false /* suppressDot */, false /* animate */);
+ }
- mFlyoutContainer.animate().alpha(1f);
- mFlyoutSpring.animateToFinalPosition(destinationX);
+ // Reset dot suppression. If we're not suppressing due to DND, then
+ // stop suppressing it with no animation (since the flyout has
+ // transformed into the dot). If we are suppressing due to DND, animate
+ // it away.
+ bubble.iconView.setSuppressDot(
+ mSuppressNewDot /* suppressDot */,
+ mSuppressNewDot /* animate */);
+ };
- mFlyout.removeCallbacks(mHideFlyout);
- mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
- });
+ // Post in case layout isn't complete and getWidth returns 0.
+ post(() -> mFlyout.showFlyout(
+ updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
+ mStackAnimationController.isStackOnLeftSide(),
+ bubble.iconView.getBadgeColor(), mAfterFlyoutHides));
+ }
+ mFlyout.removeCallbacks(mHideFlyout);
+ mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
}
}
/** Hide the flyout immediately and cancel any pending hide runnables. */
private void hideFlyoutImmediate() {
+ if (mAfterFlyoutHides != null) {
+ mAfterFlyoutHides.run();
+ }
+
mFlyout.removeCallbacks(mHideFlyout);
- mHideFlyout.run();
+ mFlyout.hideFlyout();
}
@Override
@@ -1230,7 +1405,7 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.getBoundsOnScreen(outRect);
}
- if (mFlyoutContainer.getVisibility() == View.VISIBLE) {
+ if (mFlyout.getVisibility() == View.VISIBLE) {
final Rect flyoutBounds = new Rect();
mFlyout.getBoundsOnScreen(flyoutBounds);
outRect.union(flyoutBounds);
@@ -1287,78 +1462,11 @@ public class BubbleStackView extends FrameLayout {
}
}
- /** Sets up the flyout views and drawables. */
- private void setupFlyout() {
- // Retrieve the styled floating background color.
- TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.colorBackgroundFloating});
- final int floatingBackgroundColor = ta.getColor(0, Color.WHITE);
- ta.recycle();
-
- // Retrieve the flyout background, which is currently a rounded white rectangle with a
- // shadow but no triangular arrow pointing anywhere.
- final LayerDrawable flyoutBackground = (LayerDrawable) mFlyout.getBackground();
-
- // Create the triangle drawables and set their color.
- mLeftFlyoutTriangle =
- new ShapeDrawable(TriangleShape.createHorizontal(
- mPointerSize, mPointerSize, true /* isPointingLeft */));
- mRightFlyoutTriangle =
- new ShapeDrawable(TriangleShape.createHorizontal(
- mPointerSize, mPointerSize, false /* isPointingLeft */));
- mLeftFlyoutTriangle.getPaint().setColor(floatingBackgroundColor);
- mRightFlyoutTriangle.getPaint().setColor(floatingBackgroundColor);
-
- // Add both triangles to the drawable. We'll show and hide the appropriate ones when we show
- // the flyout.
- final int leftTriangleIndex = flyoutBackground.addLayer(mLeftFlyoutTriangle);
- flyoutBackground.setLayerSize(leftTriangleIndex, mPointerSize, mPointerSize);
- flyoutBackground.setLayerGravity(leftTriangleIndex, Gravity.LEFT | Gravity.CENTER_VERTICAL);
- flyoutBackground.setLayerInsetLeft(leftTriangleIndex, -mPointerSize);
-
- final int rightTriangleIndex = flyoutBackground.addLayer(mRightFlyoutTriangle);
- flyoutBackground.setLayerSize(rightTriangleIndex, mPointerSize, mPointerSize);
- flyoutBackground.setLayerGravity(
- rightTriangleIndex, Gravity.RIGHT | Gravity.CENTER_VERTICAL);
- flyoutBackground.setLayerInsetRight(rightTriangleIndex, -mPointerSize);
-
- // Append the appropriate triangle's outline to the view's outline so that the shadows look
- // correct.
- mFlyout.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- final boolean leftPointing = mStackAnimationController.isStackOnLeftSide();
-
- // Get the outline from the appropriate triangle.
- final Outline triangleOutline = new Outline();
- if (leftPointing) {
- mLeftFlyoutTriangle.getOutline(triangleOutline);
- } else {
- mRightFlyoutTriangle.getOutline(triangleOutline);
- }
-
- // Offset it to the correct position, since it has no intrinsic position since
- // that is maintained by the parent LayerDrawable.
- triangleOutline.offset(
- leftPointing ? -mPointerSize : mFlyout.getWidth(),
- mFlyout.getHeight() / 2 - mPointerSize / 2);
-
- // Merge the outlines.
- final Outline compoundOutline = new Outline();
- flyoutBackground.getOutline(compoundOutline);
- compoundOutline.mPath.addPath(triangleOutline.mPath);
- outline.set(compoundOutline);
- }
- });
-
- mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
- mFlyoutSpring = new SpringAnimation(mFlyoutContainer, DynamicAnimation.TRANSLATION_X);
- }
-
private void applyCurrentState() {
if (DEBUG) {
Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
}
+
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (mIsExpanded) {
// First update the view so that it calculates a new height (ensuring the y position
@@ -1376,10 +1484,16 @@ public class BubbleStackView extends FrameLayout {
}
}
+ mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
+ updateBubbleShadowsAndDotPosition(false);
+ }
+
+ /** Sets the appropriate Z-order and dot position for each bubble in the stack. */
+ private void updateBubbleShadowsAndDotPosition(boolean animate) {
int bubbsCount = mBubbleContainer.getChildCount();
for (int i = 0; i < bubbsCount; i++) {
BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
- bv.updateDotVisibility();
+ bv.updateDotVisibility(true /* animate */);
bv.setZ((BubbleController.MAX_BUBBLES
* getResources().getDimensionPixelSize(R.dimen.bubble_elevation)) - i);
@@ -1393,6 +1507,11 @@ public class BubbleStackView extends FrameLayout {
}
});
bv.setClipToOutline(false);
+
+ // If the dot is on the left, and so is the stack, we need to change the dot position.
+ if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) {
+ bv.setDotPosition(!mStackOnLeftOrWillBe, animate);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index f429c2c124b3..8fe8bd305707 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -111,12 +111,13 @@ class BubbleTouchHandler implements View.OnTouchListener {
trackMovement(event);
mTouchDown.set(rawX, rawY);
+ mStack.onGestureStart();
if (isStack) {
mViewPositionOnTouchDown.set(mStack.getStackPosition());
mStack.onDragStart();
} else if (isFlyout) {
- // TODO(b/129768381): Make the flyout dismissable with a gesture.
+ mStack.onFlyoutDragStart();
} else {
mViewPositionOnTouchDown.set(
mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
@@ -137,7 +138,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
if (isStack) {
mStack.onDragged(viewX, viewY);
} else if (isFlyout) {
- // TODO(b/129768381): Make the flyout dismissable with a gesture.
+ mStack.onFlyoutDragged(deltaX);
} else {
mStack.onBubbleDragged(mTouchedView, viewX, viewY);
}
@@ -152,8 +153,10 @@ class BubbleTouchHandler implements View.OnTouchListener {
final float velY = mVelocityTracker.getYVelocity();
// If the touch event is within the dismiss target, magnet the stack to it.
- mStack.animateMagnetToDismissTarget(
- mTouchedView, mInDismissTarget, viewX, viewY, velX, velY);
+ if (!isFlyout) {
+ mStack.animateMagnetToDismissTarget(
+ mTouchedView, mInDismissTarget, viewX, viewY, velX, velY);
+ }
}
break;
@@ -174,7 +177,9 @@ class BubbleTouchHandler implements View.OnTouchListener {
: mInDismissTarget
|| velY > INDIVIDUAL_BUBBLE_DISMISS_MIN_VELOCITY;
- if (shouldDismiss) {
+ if (isFlyout && mMovedEnough) {
+ mStack.onFlyoutDragFinished(rawX - mTouchDown.x /* deltaX */, velX);
+ } else if (shouldDismiss) {
final String individualBubbleKey =
isStack ? null : ((BubbleView) mTouchedView).getKey();
mStack.magnetToStackIfNeededThenAnimateDismissal(mTouchedView, velX, velY,
@@ -200,7 +205,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
}
} else if (mTouchedView == mStack.getExpandedBubbleView()) {
mBubbleData.setExpanded(false);
- } else if (isStack) {
+ } else if (isStack || isFlyout) {
// Toggle expansion
mBubbleData.setExpanded(!mBubbleData.isExpanded());
} else {
@@ -251,9 +256,12 @@ class BubbleTouchHandler implements View.OnTouchListener {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+
mTouchedView = null;
mMovedEnough = false;
mInDismissTarget = false;
+
+ mStack.onGestureFinished();
}
private void trackMovement(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 2681b6d0c891..6f1ed28d649e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -48,9 +48,12 @@ public class BubbleView extends FrameLayout {
private Context mContext;
private BadgedImageView mBadgedImageView;
+ private int mBadgeColor;
private int mPadding;
private int mIconInset;
+ private boolean mSuppressDot = false;
+
private NotificationEntry mEntry;
public BubbleView(Context context) {
@@ -130,31 +133,75 @@ public class BubbleView extends FrameLayout {
return (mEntry != null) ? mEntry.getRow() : null;
}
+ /** Changes the dot's visibility to match the bubble view's state. */
+ void updateDotVisibility(boolean animate) {
+ updateDotVisibility(animate, null /* after */);
+ }
+
+
/**
- * Marks this bubble as "read", i.e. no badge should show.
+ * Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the
+ * flyout is visible or animating, to hide the dot until the flyout visually transforms into it.
*/
- public void updateDotVisibility() {
- boolean showDot = getEntry().showInShadeWhenBubble();
- animateDot(showDot);
+ void setSuppressDot(boolean suppressDot, boolean animate) {
+ mSuppressDot = suppressDot;
+ updateDotVisibility(animate);
+ }
+
+ /** Sets the position of the 'new' dot, animating it out and back in if requested. */
+ void setDotPosition(boolean onLeft, boolean animate) {
+ if (animate && onLeft != mBadgedImageView.getDotPosition() && !mSuppressDot) {
+ animateDot(false /* showDot */, () -> {
+ mBadgedImageView.setDotPosition(onLeft);
+ animateDot(true /* showDot */, null);
+ });
+ } else {
+ mBadgedImageView.setDotPosition(onLeft);
+ }
+ }
+
+ boolean getDotPositionOnLeft() {
+ return mBadgedImageView.getDotPosition();
+ }
+
+ /**
+ * Changes the dot's visibility to match the bubble view's state, running the provided callback
+ * after animation if requested.
+ */
+ private void updateDotVisibility(boolean animate, Runnable after) {
+ boolean showDot = getEntry().showInShadeWhenBubble() && !mSuppressDot;
+
+ if (animate) {
+ animateDot(showDot, after);
+ } else {
+ mBadgedImageView.setShowDot(showDot);
+ }
}
/**
* Animates the badge to show or hide.
*/
- private void animateDot(boolean showDot) {
+ private void animateDot(boolean showDot, Runnable after) {
if (mBadgedImageView.isShowingDot() != showDot) {
- mBadgedImageView.setShowDot(showDot);
+ if (showDot) {
+ mBadgedImageView.setShowDot(true);
+ }
+
mBadgedImageView.clearAnimation();
mBadgedImageView.animate().setDuration(200)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setUpdateListener((valueAnimator) -> {
float fraction = valueAnimator.getAnimatedFraction();
- fraction = showDot ? fraction : 1 - fraction;
+ fraction = showDot ? fraction : 1f - fraction;
mBadgedImageView.setDotScale(fraction);
}).withEndAction(() -> {
- if (!showDot) {
- mBadgedImageView.setShowDot(false);
- }
+ if (!showDot) {
+ mBadgedImageView.setShowDot(false);
+ }
+
+ if (after != null) {
+ after.run();
+ }
}).start();
}
}
@@ -181,8 +228,13 @@ public class BubbleView extends FrameLayout {
mBadgedImageView.setImageDrawable(iconDrawable);
}
int badgeColor = determineDominateColor(iconDrawable, n.color);
+ mBadgeColor = badgeColor;
mBadgedImageView.setDotColor(badgeColor);
- animateDot(mEntry.showInShadeWhenBubble() /* showDot */);
+ animateDot(mEntry.showInShadeWhenBubble() /* showDot */, null /* after */);
+ }
+
+ int getBadgeColor() {
+ return mBadgeColor;
}
private Drawable buildIconWithTint(Drawable iconDrawable, int backgroundColor) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index f937525cf417..b9cdc844eef9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -86,7 +86,10 @@ public class StackAnimationController extends
* we need to keep track of it separately from the first bubble's translation in case there are
* no bubbles, or the first bubble was just added and being animated to its new position.
*/
- private PointF mStackPosition = new PointF();
+ private PointF mStackPosition = new PointF(-1, -1);
+
+ /** Whether or not the stack's start position has been set. */
+ private boolean mStackMovedToStartPosition = false;
/** The most recent position in which the stack was resting on the edge of the screen. */
private PointF mRestingStackPosition;
@@ -193,9 +196,10 @@ public class StackAnimationController extends
/** Whether the stack is on the left side of the screen. */
public boolean isStackOnLeftSide() {
- if (mLayout == null) {
+ if (mLayout == null || !isStackPositionSet()) {
return false;
}
+
float stackCenter = mStackPosition.x + mIndividualBubbleSize / 2;
float screenCenter = mLayout.getWidth() / 2;
return stackCenter < screenCenter;
@@ -225,8 +229,10 @@ public class StackAnimationController extends
/**
* Flings the stack starting with the given velocities, springing it to the nearest edge
* afterward.
+ *
+ * @return The X value that the stack will end up at after the fling/spring.
*/
- public void flingStackThenSpringToEdge(float x, float velX, float velY) {
+ public float flingStackThenSpringToEdge(float x, float velX, float velY) {
final boolean stackOnLeftSide = x - mIndividualBubbleSize / 2 < mLayout.getWidth() / 2;
final boolean stackShouldFlingLeft = stackOnLeftSide
@@ -281,6 +287,7 @@ public class StackAnimationController extends
DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
mIsMovingFromFlinging = true;
+ return destinationRelativeX;
}
/**
@@ -627,10 +634,9 @@ public class StackAnimationController extends
@Override
void onChildAdded(View child, int index) {
if (mLayout.getChildCount() == 1) {
- // If this is the first child added, position the stack in its starting position before
- // animating in.
- moveStackToStartPosition(() -> animateInBubble(child));
- } else if (mLayout.indexOfChild(child) == 0) {
+ // If this is the first child added, position the stack in its starting position.
+ moveStackToStartPosition();
+ } else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) {
// Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble
// to the back of the stack, it'll be largely invisible so don't bother animating it in.
animateInBubble(child);
@@ -654,16 +660,21 @@ public class StackAnimationController extends
}
/** Moves the stack, without any animation, to the starting position. */
- private void moveStackToStartPosition(Runnable after) {
+ private void moveStackToStartPosition() {
// Post to ensure that the layout's width and height have been calculated.
mLayout.setVisibility(View.INVISIBLE);
mLayout.post(() -> {
+ mStackMovedToStartPosition = true;
setStackPosition(
mRestingStackPosition == null
? getDefaultStartPosition()
: mRestingStackPosition);
mLayout.setVisibility(View.VISIBLE);
- after.run();
+
+ // Animate in the top bubble now that we're visible.
+ if (mLayout.getChildCount() > 0) {
+ animateInBubble(mLayout.getChildAt(0));
+ }
});
}
@@ -715,6 +726,10 @@ public class StackAnimationController extends
getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
}
+ private boolean isStackPositionSet() {
+ return mStackMovedToStartPosition;
+ }
+
/** Animates in the given bubble. */
private void animateInBubble(View child) {
child.setTranslationY(mStackPosition.y);
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index de1069064518..05665b5ae4a2 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -16,8 +16,6 @@
package com.android.systemui.colorextraction;
-import android.annotation.ColorInt;
-import android.annotation.IntDef;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
@@ -36,13 +34,10 @@ import com.android.internal.colorextraction.types.ExtractionType;
import com.android.internal.colorextraction.types.Tonal;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import javax.inject.Inject;
@@ -55,41 +50,23 @@ import javax.inject.Singleton;
public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
ConfigurationController.ConfigurationListener {
private static final String TAG = "SysuiColorExtractor";
-
- public static final int SCRIM_TYPE_REGULAR = 1;
- public static final int SCRIM_TYPE_LIGHT = 2;
- public static final int SCRIM_TYPE_DARK = 3;
-
- @IntDef(prefix = {"SCRIM_TYPE_"}, value = {
- SCRIM_TYPE_REGULAR,
- SCRIM_TYPE_LIGHT,
- SCRIM_TYPE_DARK
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScrimType {
- }
-
private final Tonal mTonal;
- private final OverviewProxyService mOverviewProxyService;
private boolean mWallpaperVisible;
private boolean mHasBackdrop;
// Colors to return when the wallpaper isn't visible
private final GradientColors mWpHiddenColors;
@Inject
- public SysuiColorExtractor(Context context, ConfigurationController configurationController,
- OverviewProxyService overviewProxyService) {
- this(context, new Tonal(context), configurationController, true, overviewProxyService);
+ public SysuiColorExtractor(Context context, ConfigurationController configurationController) {
+ this(context, new Tonal(context), configurationController, true);
}
@VisibleForTesting
public SysuiColorExtractor(Context context, ExtractionType type,
- ConfigurationController configurationController, boolean registerVisibility,
- OverviewProxyService overviewProxyService) {
+ ConfigurationController configurationController, boolean registerVisibility) {
super(context, type, false /* immediately */);
mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
mWpHiddenColors = new GradientColors();
- mOverviewProxyService = overviewProxyService;
configurationController.addCallback(this);
WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
@@ -133,35 +110,17 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
return;
}
+ super.onColorsChanged(colors, which);
+
if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
updateDefaultGradients(colors);
}
- super.onColorsChanged(colors, which);
}
@Override
public void onUiModeChanged() {
WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
updateDefaultGradients(systemColors);
- triggerColorsChanged(WallpaperManager.FLAG_SYSTEM);
- }
-
- @Override
- protected void triggerColorsChanged(int which) {
- super.triggerColorsChanged(which);
-
- if (mWpHiddenColors != null && (which & WallpaperManager.FLAG_SYSTEM) != 0) {
- @ColorInt int colorInt = mWpHiddenColors.getMainColor();
- @ScrimType int scrimType;
- if (colorInt == Tonal.MAIN_COLOR_LIGHT) {
- scrimType = SCRIM_TYPE_LIGHT;
- } else if (colorInt == Tonal.MAIN_COLOR_DARK) {
- scrimType = SCRIM_TYPE_DARK;
- } else {
- scrimType = SCRIM_TYPE_REGULAR;
- }
- mOverviewProxyService.onScrimColorsChanged(colorInt, scrimType);
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index e87ff520e028..dcabb780ee3a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -25,6 +25,7 @@ import android.app.ActivityManager;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.StatusBarManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
@@ -38,7 +39,9 @@ import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
+import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -75,6 +78,7 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.ScrimDrawable;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.util.EmergencyAffordanceManager;
@@ -1501,6 +1505,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final Context mContext;
private final MyAdapter mAdapter;
+ private final IStatusBarService mStatusBarService;
+ private final IBinder mToken = new Binder();
private MultiListLayout mGlobalActionsLayout;
private Drawable mBackgroundDrawable;
private final SysuiColorExtractor mColorExtractor;
@@ -1516,6 +1522,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mContext = context;
mAdapter = adapter;
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ mStatusBarService = Dependency.get(IStatusBarService.class);
// Window initialization
Window window = getWindow();
@@ -1542,9 +1549,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
private boolean shouldUsePanel() {
- return isPanelEnabled(mContext)
- && mPanelController != null
- && mPanelController.getPanelContent() != null;
+ return mPanelController != null && mPanelController.getPanelContent() != null;
}
private void initializePanel() {
@@ -1574,6 +1579,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mContext, true, RotationUtils.ROTATION_NONE);
}
+ // Disable rotation suggestions, if enabled
+ setRotationSuggestionsEnabled(false);
+
FrameLayout panelContainer = new FrameLayout(mContext);
FrameLayout.LayoutParams panelParams =
new FrameLayout.LayoutParams(
@@ -1732,11 +1740,24 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
+ private void setRotationSuggestionsEnabled(boolean enabled) {
+ try {
+ final int userId = Binder.getCallingUserHandle().getIdentifier();
+ final int what = enabled
+ ? StatusBarManager.DISABLE2_NONE
+ : StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
+ mStatusBarService.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private void resetOrientation() {
if (mResetOrientationData != null) {
RotationPolicy.setRotationLockAtAngle(mContext, mResetOrientationData.locked,
mResetOrientationData.rotation);
}
+ setRotationSuggestionsEnabled(true);
}
@Override
@@ -1792,15 +1813,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
- * Determines whether or not the Global Actions Panel should appear when the power button
- * is held.
- */
- private static boolean isPanelEnabled(Context context) {
- return FeatureFlagUtils.isEnabled(
- context, FeatureFlagUtils.GLOBAL_ACTIONS_PANEL_ENABLED);
- }
-
- /**
* Determines whether the Global Actions menu should use a separated view for emergency actions.
*/
private static boolean shouldUseSeparatedView() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index e1462d15c887..03165f47c472 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -42,6 +42,8 @@ public class GlobalActionsGridLayout extends GlobalActionsLayout {
listView.setReverseSublists(shouldReverseSublists());
listView.setReverseItems(shouldReverseListItems());
listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
+
+ fixNavBarClipping();
}
@Override
@@ -73,6 +75,19 @@ public class GlobalActionsGridLayout extends GlobalActionsLayout {
}
}
+ /**
+ * Allows the dialog to clip over the navbar, which prevents shadows and animations from being
+ * cut off.
+ */
+ private void fixNavBarClipping() {
+ ViewGroup parent = (ViewGroup) this.getParent();
+ ViewGroup parentParent = (ViewGroup) parent.getParent();
+ parent.setClipChildren(false);
+ parent.setClipToPadding(false);
+ parentParent.setClipChildren(false);
+ parentParent.setClipToPadding(false);
+ }
+
@Override
protected ListGridLayout getListView() {
return (ListGridLayout) super.getListView();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 4590470697ea..35b8d203cbe2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -30,6 +30,7 @@ import android.graphics.drawable.Icon;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Handler;
import android.os.Trace;
@@ -57,6 +58,7 @@ import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import java.util.Date;
+import java.util.HashSet;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -98,6 +100,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
private final Date mCurrentTime = new Date();
private final Handler mHandler;
private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
+ private final HashSet<Integer> mMediaInvisibleStates;
private ZenModeController mZenModeController;
private String mDatePattern;
private DateFormat mDateFormat;
@@ -113,6 +116,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
private StatusBarStateController mStatusBarStateController;
protected MediaMetadata mMediaMetaData;
protected boolean mDozing;
+ private boolean mMediaIsVisible;
/**
* Receiver responsible for time ticking and updating the date format.
@@ -169,6 +173,11 @@ public class KeyguardSliceProvider extends SliceProvider implements
mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
mDndUri = Uri.parse(KEYGUARD_DND_URI);
mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
+
+ mMediaInvisibleStates = new HashSet<>();
+ mMediaInvisibleStates.add(PlaybackState.STATE_NONE);
+ mMediaInvisibleStates.add(PlaybackState.STATE_STOPPED);
+ mMediaInvisibleStates.add(PlaybackState.STATE_PAUSED);
}
/**
@@ -209,31 +218,33 @@ public class KeyguardSliceProvider extends SliceProvider implements
}
protected boolean needsMediaLocked() {
- return mMediaMetaData != null && mDozing;
+ return mMediaMetaData != null && mMediaIsVisible && mDozing;
}
protected void addMediaLocked(ListBuilder listBuilder) {
- if (mMediaMetaData != null) {
- CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
- if (TextUtils.isEmpty(title)) {
- title = getContext().getResources().getString(R.string.music_controls_no_title);
- }
- listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title));
-
- CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
- if (!TextUtils.isEmpty(album)) {
- RowBuilder albumBuilder = new RowBuilder(mMediaUri);
- albumBuilder.setTitle(album);
-
- Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
- IconCompat mediaIconCompat = mediaIcon == null ? null
- : IconCompat.createFromIcon(getContext(), mediaIcon);
- if (mediaIconCompat != null) {
- albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE);
- }
+ if (mMediaMetaData == null) {
+ return;
+ }
- listBuilder.addRow(albumBuilder);
+ CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
+ if (TextUtils.isEmpty(title)) {
+ title = getContext().getResources().getString(R.string.music_controls_no_title);
+ }
+ listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title));
+
+ CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
+ if (!TextUtils.isEmpty(album)) {
+ RowBuilder albumBuilder = new RowBuilder(mMediaUri);
+ albumBuilder.setTitle(album);
+
+ Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
+ IconCompat mediaIconCompat = mediaIcon == null ? null
+ : IconCompat.createFromIcon(getContext(), mediaIcon);
+ if (mediaIconCompat != null) {
+ albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE);
}
+
+ listBuilder.addRow(albumBuilder);
}
}
@@ -411,9 +422,14 @@ public class KeyguardSliceProvider extends SliceProvider implements
* @param metadata New metadata.
*/
@Override
- public void onMetadataChanged(MediaMetadata metadata) {
+ public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) {
synchronized (this) {
+ boolean nextVisible = !mMediaInvisibleStates.contains(state);
+ if (nextVisible == mMediaIsVisible && metadata == mMediaMetaData) {
+ return;
+ }
mMediaMetaData = metadata;
+ mMediaIsVisible = nextVisible;
}
notifyChange();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index af2b7677dcad..a9fe54bae19d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -21,7 +21,6 @@ import static android.app.ActivityManager.TaskDescription;
import android.annotation.ColorInt;
import android.annotation.UserIdInt;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.PendingIntent;
@@ -32,9 +31,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Log;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,7 +53,9 @@ public class WorkLockActivity extends Activity {
*/
static final String EXTRA_TASK_DESCRIPTION =
"com.android.systemui.keyguard.extra.TASK_DESCRIPTION";
-
+
+ private static final int REQUEST_CODE_CONFIRM_CREDENTIALS = 1;
+
/**
* Cached keyguard manager instance populated by {@link #getKeyguardManager}.
* @see KeyguardManager
@@ -111,7 +110,6 @@ public class WorkLockActivity extends Activity {
@Override
public void onBackPressed() {
// Ignore back presses.
- return;
}
@Override
@@ -151,26 +149,26 @@ public class WorkLockActivity extends Activity {
PendingIntent.FLAG_ONE_SHOT |
PendingIntent.FLAG_IMMUTABLE, options.toBundle());
- credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
- try {
- ActivityManager.getService().startConfirmDeviceCredentialIntent(credential,
- getChallengeOptions().toBundle());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to start confirm credential intent", e);
+ if (target != null) {
+ credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
}
+
+ startActivityForResult(credential, REQUEST_CODE_CONFIRM_CREDENTIALS);
}
- private ActivityOptions getChallengeOptions() {
- // If we are taking up the whole screen, just use the default animation of clipping the
- // credentials activity into the entire foreground.
- if (!isInMultiWindowMode()) {
- return ActivityOptions.makeBasic();
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_CONFIRM_CREDENTIALS && resultCode != RESULT_OK) {
+ // The user dismissed the challenge, don't show it again.
+ goToHomeScreen();
}
+ }
- // Otherwise, animate the transition from this part of the screen to fullscreen
- // using our own decor as the starting position.
- final View view = getWindow().getDecorView();
- return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
+ private void goToHomeScreen() {
+ final Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(homeIntent);
}
private KeyguardManager getKeyguardManager() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index c5591cf9d947..78c7cd406ba1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -31,7 +31,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_B
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import android.annotation.ColorInt;
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -60,7 +59,6 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.colorextraction.SysuiColorExtractor.ScrimType;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -537,16 +535,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
dispatchNavButtonBounds();
}
- public void onScrimColorsChanged(@ColorInt int color, @ScrimType int type) {
- if (mOverviewProxy != null) {
- try {
- mOverviewProxy.onScrimColorsChanged(color, type);
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScrimColorsChanged()", e);
- }
- }
- }
-
private void dispatchNavButtonBounds() {
if (mOverviewProxy != null && mActiveNavBarRegion != null) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index a76c9dc9f40a..fd76a79eab2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -189,6 +189,7 @@ public class KeyguardIndicationController implements StateListener,
mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
showTransientIndication(R.string.keyguard_indication_trust_disabled);
+ mKeyguardUpdateMonitor.onLockIconPressed();
mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index b9e0c60fabeb..75ef18545fdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -150,8 +150,8 @@ public class NotificationMediaManager implements Dumpable {
if (state != null) {
if (!isPlaybackActive(state.getState())) {
clearCurrentMediaNotification();
- dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
+ dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
}
@@ -242,7 +242,8 @@ public class NotificationMediaManager implements Dumpable {
public void addCallback(MediaListener callback) {
mMediaListeners.add(callback);
- callback.onMetadataChanged(mMediaMetadata);
+ callback.onMetadataOrStateChanged(mMediaMetadata,
+ getMediaControllerPlaybackState(mMediaController));
}
public void removeCallback(MediaListener callback) {
@@ -357,9 +358,10 @@ public class NotificationMediaManager implements Dumpable {
if (mPresenter != null) {
mPresenter.updateMediaMetaData(changed, allowEnterAnimation);
}
+ @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController);
ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).onMetadataChanged(mMediaMetadata);
+ callbacks.get(i).onMetadataOrStateChanged(mMediaMetadata, state);
}
}
@@ -698,6 +700,12 @@ public class NotificationMediaManager implements Dumpable {
}
public interface MediaListener {
- void onMetadataChanged(MediaMetadata metadata);
+ /**
+ * Called whenever there's new metadata or playback state.
+ * @param metadata Current metadata.
+ * @param state Current playback state
+ * @see PlaybackState.State
+ */
+ void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 2cca701ef582..d202190724f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -71,6 +71,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
private int mIconAppearTopPadding;
private int mShelfAppearTranslation;
private float mDarkShelfPadding;
+ private float mDarkShelfIconSize;
private int mStatusBarHeight;
private int mStatusBarPaddingStart;
private AmbientState mAmbientState;
@@ -151,6 +152,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
mScrollFastThreshold = res.getDimensionPixelOffset(R.dimen.scroll_fast_threshold);
mShowNotificationShelf = res.getBoolean(R.bool.config_showNotificationShelf);
mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+ mDarkShelfIconSize = res.getDimensionPixelOffset(R.dimen.dark_shelf_icon_size);
mGapHeight = res.getDimensionPixelSize(R.dimen.qs_notification_padding);
if (!mShowNotificationShelf) {
@@ -705,12 +707,13 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
notificationIconPosition += iconTopPadding;
float shelfIconPosition = getTranslationY() + icon.getTop();
- shelfIconPosition += (icon.getHeight() - icon.getIconScale() * mIconSize) / 2.0f;
+ float iconSize = mDark ? mDarkShelfIconSize : mIconSize;
+ shelfIconPosition += (icon.getHeight() - icon.getIconScale() * iconSize) / 2.0f;
float iconYTranslation = NotificationUtils.interpolate(
notificationIconPosition - shelfIconPosition,
0,
transitionAmount);
- float shelfIconSize = mIconSize * icon.getIconScale();
+ float shelfIconSize = iconSize * icon.getIconScale();
float alpha = 1.0f;
boolean noIcon = !row.isShowingIcon();
if (noIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 033c4fbed6f7..6552fe671794 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -40,7 +40,6 @@ import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
@@ -72,12 +71,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
/**
* Status icons are currently drawn with the intention of being 17dp tall, but we
* want to scale them (in a way that doesn't require an asset dump) down 2dp. So
- * 17dp * (15 / 17) = 15dp, the new height.
+ * 17dp * (15 / 17) = 15dp, the new height. After the first call to {@link #reloadDimens} all
+ * values will be in px.
*/
- private static final float SYSTEM_ICON_DESIRED_HEIGHT = 15f;
- private static final float SYSTEM_ICON_INTRINSIC_HEIGHT = 17f;
- private static final float SYSTEM_ICON_SCALE =
- SYSTEM_ICON_DESIRED_HEIGHT / SYSTEM_ICON_INTRINSIC_HEIGHT;
+ private float mSystemIconDesiredHeight = 15f;
+ private float mSystemIconIntrinsicHeight = 17f;
+ private float mSystemIconDefaultScale = mSystemIconDesiredHeight / mSystemIconIntrinsicHeight;
private final int ANIMATION_DURATION_FAST = 100;
public static final int STATE_ICON = 0;
@@ -209,21 +208,20 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
// Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height
// for the icon, it uses the default SCALE (15f / 17f) which is the old behavior
private void updateIconScaleForSystemIcons() {
- float iconHeight = getIconHeightInDps();
+ float iconHeight = getIconHeight();
if (iconHeight != 0) {
- mIconScale = SYSTEM_ICON_DESIRED_HEIGHT / iconHeight;
+ mIconScale = mSystemIconDesiredHeight / iconHeight;
} else {
- mIconScale = SYSTEM_ICON_SCALE;
+ mIconScale = mSystemIconDefaultScale;
}
}
- private float getIconHeightInDps() {
+ private float getIconHeight() {
Drawable d = getDrawable();
if (d != null) {
- return ((float) getDrawable().getIntrinsicHeight() * DisplayMetrics.DENSITY_DEFAULT)
- / mDensity;
+ return (float) getDrawable().getIntrinsicHeight();
} else {
- return SYSTEM_ICON_INTRINSIC_HEIGHT;
+ return mSystemIconIntrinsicHeight;
}
}
@@ -265,6 +263,11 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
if (applyRadius) {
mDotRadius = mStaticDotRadius;
}
+ mSystemIconDesiredHeight = res.getDimension(
+ com.android.internal.R.dimen.status_bar_system_icon_size);
+ mSystemIconIntrinsicHeight = res.getDimension(
+ com.android.internal.R.dimen.status_bar_system_icon_intrinsic_size);
+ mSystemIconDefaultScale = mSystemIconDesiredHeight / mSystemIconIntrinsicHeight;
}
public void setNotification(StatusBarNotification notification) {
@@ -272,6 +275,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
if (notification != null) {
setContentDescription(notification.getNotification());
}
+ maybeUpdateIconScaleDimens();
}
public StatusBarIconView(Context context, AttributeSet attrs) {
@@ -280,7 +284,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
mBlocked = false;
mAlwaysScaleIcon = true;
reloadDimens();
- updateIconScaleForNotifications();
+ maybeUpdateIconScaleDimens();
mDensity = context.getResources().getDisplayMetrics().densityDpi;
}
@@ -854,7 +858,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
public void setDark(boolean dark, boolean fade, long delay) {
mDozer.setIntensityDark(f -> {
mDarkAmount = f;
- updateIconScaleForNotifications();
+ maybeUpdateIconScaleDimens();
updateDecorColor();
updateIconColor();
updateAllowAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index a065f67c6137..b89b5cb34d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -169,7 +169,7 @@ class ChannelEditorDialogController @Inject constructor(
}
}
- @SuppressWarnings("unchecked")
+ @Suppress("unchecked_cast")
private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> {
return try {
noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 3450a8a8ebfb..a3e3426f206c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -29,6 +29,7 @@ import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -45,6 +46,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
import javax.inject.Inject;
@@ -70,6 +72,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private final AccessibilityController mAccessibilityController;
private final DockManager mDockManager;
private final Handler mMainHandler;
+ private final KeyguardMonitor mKeyguardMonitor;
private int mLastState = 0;
private boolean mTransientBiometricsError;
@@ -90,7 +93,16 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private int mIconRes;
private boolean mWasPulsingOnThisFrame;
private boolean mWakeAndUnlockRunning;
+ private boolean mKeyguardShowing;
+ private final KeyguardMonitor.Callback mKeyguardMonitorCallback =
+ new KeyguardMonitor.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ mKeyguardShowing = mKeyguardMonitor.isShowing();
+ update(false /* force */);
+ }
+ };
private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
private final DockManager.DockEventListener mDockEventListener =
new DockManager.DockEventListener() {
@@ -149,6 +161,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
AccessibilityController accessibilityController,
+ KeyguardMonitor keyguardMonitor,
@Nullable DockManager dockManager,
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
super(context, attrs);
@@ -158,6 +171,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mAccessibilityController = accessibilityController;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
+ mKeyguardMonitor = keyguardMonitor;
mDockManager = dockManager;
mMainHandler = mainHandler;
}
@@ -167,6 +181,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
super.onAttachedToWindow();
mStatusBarStateController.addCallback(this);
mConfigurationController.addCallback(this);
+ mKeyguardMonitor.addCallback(mKeyguardMonitorCallback);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mUnlockMethodCache.addListener(this);
mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
@@ -182,6 +197,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mStatusBarStateController.removeCallback(this);
mConfigurationController.removeCallback(this);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+ mKeyguardMonitor.removeCallback(mKeyguardMonitorCallback);
mUnlockMethodCache.removeListener(this);
if (mDockManager != null) {
mDockManager.removeListener(mDockEventListener);
@@ -256,9 +272,12 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
if (getDrawable() == animation && state == getState()
&& doesAnimationLoop(iconAnimRes)) {
animation.start();
+ } else {
+ Trace.endAsyncSection("LockIcon#Animation", state);
}
}
});
+ Trace.beginAsyncSection("LockIcon#Animation", state);
animation.start();
}
}
@@ -375,7 +394,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
if (mTransientBiometricsError) {
return STATE_BIOMETRICS_ERROR;
- } else if (mUnlockMethodCache.canSkipBouncer() && !mSimLocked) {
+ } else if ((mUnlockMethodCache.canSkipBouncer() || !mKeyguardShowing) && !mSimLocked) {
return STATE_LOCK_OPEN;
} else if (updateMonitor.isFaceDetectionRunning()) {
return STATE_SCANNING_FACE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index 4603ba6af89f..4f223c385eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -25,6 +25,7 @@ import android.graphics.Path;
import android.graphics.Rect;
import android.os.SystemClock;
import android.os.VibrationEffect;
+import android.util.DisplayMetrics;
import android.util.MathUtils;
import android.view.ContextThemeWrapper;
import android.view.MotionEvent;
@@ -47,14 +48,14 @@ import androidx.dynamicanimation.animation.SpringForce;
public class NavigationBarEdgePanel extends View {
- private static final long COLOR_ANIMATION_DURATION_MS = 100;
- private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 140;
+ private static final long COLOR_ANIMATION_DURATION_MS = 120;
+ private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80;
private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100;
/**
- * The minimum time required since the first vibration effect to receive a second one
+ * The time required since the first vibration effect to automatically trigger a click
*/
- private static final int MIN_TIME_BETWEEN_EFFECTS_MS = 120;
+ private static final int GESTURE_DURATION_FOR_CLICK_MS = 400;
/**
* The size of the protection of the arrow in px. Only used if this is not background protected
@@ -79,7 +80,7 @@ public class NavigationBarEdgePanel extends View {
/**
* The angle that is added per 1000 px speed to the angle of the leg
*/
- private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 8;
+ private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 4;
/**
* The maximum angle offset allowed due to speed
@@ -92,15 +93,15 @@ public class NavigationBarEdgePanel extends View {
private static final float ARROW_THICKNESS_DP = 2.5f;
/**
- * The amount of rubber banding we do for the horizontal translation beyond the base translation
+ * The amount of rubber banding we do for the vertical translation
*/
- private static final int RUBBER_BAND_AMOUNT = 10;
+ private static final int RUBBER_BAND_AMOUNT = 15;
/**
* The interpolator used to rubberband
*/
private static final Interpolator RUBBER_BAND_INTERPOLATOR
- = new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT, 1.0f, 1.0f, 1.0f);
+ = new PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f);
/**
* The amount of rubber banding we do for the translation before base translation
@@ -189,6 +190,7 @@ public class NavigationBarEdgePanel extends View {
private int mCurrentArrowColor;
private float mDisappearAmount;
private long mVibrationTime;
+ private int mScreenSize;
private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener
= new DynamicAnimation.OnAnimationEndListener() {
@@ -281,9 +283,8 @@ public class NavigationBarEdgePanel extends View {
mAngleAnimation =
new SpringAnimation(this, CURRENT_ANGLE);
mAngleAppearForce = new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setDampingRatio(0.4f)
- .setFinalPosition(ARROW_ANGLE_WHEN_EXTENDED_DEGREES);
+ .setStiffness(500)
+ .setDampingRatio(0.5f);
mAngleDisappearForce = new SpringForce()
.setStiffness(SpringForce.STIFFNESS_MEDIUM)
.setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
@@ -447,13 +448,14 @@ public class NavigationBarEdgePanel extends View {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- // TODO: read the gesture length from the nav controller.
mMaxTranslation = getWidth() - mArrowPaddingEnd;
}
private void loadDimens() {
mArrowPaddingEnd = getContext().getResources().getDimensionPixelSize(
R.dimen.navigation_edge_panel_padding);
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ mScreenSize = Math.min(metrics.widthPixels, metrics.heightPixels);
}
private void updateArrowDirection() {
@@ -510,7 +512,7 @@ public class NavigationBarEdgePanel extends View {
if (!mArrowsPointLeft) {
x = -x;
}
- float extent = 1.0f - mDisappearAmount;
+ float extent = MathUtils.lerp(1.0f, 0.75f, mDisappearAmount);
x = x * extent;
y = y * extent;
mArrowPath.reset();
@@ -529,27 +531,29 @@ public class NavigationBarEdgePanel extends View {
}
private void triggerBack() {
- if (SystemClock.uptimeMillis() - mVibrationTime >= MIN_TIME_BETWEEN_EFFECTS_MS) {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK);
- }
mVelocityTracker.computeCurrentVelocity(1000);
// Only do the extra translation if we're not already flinging
- boolean doExtraTranslation = Math.abs(mVelocityTracker.getXVelocity()) < 1000;
- if (doExtraTranslation) {
- setDesiredTranslation(mDesiredTranslation + dp(16), true /* animate */);
+ boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500;
+ if (isSlow
+ || SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK);
}
// Let's also snap the angle a bit
- if (mAngleOffset < -4) {
- mAngleOffset = Math.max(-16, mAngleOffset - 16);
+ if (mAngleOffset > -4) {
+ mAngleOffset = Math.max(-8, mAngleOffset - 8);
updateAngle(true /* animated */);
}
// Finally, after the translation, animate back and disappear the arrow
Runnable translationEnd = () -> {
- setTriggerBack(false /* false */, true /* animate */);
+ // let's snap it back
+ mAngleOffset = Math.max(0, mAngleOffset + 8);
+ updateAngle(true /* animated */);
+
mTranslationAnimation.setSpring(mTriggerBackSpring);
- setDesiredTranslation(0, true /* animated */);
+ // Translate the arrow back a bit to make for a nice transition
+ setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */);
animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS)
.withEndAction(() -> setVisibility(GONE));
mArrowDisappearAnimation.start();
@@ -584,6 +588,7 @@ public class NavigationBarEdgePanel extends View {
setTriggerBack(false /* triggerBack */, false /* animated */);
setDesiredTranslation(0, false /* animated */);
setCurrentTranslation(0);
+ updateAngle(false /* animate */);
mPreviousTouchTranslation = 0;
mTotalTouchDelta = 0;
mVibrationTime = 0;
@@ -621,7 +626,7 @@ public class NavigationBarEdgePanel extends View {
// Let's make sure we only go to the baseextend and apply rubberbanding afterwards
if (touchTranslation > mBaseTranslation) {
float diff = touchTranslation - mBaseTranslation;
- float progress = MathUtils.saturate(diff / (mBaseTranslation * RUBBER_BAND_AMOUNT));
+ float progress = MathUtils.saturate(diff / (mScreenSize - mBaseTranslation));
progress = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
* (mMaxTranslation - mBaseTranslation);
touchTranslation = mBaseTranslation + progress;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 9e0aff070364..3b3336b3f14d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -107,6 +107,8 @@ public final class NavigationBarTransitions extends BarTransitions implements
@Override
public void setAutoDim(boolean autoDim) {
+ // Ensure we aren't in gestural nav if we are triggering auto dim
+ if (autoDim && NavBarTintController.isEnabled(mView.getContext(), mNavBarMode)) return;
if (mAutoDim == autoDim) return;
mAutoDim = autoDim;
applyLightsOut(true, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d85b8acfb462..062fb6dcce92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -967,12 +967,6 @@ public class NavigationBarView extends FrameLayout implements
// If car mode or density changes, we need to reset the icons.
updateNavButtonIcons();
}
-
- if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
- mTintController.start();
- } else {
- mTintController.stop();
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
index d59319e110de..2b0bb21c6560 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
@@ -27,6 +27,7 @@ import android.provider.Settings;
import android.view.CompositionSamplingListener;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import com.android.systemui.R;
@@ -153,8 +154,12 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
boolean isSamplingEnabled = mSamplingEnabled && !mSamplingRequestBounds.isEmpty()
&& (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart);
if (isSamplingEnabled) {
- SurfaceControl stopLayerControl = mSampledView.getViewRootImpl().getSurfaceControl();
- if (!stopLayerControl.isValid()) {
+ ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
+ SurfaceControl stopLayerControl = null;
+ if (viewRootImpl != null) {
+ stopLayerControl = viewRootImpl.getSurfaceControl();
+ }
+ if (stopLayerControl == null || !stopLayerControl.isValid()) {
if (!mWaitingOnDraw) {
mWaitingOnDraw = true;
// The view might be attached but we haven't drawn yet, so wait until the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 0b83b697e53d..712e96255b02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -262,7 +262,9 @@ public class StatusBarWindowView extends FrameLayout {
* Propagate {@link StatusBar} pulsing state.
*/
public void setPulsing(boolean pulsing) {
- mLockIcon.setPulsing(pulsing);
+ if (mLockIcon != null) {
+ mLockIcon.setPulsing(pulsing);
+ }
}
/**
@@ -270,14 +272,18 @@ public class StatusBarWindowView extends FrameLayout {
* @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
*/
public void onBiometricAuthModeChanged(boolean wakeAndUnlock) {
- mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock);
+ if (mLockIcon != null) {
+ mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock);
+ }
}
/**
* Called after finished unlocking and the status bar window is already collapsed.
*/
public void onKeyguardFadedAway() {
- mLockIcon.onKeyguardFadedAway();
+ if (mLockIcon != null) {
+ mLockIcon.onKeyguardFadedAway();
+ }
}
public void setStatusBarView(PhoneStatusBarView statusBarView) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6bed43eb37b6..6208ab82dda6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -143,10 +143,21 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testTelephonyCapable_SimState_Absent() {
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
- , IccCardConstants.INTENT_VALUE_ICC_ABSENT);
- mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
- , putPhoneInfo(intent,null, false));
+ intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
+ putPhoneInfo(intent, null, false));
+ mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
+ }
+
+ @Test
+ public void testTelephonyCapable_SimState_CardIOError() {
+ Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+ IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
+ putPhoneInfo(intent, null, false));
mTestableLooper.processAllMessages();
assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 35a15167d207..b3f6f4ecdf0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -47,6 +47,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
+import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -69,6 +70,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModeController;
import org.junit.Before;
import org.junit.Test;
@@ -99,6 +101,10 @@ public class BubbleControllerTest extends SysuiTestCase {
private DozeParameters mDozeParameters;
@Mock
private ConfigurationController mConfigurationController;
+ @Mock
+ private ZenModeController mZenModeController;
+ @Mock
+ private ZenModeConfig mZenModeConfig;
private FrameLayout mStatusBarView;
@Captor
@@ -162,6 +168,9 @@ public class BubbleControllerTest extends SysuiTestCase {
when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
+ mZenModeConfig.suppressedVisualEffects = 0;
+ when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
+
TestableNotificationInterruptionStateProvider interruptionStateProvider =
new TestableNotificationInterruptionStateProvider(mContext);
interruptionStateProvider.setUpWithPresenter(
@@ -170,7 +179,8 @@ public class BubbleControllerTest extends SysuiTestCase {
mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
mBubbleData = new BubbleData(mContext);
mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
- mBubbleData, mConfigurationController, interruptionStateProvider);
+ mBubbleData, mConfigurationController, interruptionStateProvider,
+ mZenModeController);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -628,9 +638,10 @@ public class BubbleControllerTest extends SysuiTestCase {
TestableBubbleController(Context context,
StatusBarWindowController statusBarWindowController, BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider) {
+ NotificationInterruptionStateProvider interruptionStateProvider,
+ ZenModeController zenModeController) {
super(context, statusBarWindowController, data, Runnable::run,
- configurationController, interruptionStateProvider);
+ configurationController, interruptionStateProvider, zenModeController);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 33b2e6e59470..364a0f7ed2e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -103,13 +103,13 @@ public class BubbleDataTest extends SysuiTestCase {
mEntryB3 = createBubbleEntry(1, "b3", "package.b");
mEntryC1 = createBubbleEntry(1, "c1", "package.c");
- mBubbleA1 = new Bubble(mEntryA1);
- mBubbleA2 = new Bubble(mEntryA2);
- mBubbleA3 = new Bubble(mEntryA3);
- mBubbleB1 = new Bubble(mEntryB1);
- mBubbleB2 = new Bubble(mEntryB2);
- mBubbleB3 = new Bubble(mEntryB3);
- mBubbleC1 = new Bubble(mEntryC1);
+ mBubbleA1 = new Bubble(mContext, mEntryA1);
+ mBubbleA2 = new Bubble(mContext, mEntryA2);
+ mBubbleA3 = new Bubble(mContext, mEntryA3);
+ mBubbleB1 = new Bubble(mContext, mEntryB1);
+ mBubbleB2 = new Bubble(mContext, mEntryB2);
+ mBubbleB3 = new Bubble(mContext, mEntryB3);
+ mBubbleC1 = new Bubble(mContext, mEntryC1);
mBubbleData = new BubbleData(getContext());
@@ -803,4 +803,4 @@ public class BubbleDataTest extends SysuiTestCase {
private static <T> List<T> listOf(T... a) {
return ImmutableList.copyOf(a);
}
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
new file mode 100644
index 000000000000..173237f7b311
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 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.bubbles;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleFlyoutViewTest extends SysuiTestCase {
+ private BubbleFlyoutView mFlyout;
+ private TextView mFlyoutText;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mFlyout = new BubbleFlyoutView(getContext());
+
+ mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
+ }
+
+ @Test
+ public void testShowFlyout_isVisible() {
+ mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null);
+ assertEquals("Hello", mFlyoutText.getText());
+ assertEquals(View.VISIBLE, mFlyout.getVisibility());
+ assertEquals(1f, mFlyoutText.getAlpha(), .01f);
+ }
+
+ @Test
+ public void testFlyoutHide_runsCallback() {
+ Runnable after = Mockito.mock(Runnable.class);
+ mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, after);
+ mFlyout.hideFlyout();
+
+ verify(after).run();
+ }
+
+ @Test
+ public void testSetCollapsePercent() {
+ mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null);
+
+ float initialTranslationZ = mFlyout.getTranslationZ();
+
+ mFlyout.setCollapsePercent(1f);
+ assertEquals(0f, mFlyoutText.getAlpha(), 0.01f);
+ assertNotSame(0f, mFlyoutText.getTranslationX()); // Should have moved to collapse.
+ assertTrue(mFlyout.getTranslationZ() < initialTranslationZ); // Should be descending.
+
+ mFlyout.setCollapsePercent(0f);
+ assertEquals(1f, mFlyoutText.getAlpha(), 0.01f);
+ assertEquals(0f, mFlyoutText.getTranslationX());
+ assertEquals(initialTranslationZ, mFlyout.getTranslationZ());
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
deleted file mode 100644
index bafae6ce737a..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 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.bubbles;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.widget.TextView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class BubbleStackViewTest extends SysuiTestCase {
- private BubbleStackView mStackView;
- @Mock private Bubble mBubble;
- @Mock private NotificationEntry mNotifEntry;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mStackView = new BubbleStackView(mContext, new BubbleData(getContext()), null);
- mBubble.entry = mNotifEntry;
- }
-
- @Test
- public void testAnimateInFlyoutForBubble() {
- when(mNotifEntry.getUpdateMessage(any())).thenReturn("Test Flyout Message.");
- mStackView.animateInFlyoutForBubble(mBubble);
-
- assertEquals("Test Flyout Message.",
- ((TextView) mStackView.findViewById(R.id.bubble_flyout_text)).getText());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index a398fba008bb..c6acef5d4907 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -138,6 +138,16 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase {
}
@Override
+ public boolean post(Runnable action) {
+ return mMainThreadHandler.post(action);
+ }
+
+ @Override
+ public boolean postDelayed(Runnable action, long delayMillis) {
+ return mMainThreadHandler.postDelayed(action, delayMillis);
+ }
+
+ @Override
public void setController(PhysicsAnimationController controller) {
runOnMainThreadAndBlock(
() -> super.setController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index b83276bc93da..9d5c1a4ce79e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -38,6 +38,9 @@ import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.Spy;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
@@ -46,12 +49,13 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
private TestableStackController mStackController = new TestableStackController();
private int mStackOffset;
+ private Runnable mCheckStartPosSet;
@Before
public void setUp() throws Exception {
super.setUp();
- addOneMoreThanRenderLimitBubbles();
mLayout.setController(mStackController);
+ addOneMoreThanRenderLimitBubbles();
mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
}
@@ -166,6 +170,8 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
0,
new FrameLayout.LayoutParams(50, 50));
+ waitForStartPosToBeSet();
+ waitForLayoutMessageQueue();
waitForPropertyAnimations(
DynamicAnimation.TRANSLATION_X,
DynamicAnimation.TRANSLATION_Y,
@@ -293,6 +299,28 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
}
}
+ /** Waits up to 2 seconds for the initial stack position to be initialized. */
+ private void waitForStartPosToBeSet() throws InterruptedException {
+ final CountDownLatch animLatch = new CountDownLatch(1);
+
+ mCheckStartPosSet = () -> {
+ if (mStackController.getStackPosition().x >= 0) {
+ animLatch.countDown();
+ } else {
+ mMainThreadHandler.post(mCheckStartPosSet);
+ }
+ };
+
+ mMainThreadHandler.post(mCheckStartPosSet);
+
+ try {
+ animLatch.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ mMainThreadHandler.removeCallbacks(mCheckStartPosSet);
+ throw e;
+ }
+ }
+
/**
* Testable version of the stack controller that dispatches its animations on the main thread.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 3d3c29564910..67df60a3dcfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -34,14 +34,10 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.types.Tonal;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
/**
* Tests color extraction generation.
@@ -57,13 +53,6 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
ColorExtractor.TYPE_NORMAL,
ColorExtractor.TYPE_DARK,
ColorExtractor.TYPE_EXTRA_DARK};
- @Mock
- private OverviewProxyService mOverviewProxyService;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
@Test
public void getColors_usesGreyIfWallpaperNotVisible() {
@@ -129,8 +118,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
Tonal tonal = mock(Tonal.class);
ConfigurationController configurationController = mock(ConfigurationController.class);
SysuiColorExtractor sysuiColorExtractor = new SysuiColorExtractor(getContext(),
- tonal, configurationController, false /* registerVisibility */,
- mOverviewProxyService);
+ tonal, configurationController, false /* registerVisibility */);
verify(configurationController).addCallback(eq(sysuiColorExtractor));
reset(tonal);
@@ -145,7 +133,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
outGradientColorsNormal.set(colors);
outGradientColorsDark.set(colors);
outGradientColorsExtraDark.set(colors);
- }, mock(ConfigurationController.class), false, mOverviewProxyService);
+ }, mock(ConfigurationController.class), false);
}
private void simulateEvent(SysuiColorExtractor extractor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index c534de7e24a3..355e26071b2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
import android.net.Uri;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -105,7 +106,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
MediaMetadata metadata = mock(MediaMetadata.class);
when(metadata.getText(any())).thenReturn("metadata");
mProvider.onDozingChanged(true);
- mProvider.onMetadataChanged(metadata);
+ mProvider.onMetadataOrStateChanged(metadata, PlaybackState.STATE_PLAYING);
mProvider.onBindSlice(mProvider.getUri());
verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST));
@@ -170,7 +171,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
public void onMetadataChanged_updatesSlice() {
mProvider.onDozingChanged(true);
reset(mContentResolver);
- mProvider.onMetadataChanged(mock(MediaMetadata.class));
+ mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
// Hides after waking up
@@ -181,7 +182,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
@Test
public void onDozingChanged_updatesSliceIfMedia() {
- mProvider.onMetadataChanged(mock(MediaMetadata.class));
+ mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
reset(mContentResolver);
// Show media when dozing
mProvider.onDozingChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 6d9a77c58227..daee55bd3d61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -261,6 +261,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
longClickCaptor.getValue().onLongClick(mLockIcon);
verify(mLockPatternUtils).requireCredentialEntry(anyInt());
+ verify(mKeyguardUpdateMonitor).onLockIconPressed();
}
@Test
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5fd14a37f23c..a5a4fec59350 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7294,6 +7294,7 @@ message MetricsEvent {
ACTION_VERIFY_SLICE_OTHER_EXCEPTION = 1727;
// ---- Skipping ahead to avoid conflicts between master and release branches.
+
// OPEN: Settings > System > Gestures > Global Actions Panel
// CATEGORY: SETTINGS
// OS: Q
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
new file mode 100644
index 000000000000..dd1b84b16f68
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
+import android.service.contentcapture.FlushMetrics;
+import android.util.StatsLog;
+
+import java.util.List;
+
+/** @hide */
+public final class ContentCaptureMetricsLogger {
+ /**
+ * Class only contains static utility functions, and should not be instantiated
+ */
+ private ContentCaptureMetricsLogger() {
+ }
+
+ /** @hide */
+ public static void writeServiceEvent(int eventType, @NonNull String serviceName,
+ @Nullable String targetPackage) {
+ StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS, eventType, serviceName,
+ targetPackage);
+ }
+
+ /** @hide */
+ public static void writeServiceEvent(int eventType, @NonNull ComponentName service,
+ @Nullable ComponentName target) {
+ writeServiceEvent(eventType, ComponentName.flattenToShortString(service),
+ ComponentName.flattenToShortString(target));
+ }
+
+ /** @hide */
+ public static void writeServiceEvent(int eventType, @NonNull ComponentName service,
+ @Nullable String targetPackage) {
+ writeServiceEvent(eventType, ComponentName.flattenToShortString(service), targetPackage);
+ }
+
+ /** @hide */
+ public static void writeServiceEvent(int eventType, @NonNull ComponentName service) {
+ writeServiceEvent(eventType, ComponentName.flattenToShortString(service), null);
+ }
+
+ /** @hide */
+ public static void writeSetWhitelistEvent(@Nullable ComponentName service,
+ @Nullable List<String> packages, @Nullable List<ComponentName> activities) {
+ final String serviceName = ComponentName.flattenToShortString(service);
+ StringBuilder stringBuilder = new StringBuilder();
+ if (packages != null && packages.size() > 0) {
+ final int size = packages.size();
+ stringBuilder.append(packages.get(0));
+ for (int i = 1; i < size; i++) {
+ stringBuilder.append(" ");
+ stringBuilder.append(packages.get(i));
+ }
+ }
+ if (activities != null && activities.size() > 0) {
+ stringBuilder.append(" ");
+ stringBuilder.append(activities.get(0).flattenToShortString());
+ final int size = activities.size();
+ for (int i = 1; i < size; i++) {
+ stringBuilder.append(" ");
+ stringBuilder.append(activities.get(i).flattenToShortString());
+ }
+ }
+ StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS,
+ StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_WHITELIST,
+ serviceName, stringBuilder.toString());
+ }
+
+ /** @hide */
+ public static void writeSessionEvent(int sessionId, int event, int flags,
+ @NonNull ComponentName service, @Nullable ComponentName app, boolean isChildSession) {
+ StatsLog.write(StatsLog.CONTENT_CAPTURE_SESSION_EVENTS, sessionId, event, flags,
+ ComponentName.flattenToShortString(service),
+ ComponentName.flattenToShortString(app), isChildSession);
+ }
+
+ /** @hide */
+ public static void writeSessionFlush(int sessionId, @NonNull ComponentName service,
+ @Nullable ComponentName app, @NonNull FlushMetrics fm,
+ @NonNull ContentCaptureOptions options, int flushReason) {
+ StatsLog.write(StatsLog.CONTENT_CAPTURE_FLUSHED, sessionId,
+ ComponentName.flattenToShortString(service),
+ ComponentName.flattenToShortString(app), fm.sessionStarted, fm.sessionFinished,
+ fm.viewAppearedCount, fm.viewDisappearedCount, fm.viewTextChangedCount,
+ options.maxBufferSize, options.idleFlushingFrequencyMs,
+ options.textChangeFlushingFrequencyMs, flushReason);
+ }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 67c3d01cb86b..a186d4e7f467 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -24,6 +24,9 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_E
import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED;
import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSetWhitelistEvent;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -35,6 +38,7 @@ import android.app.ActivityManagerInternal;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -48,6 +52,7 @@ import android.service.contentcapture.ActivityEvent;
import android.service.contentcapture.ActivityEvent.ActivityEventType;
import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.ContentCaptureServiceInfo;
+import android.service.contentcapture.FlushMetrics;
import android.service.contentcapture.IContentCaptureServiceCallback;
import android.service.contentcapture.SnapshotData;
import android.util.ArrayMap;
@@ -55,6 +60,7 @@ import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.DataRemovalRequest;
@@ -231,7 +237,6 @@ final class ContentCapturePerUserService
resurrectSessionsLocked();
}
- // TODO(b/119613670): log metrics
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
@NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid,
@@ -263,9 +268,14 @@ final class ContentCapturePerUserService
if (!enabled) {
// TODO: it would be better to split in differet reasons, like
- // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
+ // STATE_DISABLED_NO and STATE_DISABLED_BY_DEVICE_POLICY
setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
/* binder= */ null);
+ // Log metrics.
+ writeSessionEvent(sessionId,
+ StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
+ componentName, /* isChildSession= */ false);
return;
}
if (serviceComponentName == null) {
@@ -285,6 +295,11 @@ final class ContentCapturePerUserService
}
setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED,
/* binder= */ null);
+ // Log metrics.
+ writeSessionEvent(sessionId,
+ StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ STATE_DISABLED | STATE_NOT_WHITELISTED, serviceComponentName,
+ componentName, /* isChildSession= */ false);
return;
}
@@ -294,6 +309,11 @@ final class ContentCapturePerUserService
+ ": ignoring because it already exists for " + existingSession.mActivityToken);
setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
/* binder=*/ null);
+ // Log metrics.
+ writeSessionEvent(sessionId,
+ StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ STATE_DISABLED | STATE_DUPLICATED_ID,
+ serviceComponentName, componentName, /* isChildSession= */ false);
return;
}
@@ -302,11 +322,15 @@ final class ContentCapturePerUserService
}
if (mRemoteService == null) {
- // TODO(b/119613670): log metrics
Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+ ": ignoring because service is not set");
setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
/* binder= */ null);
+ // Log metrics.
+ writeSessionEvent(sessionId,
+ StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
+ componentName, /* isChildSession= */ false);
return;
}
@@ -324,7 +348,6 @@ final class ContentCapturePerUserService
newSession.notifySessionStartedLocked(clientReceiver);
}
- // TODO(b/119613670): log metrics
@GuardedBy("mLock")
public void finishSessionLocked(int sessionId) {
if (!isEnabledLocked()) {
@@ -553,6 +576,7 @@ final class ContentCapturePerUserService
+ " for user " + mUserId);
}
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
+ writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
// Must disable session that are not the whitelist anymore...
final int numSessions = mSessions.size();
@@ -602,7 +626,6 @@ final class ContentCapturePerUserService
mConditionsByPkg.put(packageName, new ArraySet<>(conditions));
}
}
- // TODO(b/119613670): log metrics
}
@Override
@@ -616,6 +639,15 @@ final class ContentCapturePerUserService
} finally {
Binder.restoreCallingIdentity(token);
}
+ writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED,
+ getServiceComponentName());
+ }
+
+ @Override
+ public void writeSessionFlush(int sessionId, ComponentName app, FlushMetrics flushMetrics,
+ ContentCaptureOptions options, int flushReason) {
+ ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app,
+ flushMetrics, options, flushReason);
}
}
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 2171033c5a28..18daf325db8c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -18,6 +18,9 @@ package com.android.server.contentcapture;
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -28,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.IContentCaptureServiceCallback;
import android.service.contentcapture.SnapshotData;
import android.util.Slog;
+import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.DataRemovalRequest;
@@ -77,6 +81,8 @@ final class RemoteContentCaptureService
if (connected) {
try {
mService.onConnected(mServerCallback, sVerbose, sDebug);
+ writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED,
+ mComponentName);
} finally {
// Update the system-service state, in case the service reconnected after
// dying
@@ -84,6 +90,8 @@ final class RemoteContentCaptureService
}
} else {
mService.onDisconnected();
+ writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED,
+ mComponentName);
}
} catch (Exception e) {
Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e);
@@ -102,6 +110,10 @@ final class RemoteContentCaptureService
@NonNull IResultReceiver clientReceiver, int initialState) {
scheduleAsyncRequest(
(s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver, initialState));
+ // Metrics logging.
+ writeSessionEvent(sessionId,
+ StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_STARTED, initialState,
+ getComponentName(), context.getActivityComponent(), /* is_child_session= */ false);
}
/**
@@ -110,6 +122,11 @@ final class RemoteContentCaptureService
*/
public void onSessionFinished(int sessionId) {
scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
+ // Metrics logging.
+ writeSessionEvent(sessionId,
+ StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_FINISHED,
+ /* flags= */ 0, getComponentName(), /* app= */ null,
+ /* is_child_session= */ false);
}
/**
@@ -124,6 +141,8 @@ final class RemoteContentCaptureService
*/
public void onDataRemovalRequest(@NonNull DataRemovalRequest request) {
scheduleAsyncRequest((s) -> s.onDataRemovalRequest(request));
+ writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_USER_DATA_REMOVED,
+ mComponentName);
}
/**
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 72899f62a55d..cb61259ec53d 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1138,8 +1138,8 @@ class AlarmManagerService extends SystemService {
? clampPositive(whenElapsed + a.windowLength)
: maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
}
- a.whenElapsed = whenElapsed;
- a.maxWhenElapsed = maxElapsed;
+ a.expectedWhenElapsed = a.whenElapsed = whenElapsed;
+ a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed;
setImplLocked(a, true, doValidate);
}
@@ -1243,7 +1243,7 @@ class AlarmManagerService extends SystemService {
alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
- final long nextElapsed = alarm.whenElapsed + delta;
+ final long nextElapsed = alarm.expectedWhenElapsed + delta;
setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
@@ -3596,10 +3596,9 @@ class AlarmManagerService extends SystemService {
// this adjustment will be zero if we're late by
// less than one full repeat interval
alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
-
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
- final long nextElapsed = alarm.whenElapsed + delta;
+ final long nextElapsed = alarm.expectedWhenElapsed + delta;
setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index facc2994507d..f12bfc33ea1b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3497,7 +3497,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
@Override
public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
- mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ mContext.enforceCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ "ConnectivityService");
final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
appIntent.putExtras(appExtras);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c6461491b8cd..fc355b7cce2f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -286,6 +286,7 @@ class StorageManagerService extends IStorageManager.Stub
private static final String ATTR_NICKNAME = "nickname";
private static final String ATTR_USER_FLAGS = "userFlags";
private static final String ATTR_CREATED_MILLIS = "createdMillis";
+ private static final String ATTR_LAST_SEEN_MILLIS = "lastSeenMillis";
private static final String ATTR_LAST_TRIM_MILLIS = "lastTrimMillis";
private static final String ATTR_LAST_BENCH_MILLIS = "lastBenchMillis";
@@ -1313,7 +1314,7 @@ class StorageManagerService extends IStorageManager.Stub
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
- if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
+ if (!TextUtils.isEmpty(vol.fsUuid)) {
VolumeRecord rec = mRecords.get(vol.fsUuid);
if (rec == null) {
rec = new VolumeRecord(vol.type, vol.fsUuid);
@@ -1323,14 +1324,15 @@ class StorageManagerService extends IStorageManager.Stub
rec.nickname = vol.disk.getDescription();
}
mRecords.put(rec.fsUuid, rec);
- writeSettingsLocked();
} else {
// Handle upgrade case where we didn't store partition GUID
if (TextUtils.isEmpty(rec.partGuid)) {
rec.partGuid = vol.partGuid;
- writeSettingsLocked();
}
}
+
+ rec.lastSeenMillis = System.currentTimeMillis();
+ writeSettingsLocked();
}
mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
@@ -1731,9 +1733,10 @@ class StorageManagerService extends IStorageManager.Stub
meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
- meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
- meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS);
- meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS);
+ meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS, 0);
+ meta.lastSeenMillis = readLongAttribute(in, ATTR_LAST_SEEN_MILLIS, 0);
+ meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS, 0);
+ meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS, 0);
return meta;
}
@@ -1745,6 +1748,7 @@ class StorageManagerService extends IStorageManager.Stub
writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
+ writeLongAttribute(out, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis);
writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
out.endTag(null, TAG_VOLUME);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index f9aaf11c75f0..a7fb99f8b004 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -140,6 +140,7 @@ public class Watchdog extends Thread {
private boolean mCompleted;
private Monitor mCurrentMonitor;
private long mStartTime;
+ private int mPauseCount;
HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
@@ -160,17 +161,18 @@ public class Watchdog extends Thread {
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
- if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
+ if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
+ || (mPauseCount > 0)) {
+ // Don't schedule until after resume OR
// If the target looper has recently been polling, then
// there is no reason to enqueue our checker on it since that
// is as good as it not being deadlocked. This avoid having
- // to do a context switch to check the thread. Note that we
- // only do this if mCheckReboot is false and we have no
- // monitors, since those would need to be executed at this point.
+ // to do a context switch to check the thread. Note that we
+ // only do this if we have no monitors since those would need to
+ // be executed at this point.
mCompleted = true;
return;
}
-
if (!mCompleted) {
// we already have a check in flight, so no need
return;
@@ -236,6 +238,28 @@ public class Watchdog extends Thread {
mCurrentMonitor = null;
}
}
+
+ /** Pause the HandlerChecker. */
+ public void pauseLocked(String reason) {
+ mPauseCount++;
+ // Mark as completed, because there's a chance we called this after the watchog
+ // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
+ // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ mCompleted = true;
+ Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
+ + reason + ". Pause count: " + mPauseCount);
+ }
+
+ /** Resume the HandlerChecker from the last {@link #pauseLocked}. */
+ public void resumeLocked(String reason) {
+ if (mPauseCount > 0) {
+ mPauseCount--;
+ Slog.i(TAG, "Resuming HandlerChecker: " + mName + " for reason: "
+ + reason + ". Pause count: " + mPauseCount);
+ } else {
+ Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
+ }
+ }
}
final class RebootRequestReceiver extends BroadcastReceiver {
@@ -364,6 +388,51 @@ public class Watchdog extends Thread {
}
/**
+ * Pauses Watchdog action for the currently running thread. Useful before executing long running
+ * operations that could falsely trigger the watchdog. Each call to this will require a matching
+ * call to {@link #resumeWatchingCurrentThread}.
+ *
+ * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
+ *
+ * <p>If the Watchdog is already paused for the current thread, this call adds
+ * adds another pause and will require an additional {@link #resumeCurrentThread} to resume.
+ *
+ * <p>Note: Use with care, as any deadlocks on the current thread will be undetected until all
+ * pauses have been resumed.
+ */
+ public void pauseWatchingCurrentThread(String reason) {
+ synchronized (this) {
+ for (HandlerChecker hc : mHandlerCheckers) {
+ if (Thread.currentThread().equals(hc.getThread())) {
+ hc.pauseLocked(reason);
+ }
+ }
+ }
+ }
+
+ /**
+ * Resumes the last pause from {@link #pauseWatchingCurrentThread} for the currently running
+ * thread.
+ *
+ * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
+ *
+ * <p>If the Watchdog action for the current thread is already resumed, this call logs a wtf.
+ *
+ * <p>If all pauses have been resumed, the Watchdog action is finally resumed, otherwise,
+ * the Watchdog action for the current thread remains paused until resume is called at least
+ * as many times as the calls to pause.
+ */
+ public void resumeWatchingCurrentThread(String reason) {
+ synchronized (this) {
+ for (HandlerChecker hc : mHandlerCheckers) {
+ if (Thread.currentThread().equals(hc.getThread())) {
+ hc.resumeLocked(reason);
+ }
+ }
+ }
+ }
+
+ /**
* Perform a full reboot of the system.
*/
void rebootSystem(String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d8f5937d3cdd..ea71a3b2e17e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -735,11 +735,11 @@ public class ActivityManagerService extends IActivityManager.Stub
* <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
* method.
*/
- void put(int key, ProcessRecord value) {
+ void put(ProcessRecord app) {
synchronized (this) {
- mPidMap.put(key, value);
+ mPidMap.put(app.pid, app);
}
- mAtmInternal.onProcessMapped(key, value.getWindowProcessController());
+ mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController());
}
/**
@@ -747,11 +747,18 @@ public class ActivityManagerService extends IActivityManager.Stub
* <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
* method.
*/
- void remove(int pid) {
+ void remove(ProcessRecord app) {
+ boolean removed = false;
synchronized (this) {
- mPidMap.remove(pid);
+ final ProcessRecord existingApp = mPidMap.get(app.pid);
+ if (existingApp != null && existingApp.startSeq == app.startSeq) {
+ mPidMap.remove(app.pid);
+ removed = true;
+ }
+ }
+ if (removed) {
+ mAtmInternal.onProcessUnMapped(app.pid);
}
- mAtmInternal.onProcessUnMapped(pid);
}
/**
@@ -759,17 +766,18 @@ public class ActivityManagerService extends IActivityManager.Stub
* <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
* method.
*/
- boolean removeIfNoThread(int pid) {
+ boolean removeIfNoThread(ProcessRecord app) {
boolean removed = false;
synchronized (this) {
- final ProcessRecord app = get(pid);
- if (app != null && app.thread == null) {
- mPidMap.remove(pid);
+ final ProcessRecord existingApp = get(app.pid);
+ if (existingApp != null && existingApp.startSeq == app.startSeq
+ && app.thread == null) {
+ mPidMap.remove(app.pid);
removed = true;
}
}
if (removed) {
- mAtmInternal.onProcessUnMapped(pid);
+ mAtmInternal.onProcessUnMapped(app.pid);
}
return removed;
}
@@ -1970,7 +1978,7 @@ public class ActivityManagerService extends IActivityManager.Stub
app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
- mPidsSelfLocked.put(app.pid, app);
+ mPidsSelfLocked.put(app);
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
@@ -4601,7 +4609,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
- boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
+ boolean gone = mPidsSelfLocked.removeIfNoThread(app);
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
@@ -4658,6 +4666,26 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
+ if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) {
+ String processName = null;
+ final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
+ if (pending != null) {
+ processName = pending.processName;
+ }
+ final String msg = "attachApplicationLocked process:" + processName
+ + " startSeq:" + startSeq
+ + " pid:" + pid
+ + " belongs to another existing app:" + app.processName
+ + " startSeq:" + app.startSeq;
+ Slog.wtf(TAG, msg);
+ // SafetyNet logging for b/131105245.
+ EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg);
+ // If there is already an app occupying that pid that hasn't been cleaned up
+ cleanUpApplicationRecordLocked(app, false, false, -1,
+ true /*replacingPid*/);
+ mPidsSelfLocked.remove(app);
+ app = null;
+ }
} else {
app = null;
}
@@ -4666,7 +4694,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// update the internal state.
if (app == null && startSeq > 0) {
final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
- if (pending != null && pending.startUid == callingUid
+ if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
&& mProcessList.handleProcessStartedLocked(pending, pid, pending
.isUsingWrapper(),
startSeq, true)) {
@@ -13642,7 +13670,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
- mPidsSelfLocked.remove(app.pid);
+ mPidsSelfLocked.remove(app);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 376999dfd80d..beb0e4741c36 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -107,9 +107,11 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub {
EventLogTags.writeAmPreBoot(mUserId, componentName.getPackageName());
mIntent.setComponent(componentName);
- mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
- AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
- Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
+ synchronized (mService) {
+ mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
+ AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+ Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 696697eddf25..dc94c1e01250 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1445,10 +1445,11 @@ public final class ProcessList {
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
- mService.mPidsSelfLocked.remove(app.pid);
+ mService.mPidsSelfLocked.remove(app);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startTime, "startProcess: done removing from pids map");
app.setPid(0);
+ app.startSeq = 0;
}
if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
@@ -1656,25 +1657,25 @@ public final class ProcessList {
app.killedByAm = false;
app.removed = false;
app.killed = false;
+ if (app.startSeq != 0) {
+ Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+ + " with non-zero startSeq:" + app.startSeq);
+ }
+ if (app.pid != 0) {
+ Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+ + " with non-zero pid:" + app.pid);
+ }
final long startSeq = app.startSeq = ++mProcStartSeqCounter;
app.setStartParams(uid, hostingRecord, seInfo, startTime);
+ app.setUsingWrapper(invokeWith != null
+ || SystemProperties.get("wrap." + app.processName) != null);
+ mPendingStarts.put(startSeq, app);
+
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
"Posting procStart msg for " + app.toShortString());
mService.mProcStartHandler.post(() -> {
try {
- synchronized (mService) {
- final String reason = isProcStartValidLocked(app, startSeq);
- if (reason != null) {
- Slog.w(TAG_PROCESSES, app + " not valid anymore,"
- + " don't start process, " + reason);
- app.pendingStart = false;
- return;
- }
- app.setUsingWrapper(invokeWith != null
- || SystemProperties.get("wrap." + app.processName) != null);
- mPendingStarts.put(startSeq, app);
- }
final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
@@ -2071,12 +2072,15 @@ public final class ProcessList {
// If there is already an app occupying that pid that hasn't been cleaned up
if (oldApp != null && !app.isolated) {
// Clean up anything relating to this pid first
- Slog.w(TAG, "Reusing pid " + pid
- + " while app is still mapped to it");
+ Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
+ + " startSeq:" + app.startSeq
+ + " pid:" + pid
+ + " belongs to another existing app:" + oldApp.processName
+ + " startSeq:" + oldApp.startSeq);
mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
true /*replacingPid*/);
}
- mService.mPidsSelfLocked.put(pid, app);
+ mService.mPidsSelfLocked.put(app);
synchronized (mService.mPidsSelfLocked) {
if (!procAttached) {
Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
@@ -2249,7 +2253,7 @@ public final class ProcessList {
.pendingStart)) {
int pid = app.pid;
if (pid > 0) {
- mService.mPidsSelfLocked.remove(pid);
+ mService.mPidsSelfLocked.remove(app);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 7605ccb5aeda..e14846863946 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -270,7 +270,7 @@ public class AttentionManagerService extends SystemService {
return;
}
if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) {
- Slog.e(LOG_TAG, "Cannot cancel a non-current request");
+ Slog.w(LOG_TAG, "Cannot cancel a non-current request");
return;
}
cancel(userState);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 88919df04f00..db17f8397ed4 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -475,7 +475,7 @@ public class BiometricService extends SystemService {
DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */,
userId) != 0);
- if (userId == ActivityManager.getCurrentUser()) {
+ if (userId == ActivityManager.getCurrentUser() && !selfChange) {
notifyEnabledOnKeyguardCallbacks(userId);
}
} else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
@@ -494,17 +494,25 @@ public class BiometricService extends SystemService {
}
boolean getFaceEnabledOnKeyguard() {
- return mFaceEnabledOnKeyguard.getOrDefault(
- ActivityManager.getCurrentUser(), DEFAULT_KEYGUARD_ENABLED);
+ final int user = ActivityManager.getCurrentUser();
+ if (!mFaceEnabledOnKeyguard.containsKey(user)) {
+ onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
+ }
+ return mFaceEnabledOnKeyguard.get(user);
}
boolean getFaceEnabledForApps(int userId) {
+ if (!mFaceEnabledForApps.containsKey(userId)) {
+ onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
+ }
return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
}
boolean getFaceAlwaysRequireConfirmation(int userId) {
- return mFaceAlwaysRequireConfirmation
- .getOrDefault(userId, DEFAULT_ALWAYS_REQUIRE_CONFIRMATION);
+ if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
+ onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
+ }
+ return mFaceAlwaysRequireConfirmation.get(userId);
}
void notifyEnabledOnKeyguardCallbacks(int userId) {
@@ -760,7 +768,6 @@ public class BiometricService extends SystemService {
@Override // Binder call
public int canAuthenticate(String opPackageName) {
checkPermission();
- checkAppOp(opPackageName, Binder.getCallingUid());
final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
@@ -833,9 +840,9 @@ public class BiometricService extends SystemService {
}
private void checkPermission() {
- if (getContext().checkCallingPermission(USE_FINGERPRINT)
+ if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
!= PackageManager.PERMISSION_GRANTED) {
- getContext().enforceCallingPermission(USE_BIOMETRIC,
+ getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
"Must have USE_BIOMETRIC permission");
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index ac3d6def6f80..0910dac27337 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -19,7 +19,6 @@ package com.android.server.connectivity;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
import android.app.Notification;
import android.app.NotificationManager;
@@ -107,10 +106,14 @@ public class NetworkNotificationManager {
}
}
- private static int getIcon(int transportType) {
- return (transportType == TRANSPORT_WIFI) ?
- R.drawable.stat_notify_wifi_in_range : // TODO: Distinguish ! from ?.
- R.drawable.stat_notify_rssi_in_range;
+ private static int getIcon(int transportType, NotificationType notifyType) {
+ if (transportType != TRANSPORT_WIFI) {
+ return R.drawable.stat_notify_rssi_in_range;
+ }
+
+ return notifyType == NotificationType.LOGGED_IN
+ ? R.drawable.ic_wifi_signal_4
+ : R.drawable.stat_notify_wifi_in_range; // TODO: Distinguish ! from ?.
}
/**
@@ -127,6 +130,7 @@ public class NetworkNotificationManager {
* @param id an identifier that uniquely identifies this notification. This must match
* between show and hide calls. We use the NetID value but for legacy callers
* we concatenate the range of types with the range of NetIDs.
+ * @param notifyType the type of the notification.
* @param nai the network with which the notification is associated. For a SIGN_IN, NO_INTERNET,
* or LOST_INTERNET notification, this is the network we're connecting to. For a
* NETWORK_SWITCH notification it's the network that we switched from. When this network
@@ -173,7 +177,7 @@ public class NetworkNotificationManager {
Resources r = Resources.getSystem();
CharSequence title;
CharSequence details;
- int icon = getIcon(transportType);
+ int icon = getIcon(transportType, notifyType);
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index f8582cd7928f..d05369e9cfa1 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -650,7 +650,7 @@ public class PermissionMonitor {
case INetd.PERMISSION_UPDATE_DEVICE_STATS:
updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
- case INetd.NO_PERMISSIONS:
+ case INetd.PERMISSION_NONE:
noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
case INetd.PERMISSION_UNINSTALLED:
@@ -676,7 +676,7 @@ public class PermissionMonitor {
ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
}
if (noPermissionAppIds.size() != 0) {
- mNetd.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE,
ArrayUtils.convertToIntArray(noPermissionAppIds));
}
if (uninstalledAppIds.size() != 0) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b676618f4cd3..35a82aef51b5 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -49,6 +49,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
@@ -1625,6 +1626,15 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
+ * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This
+ * does not cause a job's period to be larger than requested (eg: if the requested period is
+ * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene
+ * and try to optimize scheduling if the current job finished less than this amount of time to
+ * the start of the next period
+ */
+ private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
+
+ /**
* Called after a periodic has executed so we can reschedule it. We take the last execution
* time of the job to be the time of completion (i.e. the time at which this function is
* called).
@@ -1644,16 +1654,18 @@ public class JobSchedulerService extends com.android.server.SystemService
final long period = periodicToReschedule.getJob().getIntervalMillis();
final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed();
final long flex = periodicToReschedule.getJob().getFlexMillis();
+ long rescheduleBuffer = 0;
+ final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed);
if (elapsedNow > latestRunTimeElapsed) {
// The job ran past its expected run window. Have it count towards the current window
// and schedule a new job for the next window.
if (DEBUG) {
Slog.i(TAG, "Periodic job ran after its intended window.");
}
- final long diffMs = (elapsedNow - latestRunTimeElapsed);
int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window
- if (period != flex && diffMs > Math.min(30 * MINUTE_IN_MILLIS, (period - flex) / 2)) {
+ if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
+ (period - flex) / 2)) {
if (DEBUG) {
Slog.d(TAG, "Custom flex job ran too close to next window.");
}
@@ -1664,9 +1676,15 @@ public class JobSchedulerService extends com.android.server.SystemService
newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
} else {
newLatestRuntimeElapsed = latestRunTimeElapsed + period;
+ if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) {
+ // Add a little buffer to the start of the next window so the job doesn't run
+ // too soon after this completed one.
+ rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs);
+ }
}
- final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
+ final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
+ - Math.min(flex, period - rescheduleBuffer);
if (DEBUG) {
Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
@@ -2764,12 +2782,12 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
- public List<JobInfo> getAllPendingJobs() throws RemoteException {
+ public ParceledListSlice<JobInfo> getAllPendingJobs() throws RemoteException {
final int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
- return JobSchedulerService.this.getPendingJobs(uid);
+ return new ParceledListSlice<>(JobSchedulerService.this.getPendingJobs(uid));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2905,7 +2923,7 @@ public class JobSchedulerService extends com.android.server.SystemService
* <p class="note">This is a slow operation, so it should be called sparingly.
*/
@Override
- public List<JobSnapshot> getAllJobSnapshots() {
+ public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
throw new SecurityException(
@@ -2916,7 +2934,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mJobs.forEachJob((job) -> snapshots.add(
new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
isReadyToBeExecutedLocked(job))));
- return snapshots;
+ return new ParceledListSlice<>(snapshots);
}
}
};
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index bd6662d9dc51..aa51aec216eb 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -317,8 +317,10 @@ class GnssConfiguration {
if (configManager == null) {
return;
}
- PersistableBundle configs = configManager.getConfigForSubId(
- SubscriptionManager.getDefaultDataSubscriptionId());
+
+ int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ PersistableBundle configs = SubscriptionManager.isValidSubscriptionId(ddSubId)
+ ? configManager.getConfigForSubId(ddSubId) : null;
if (configs == null) {
if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
configs = CarrierConfigManager.getDefaultConfig();
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5b7eca6e310e..b2315c7b7314 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -60,7 +60,6 @@ import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
@@ -75,6 +74,7 @@ import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.location.gnssmetrics.GnssMetrics;
+import com.android.internal.telephony.TelephonyIntents;
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
@@ -184,7 +184,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final int DOWNLOAD_PSDS_DATA = 6;
private static final int UPDATE_LOCATION = 7; // Handle external location from network listener
private static final int DOWNLOAD_PSDS_DATA_FINISHED = 11;
- private static final int SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED = 12;
private static final int INITIALIZE_HANDLER = 13;
private static final int REQUEST_LOCATION = 16;
private static final int REPORT_LOCATION = 17; // HAL reports location
@@ -484,22 +483,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
updateLowPowerMode();
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
+ case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
subscriptionOrCarrierConfigChanged(context);
break;
}
}
};
- // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
- // broadcast receiver.
- private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
- new OnSubscriptionsChangedListener() {
- @Override
- public void onSubscriptionsChanged() {
- sendMessage(SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED, 0, null);
- }
- };
-
/**
* Implements {@link GnssSatelliteBlacklistCallback#onUpdateSatelliteBlacklist}.
*/
@@ -515,12 +505,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mContext.getSystemService(Context.TELEPHONY_SERVICE);
CarrierConfigManager configManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- String mccMnc = phone.getSimOperator();
+ int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ String mccMnc = SubscriptionManager.isValidSubscriptionId(ddSubId)
+ ? phone.getSimOperator(ddSubId) : phone.getSimOperator();
boolean isKeepLppProfile = false;
if (!TextUtils.isEmpty(mccMnc)) {
if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
if (configManager != null) {
- PersistableBundle b = configManager.getConfig();
+ PersistableBundle b = SubscriptionManager.isValidSubscriptionId(ddSubId)
+ ? configManager.getConfigForSubId(ddSubId) : null;
if (b != null) {
isKeepLppProfile =
b.getBoolean(CarrierConfigManager.Gps.KEY_PERSIST_LPP_MODE_BOOL);
@@ -539,7 +532,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
SystemProperties.set(GnssConfiguration.LPP_PROFILE, "");
}
reloadGpsProperties();
- mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
} else {
if (DEBUG) Log.d(TAG, "SIM MCC/MNC is still not available");
}
@@ -577,9 +569,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
+ mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
if (mGnssVisibilityControl != null) {
- mGnssVisibilityControl.updateProxyApps(mGnssConfiguration.getProxyApps());
- mGnssVisibilityControl.setEsNotify(mGnssConfiguration.getEsNotify(0));
+ mGnssVisibilityControl.onConfigurationUpdated(mGnssConfiguration);
}
}
@@ -1892,28 +1884,34 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
int type = AGPS_SETID_TYPE_NONE;
- String data = "";
+ String setId = null;
+ int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
- String data_temp = phone.getSubscriberId();
- if (data_temp == null) {
- // This means the framework does not have the SIM card ready.
- } else {
+ if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+ setId = phone.getSubscriberId(ddSubId);
+ }
+ if (setId == null) {
+ setId = phone.getSubscriberId();
+ }
+ if (setId != null) {
// This means the framework has the SIM card.
- data = data_temp;
type = AGPS_SETID_TYPE_IMSI;
}
} else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
- String data_temp = phone.getLine1Number();
- if (data_temp == null) {
- // This means the framework does not have the SIM card ready.
- } else {
+ if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+ setId = phone.getLine1Number(ddSubId);
+ }
+ if (setId == null) {
+ setId = phone.getLine1Number();
+ }
+ if (setId != null) {
// This means the framework has the SIM card.
- data = data_temp;
type = AGPS_SETID_TYPE_MSISDN;
}
}
- native_agps_set_id(type, data);
+
+ native_agps_set_id(type, (setId == null) ? "" : setId);
}
@NativeEntryPoint
@@ -2025,9 +2023,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case UPDATE_LOCATION:
handleUpdateLocation((Location) msg.obj);
break;
- case SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED:
- subscriptionOrCarrierConfigChanged(mContext);
- break;
case INITIALIZE_HANDLER:
handleInitialize();
break;
@@ -2066,17 +2061,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
// (this configuration might change in the future based on SIM changes)
reloadGpsProperties();
- // TODO: When this object "finishes" we should unregister by invoking
- // SubscriptionManager.getInstance(mContext).unregister
- // (mOnSubscriptionsChangedListener);
- // This is not strictly necessary because it will be unregistered if the
- // notification fails but it is good form.
-
- // Register for SubscriptionInfo list changes which is guaranteed
- // to invoke onSubscriptionsChanged the first time.
- SubscriptionManager.from(mContext)
- .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
-
// listen for events
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
@@ -2086,6 +2070,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intentFilter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
mNetworkConnectivityHandler.registerNetworkCallbacks();
@@ -2164,8 +2149,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
return "DOWNLOAD_PSDS_DATA_FINISHED";
case UPDATE_LOCATION:
return "UPDATE_LOCATION";
- case SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED:
- return "SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED";
case INITIALIZE_HANDLER:
return "INITIALIZE_HANDLER";
case REPORT_LOCATION:
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index a3670a45d9cb..c49d9000c1dd 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -76,7 +76,7 @@ class GnssVisibilityControl {
private final GpsNetInitiatedHandler mNiHandler;
private boolean mIsGpsEnabled;
- private volatile boolean mEsNotify;
+ private boolean mEsNotify;
// Number of non-framework location access proxy apps is expected to be small (< 5).
private static final int ARRAY_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -124,10 +124,6 @@ class GnssVisibilityControl {
}
}
- void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
- runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
- }
-
void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
boolean inEmergencyMode, boolean isCachedLocation) {
@@ -136,15 +132,25 @@ class GnssVisibilityControl {
requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
}
- void setEsNotify(int esNotifyConfig) {
- if (esNotifyConfig != ES_NOTIFY_NONE && esNotifyConfig != ES_NOTIFY_ALL) {
+ void onConfigurationUpdated(GnssConfiguration configuration) {
+ // The configuration object must be accessed only in the caller thread and not in mHandler.
+ List<String> nfwLocationAccessProxyApps = configuration.getProxyApps();
+ int esNotify = configuration.getEsNotify(ES_NOTIFY_NONE);
+ runOnHandler(() -> {
+ setEsNotify(esNotify);
+ handleUpdateProxyApps(nfwLocationAccessProxyApps);
+ });
+ }
+
+ private void setEsNotify(int esNotify) {
+ if (esNotify != ES_NOTIFY_NONE && esNotify != ES_NOTIFY_ALL) {
Log.e(TAG, "Config parameter " + GnssConfiguration.CONFIG_ES_NOTIFY_INT
- + " is set to invalid value: " + esNotifyConfig
+ + " is set to invalid value: " + esNotify
+ ". Using default value: " + ES_NOTIFY_NONE);
- esNotifyConfig = ES_NOTIFY_NONE;
+ esNotify = ES_NOTIFY_NONE;
}
- mEsNotify = (esNotifyConfig == ES_NOTIFY_ALL);
+ mEsNotify = (esNotify == ES_NOTIFY_ALL);
}
private void handleInitialize() {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c2125b0a997d..b246eb6d38bd 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -421,8 +421,9 @@ public class LockSettingsService extends ILockSettings.Stub {
new PasswordSlotManager());
}
- public boolean hasBiometrics() {
- return BiometricManager.hasBiometrics(mContext);
+ public boolean hasEnrolledBiometrics() {
+ BiometricManager bm = mContext.getSystemService(BiometricManager.class);
+ return bm.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS;
}
public int binderGetCallingUid() {
@@ -2502,7 +2503,8 @@ public class LockSettingsService extends ILockSettings.Stub {
// TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
// we need to generate challenge for each one, have it signed by GK and reset lockout
// for each modality.
- if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)
+ && mInjector.hasEnrolledBiometrics()) {
challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
}
@@ -2544,8 +2546,8 @@ public class LockSettingsService extends ILockSettings.Stub {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
notifyActivePasswordMetricsAvailable(credentialType, userCredential, userId);
unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
- // Reset lockout
- if (mInjector.hasBiometrics()) {
+ // Reset lockout only if user has enrolled templates
+ if (mInjector.hasEnrolledBiometrics()) {
BiometricManager bm = mContext.getSystemService(BiometricManager.class);
Slog.i(TAG, "Resetting lockout, length: "
+ authResult.gkResponse.getPayload().length);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index ef94000d3a6b..388e51f203ca 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -30,6 +30,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
@@ -43,6 +44,7 @@ import javax.crypto.spec.SecretKeySpec;
public class SyntheticPasswordCrypto {
private static final int PROFILE_KEY_IV_SIZE = 12;
+ private static final int DEFAULT_TAG_LENGTH_BITS = 128;
private static final int AES_KEY_LENGTH = 32; // 256-bit AES key
private static final byte[] APPLICATION_ID_PERSONALIZATION = "application-id".getBytes();
// Time between the user credential is verified with GK and the decryption of synthetic password
@@ -60,13 +62,14 @@ public class SyntheticPasswordCrypto {
byte[] ciphertext = Arrays.copyOfRange(blob, PROFILE_KEY_IV_SIZE, blob.length);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
- cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
+ cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(DEFAULT_TAG_LENGTH_BITS, iv));
return cipher.doFinal(ciphertext);
}
private static byte[] encrypt(SecretKey key, byte[] blob)
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException,
- InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
+ InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
+ InvalidParameterSpecException {
if (blob == null) {
return null;
}
@@ -79,6 +82,11 @@ public class SyntheticPasswordCrypto {
if (iv.length != PROFILE_KEY_IV_SIZE) {
throw new RuntimeException("Invalid iv length: " + iv.length);
}
+ final GCMParameterSpec spec = cipher.getParameters().getParameterSpec(
+ GCMParameterSpec.class);
+ if (spec.getTLen() != DEFAULT_TAG_LENGTH_BITS) {
+ throw new RuntimeException("Invalid tag length: " + spec.getTLen());
+ }
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(iv);
outputStream.write(ciphertext);
@@ -92,7 +100,8 @@ public class SyntheticPasswordCrypto {
try {
return encrypt(key, message);
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
- | IllegalBlockSizeException | BadPaddingException | IOException e) {
+ | IllegalBlockSizeException | BadPaddingException | IOException
+ | InvalidParameterSpecException e) {
e.printStackTrace();
return null;
}
@@ -147,7 +156,7 @@ public class SyntheticPasswordCrypto {
public static byte[] createBlob(String keyAlias, byte[] data, byte[] applicationId, long sid) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
- keyGenerator.init(new SecureRandom());
+ keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
@@ -169,7 +178,8 @@ public class SyntheticPasswordCrypto {
} catch (CertificateException | IOException | BadPaddingException
| IllegalBlockSizeException
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
- | InvalidKeyException e) {
+ | InvalidKeyException
+ | InvalidParameterSpecException e) {
e.printStackTrace();
throw new RuntimeException("Failed to encrypt blob", e);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4f859412f5f4..13b4ab927c59 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -23,6 +23,7 @@ import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
@@ -1031,12 +1032,19 @@ public class NotificationManagerService extends SystemService {
final StatusBarNotification n = r.sbn;
final int callingUid = n.getUid();
final String pkg = n.getPackageName();
+ final boolean wasBubble = r.getNotification().isBubbleNotification();
if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid,
null /* oldEntry */)) {
r.getNotification().flags |= FLAG_BUBBLE;
} else {
r.getNotification().flags &= ~FLAG_BUBBLE;
}
+ if (wasBubble != r.getNotification().isBubbleNotification()) {
+ // Add the "alert only once" flag so that the notification won't HUN
+ // unnecessarily just because the bubble flag was changed.
+ r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+ mListeners.notifyPostedLocked(r, r);
+ }
}
}
}
@@ -5732,7 +5740,7 @@ public class NotificationManagerService extends SystemService {
}
// Suppressed because it's a silent update
final Notification notification = record.getNotification();
- if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+ if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
return false;
}
// Suppressed because another notification in its group handles alerting
@@ -5751,7 +5759,7 @@ public class NotificationManagerService extends SystemService {
boolean shouldMuteNotificationLocked(final NotificationRecord record) {
// Suppressed because it's a silent update
final Notification notification = record.getNotification();
- if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+ if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
return true;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index 7ee167adfd38..99f583978535 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -120,42 +120,65 @@ final class OverlayManagerShellCommand extends ShellCommand {
return 1;
}
}
+
final String packageName = getNextArg();
+ if (packageName != null) {
+ List<OverlayInfo> overlaysForTarget = mInterface.getOverlayInfosForTarget(
+ packageName, userId);
+
+ // If the package is not targeted by any overlays, check if the package is an overlay.
+ if (overlaysForTarget.isEmpty()) {
+ final OverlayInfo info = mInterface.getOverlayInfo(packageName, userId);
+ if (info != null) {
+ printListOverlay(out, info);
+ }
+ return 0;
+ }
+
+ out.println(packageName);
+
+ // Print the overlays for the target.
+ final int n = overlaysForTarget.size();
+ for (int i = 0; i < n; i++) {
+ printListOverlay(out, overlaysForTarget.get(i));
+ }
+
+ return 0;
+ }
+ // Print all overlays grouped by target package name.
final Map<String, List<OverlayInfo>> allOverlays = mInterface.getAllOverlays(userId);
for (final String targetPackageName : allOverlays.keySet()) {
- if (targetPackageName.equals(packageName)) {
- out.println(targetPackageName);
- }
+ out.println(targetPackageName);
+
List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName);
final int n = overlaysForTarget.size();
for (int i = 0; i < n; i++) {
- final OverlayInfo oi = overlaysForTarget.get(i);
- if (!targetPackageName.equals(packageName) && !oi.packageName.equals(packageName)) {
- continue;
- }
- String status;
- switch (oi.state) {
- case OverlayInfo.STATE_ENABLED_STATIC:
- case OverlayInfo.STATE_ENABLED:
- status = "[x]";
- break;
- case OverlayInfo.STATE_DISABLED:
- status = "[ ]";
- break;
- default:
- status = "---";
- break;
- }
- out.println(String.format("%s %s", status, oi.packageName));
- }
- if (targetPackageName.equals(packageName)) {
- out.println();
+ printListOverlay(out, overlaysForTarget.get(i));
}
+ out.println();
}
+
return 0;
}
+ private void printListOverlay(PrintWriter out, OverlayInfo oi) {
+ String status;
+ switch (oi.state) {
+ case OverlayInfo.STATE_ENABLED_STATIC:
+ case OverlayInfo.STATE_ENABLED:
+ status = "[x]";
+ break;
+ case OverlayInfo.STATE_DISABLED:
+ status = "[ ]";
+ break;
+ default:
+ status = "---";
+ break;
+ }
+ out.println(String.format("%s %s", status, oi.packageName));
+ }
+
private int runEnableDisable(final boolean enable) throws RemoteException {
final PrintWriter err = getErrPrintWriter();
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index bab612d3c092..9d115963423d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -73,6 +73,7 @@ public class Installer extends SystemService {
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
+ public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL;
public static final int FLAG_CLEAR_CACHE_ONLY = IInstalld.FLAG_CLEAR_CACHE_ONLY;
public static final int FLAG_CLEAR_CODE_CACHE_ONLY = IInstalld.FLAG_CLEAR_CODE_CACHE_ONLY;
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 691b38e2b093..6499530bd7c3 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -28,7 +28,6 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.R;
@@ -162,29 +161,34 @@ public class ModuleInfoProvider {
}
}
- List<ModuleInfo> getInstalledModules(int flags) {
+ /**
+ * By default, returns installed module info, including installed apex modules.
+ *
+ * @param flags Use {@link PackageManager#MATCH_ALL} flag to get all modules.
+ */
+ List<ModuleInfo> getInstalledModules(@PackageManager.ModuleInfoFlags int flags) {
if (!mMetadataLoaded) {
throw new IllegalStateException("Call to getInstalledModules before metadata loaded");
}
- ArrayList<ModuleInfo> allModules = new ArrayList<>(mModuleInfo.values());
if ((flags & PackageManager.MATCH_ALL) != 0) {
- return allModules;
+ return new ArrayList<>(mModuleInfo.values());
}
- ArraySet<String> allPackages;
+ List<PackageInfo> allPackages;
try {
- allPackages = new ArraySet<>(mPackageManager.getAllPackages());
+ allPackages = mPackageManager.getInstalledPackages(
+ flags | PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM).getList();
} catch (RemoteException e) {
Slog.w(TAG, "Unable to retrieve all package names", e);
return Collections.emptyList();
}
ArrayList<ModuleInfo> installedModules = new ArrayList<>(allPackages.size());
- for (int i = allModules.size() - 1; i >= 0; --i) {
- ModuleInfo mi = allModules.get(i);
- if (allPackages.contains(mi.getPackageName())) {
- installedModules.add(mi);
+ for (PackageInfo p : allPackages) {
+ ModuleInfo m = mModuleInfo.get(p.packageName);
+ if (m != null) {
+ installedModules.add(m);
}
}
return installedModules;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 13818855f82f..dd48e2dfa2c5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -89,6 +89,7 @@ import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
@@ -3249,7 +3250,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
@@ -3502,8 +3503,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
return false;
}
- clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
- | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
mDexManager.notifyPackageUpdated(pkg.packageName,
pkg.baseCodePath, pkg.splitCodePaths);
}
@@ -15987,6 +15988,9 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mInstallLock) {
// Clean up both app data and code
// All package moves are frozen until finished
+
+ // We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
+ // this task was only focused on moving data on internal storage.
for (int userId : userIds) {
try {
mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
@@ -17087,8 +17091,8 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = pkg.packageName;
prepareAppDataAfterInstallLIF(pkg);
if (reconciledPkg.prepareResult.clearCodeCache) {
- clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
if (reconciledPkg.prepareResult.replace) {
mDexManager.notifyPackageUpdated(pkg.packageName,
@@ -18860,7 +18864,7 @@ public class PackageManagerService extends IPackageManager.Stub
resolvedPkg.setVolumeUuid(deletedPs.volumeUuid);
}
destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
destroyAppProfilesLIF(resolvedPkg);
if (outInfo != null) {
outInfo.dataRemoved = true;
@@ -19612,7 +19616,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
destroyAppDataLIF(pkg, nextUserId,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
clearDefaultBrowserIfNeededForUser(ps.name, nextUserId);
removeKeystoreDataIfNeeded(nextUserId, ps.appId);
final SparseBooleanArray changedUsers = new SparseBooleanArray();
@@ -19748,7 +19752,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
clearAppDataLIF(pkg, userId,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
removeKeystoreDataIfNeeded(userId, appId);
@@ -19979,8 +19983,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (doClearData) {
synchronized (mInstallLock) {
- final int flags = StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE;
+ final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL;
// We're only clearing cache files, so we don't care if the
// app is unfrozen and still able to run
clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
@@ -22528,9 +22531,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
- clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
- | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 37c1aaa45b8b..b2ba2904cabc 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -929,6 +929,7 @@ public class PermissionManagerService {
final BasePermission bp = mSettings.getPermissionLocked(permName);
final boolean appSupportsRuntimePermissions =
pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
+ String upgradedActivityRecognitionPermission = null;
if (DEBUG_INSTALL) {
Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
@@ -947,11 +948,40 @@ public class PermissionManagerService {
// Cache newImplicitPermissions before modifing permissionsState as for the shared
// uids the original and new state are the same object
if (!origPermissions.hasRequestedPermission(permName)
- && pkg.implicitPermissions.contains(permName)) {
- newImplicitPermissions.add(permName);
+ && (pkg.implicitPermissions.contains(permName)
+ || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
+ if (pkg.implicitPermissions.contains(permName)) {
+ // If permName is an implicit permission, try to auto-grant
+ newImplicitPermissions.add(permName);
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for " + pkg.packageName);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for " + pkg.packageName);
+ }
+ } else {
+ // Special case for Activity Recognition permission. Even if AR permission
+ // is not an implicit permission we want to add it to the list (try to
+ // auto-grant it) if the app was installed on a device before AR permission
+ // was split, regardless of if the app now requests the new AR permission
+ // or has updated its target SDK and AR is no longer implicit to it.
+ // This is a compatibility workaround for apps when AR permission was
+ // split in Q.
+ int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ PermissionManager.SplitPermissionInfo sp =
+ PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
+ String splitPermName = sp.getSplitPermission();
+ if (sp.getNewPermissions().contains(permName)
+ && origPermissions.hasInstallPermission(splitPermName)) {
+ upgradedActivityRecognitionPermission = splitPermName;
+ newImplicitPermissions.add(permName);
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for "
+ + pkg.packageName);
+ }
+ break;
+ }
+ }
}
}
@@ -985,7 +1015,8 @@ public class PermissionManagerService {
// For all apps normal permissions are install time ones.
grant = GRANT_INSTALL;
} else if (bp.isRuntime()) {
- if (origPermissions.hasInstallPermission(bp.getName())) {
+ if (origPermissions.hasInstallPermission(bp.getName())
+ || upgradedActivityRecognitionPermission != null) {
// Before Q we represented some runtime permissions as install permissions,
// in Q we cannot do this anymore. Hence upgrade them all.
grant = GRANT_UPGRADE;
@@ -1161,10 +1192,15 @@ public class PermissionManagerService {
.getInstallPermissionState(perm);
int flags = (permState != null) ? permState.getFlags() : 0;
+ BasePermission bpToRevoke =
+ upgradedActivityRecognitionPermission == null
+ ? bp : mSettings.getPermissionLocked(
+ upgradedActivityRecognitionPermission);
// Remove install permission
- if (origPermissions.revokeInstallPermission(bp)
+ if (origPermissions.revokeInstallPermission(bpToRevoke)
!= PERMISSION_OPERATION_FAILURE) {
- origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ origPermissions.updatePermissionFlags(bpToRevoke,
+ UserHandle.USER_ALL,
(MASK_PERMISSION_FLAGS_ALL
& ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
changedInstallPermission = true;
@@ -1489,9 +1525,11 @@ public class PermissionManagerService {
for (int userNum = 0; userNum < numUsers; userNum++) {
int userId = users[userNum];
- ps.updatePermissionFlags(bp, userId,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
+ ps.updatePermissionFlags(bp, userId,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ }
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
boolean inheritsFromInstallPerm = false;
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 3262eb67a052..14f1196ab3a2 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -75,6 +75,8 @@ public class AttentionDetector {
*/
private final AtomicBoolean mRequested;
+ private long mLastActedOnNextScreenDimming;
+
/**
* Monotonously increasing ID for the requests sent.
*/
@@ -150,6 +152,9 @@ public class AttentionDetector {
}
public long updateUserActivity(long nextScreenDimming) {
+ if (nextScreenDimming == mLastActedOnNextScreenDimming) {
+ return nextScreenDimming;
+ }
if (!mIsSettingEnabled) {
return nextScreenDimming;
}
@@ -190,13 +195,14 @@ public class AttentionDetector {
// afterwards if AttentionManager couldn't deliver it.
mRequested.set(true);
mRequestId++;
+ mLastActedOnNextScreenDimming = nextScreenDimming;
mCallback = new AttentionCallbackInternalImpl(mRequestId);
+ Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
if (!sent) {
mRequested.set(false);
}
- Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
return whenToCheck;
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index fe0b9a6acc85..b7e18c35829e 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -537,6 +537,7 @@ public class BatterySaverStateMachine {
Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted
+ " mSettingsLoaded=" + mSettingsLoaded
+ " mBatteryStatusSet=" + mBatteryStatusSet
+ + " mState=" + mState
+ " mIsBatteryLevelLow=" + mIsBatteryLevelLow
+ " mIsPowered=" + mIsPowered
+ " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver
@@ -689,9 +690,9 @@ public class BatterySaverStateMachine {
final boolean isStickyDisabled =
mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky;
if (isStickyDisabled || shouldTurnOffSticky) {
+ mState = STATE_OFF;
setStickyActive(false);
triggerStickyDisabledNotification();
- mState = STATE_OFF;
} else if (!mIsPowered) {
// Re-enable BS.
enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
@@ -797,7 +798,8 @@ public class BatterySaverStateMachine {
Intent.ACTION_POWER_USAGE_SUMMARY));
}
- private void triggerStickyDisabledNotification() {
+ @VisibleForTesting
+ void triggerStickyDisabledNotification() {
NotificationManager manager = mContext.getSystemService(NotificationManager.class);
ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
R.string.battery_saver_notification_channel_name);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 1edb93a0cdb9..7d0da68a0750 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.stats;
+import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.os.Process.getPidsForCommands;
@@ -33,6 +34,11 @@ import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequest;
+import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.HistoricalUidOps;
import android.app.ProcessMemoryHighWaterMark;
import android.app.ProcessMemoryState;
import android.app.StatsManager;
@@ -146,6 +152,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -154,6 +162,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -1941,6 +1950,53 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
+ private void pullAppOps(long elapsedNanos, final long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+ CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
+ HistoricalOpsRequest histOpsRequest =
+ new HistoricalOpsRequest.Builder(
+ Instant.now().minus(1, ChronoUnit.HOURS).toEpochMilli(),
+ Long.MAX_VALUE).build();
+ appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
+
+ HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.APP_OPS, elapsedNanos,
+ wallClockNanos);
+
+ for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+ final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+ final int uid = uidOps.getUid();
+ for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
+ final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
+ for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
+ final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
+ e.writeInt(uid);
+ e.writeString(packageOps.getPackageName());
+ e.writeInt(op.getOpCode());
+ e.writeLong(op.getForegroundAccessCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getForegroundRejectCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+ pulledData.add(e);
+ }
+ }
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Could not read appops", t);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+
/**
* Add a RoleHolder atom for each package that holds a role.
*
@@ -2331,6 +2387,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
pullFaceSettings(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.APP_OPS: {
+ pullAppOps(elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
index 7310af3a6eb6..bbe534e5aeda 100644
--- a/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
@@ -25,4 +25,9 @@ public class ConversationActionsInstallReceiver extends ConfigUpdateInstallRecei
"metadata/actions_suggestions",
"version");
}
+
+ @Override
+ protected boolean verifyVersion(int current, int alternative) {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
index 05dad2178798..ecfa3aa97ac0 100644
--- a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
@@ -25,4 +25,9 @@ public class LangIdInstallReceiver extends ConfigUpdateInstallReceiver {
"metadata/lang_id",
"version");
}
+
+ @Override
+ protected boolean verifyVersion(int current, int alternative) {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1344727ab36d..66b305ec2dac 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1085,6 +1085,22 @@ final class ActivityRecord extends ConfigurationContainer {
if (root == this) {
task.setRootProcess(proc);
}
+ // Override the process configuration to match the display where the first activity in
+ // the process was launched. This can help with compat issues on secondary displays when
+ // apps use Application to obtain configuration or metrics instead of Activity.
+ final ActivityDisplay display = getDisplay();
+ if (display == null || display.mDisplayId == INVALID_DISPLAY) {
+ return;
+ }
+ if (!proc.hasActivities() && display.mDisplayId != DEFAULT_DISPLAY) {
+ proc.registerDisplayConfigurationListenerLocked(display);
+ } else if (display.mDisplayId == DEFAULT_DISPLAY) {
+ // Once an activity is launched on default display - stop listening for other displays
+ // configurations to maintain compatibility with previous platform releases. E.g. when
+ // an activity is launched in a Bubble and then moved to default screen, we should match
+ // the global device config.
+ proc.unregisterDisplayConfigurationListenerLocked();
+ }
}
boolean hasProcess() {
@@ -3233,7 +3249,7 @@ final class ActivityRecord extends ConfigurationContainer {
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
+ setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
@@ -3342,6 +3358,14 @@ final class ActivityRecord extends ConfigurationContainer {
return true;
}
+ /** Get process configuration, or global config if the process is not set. */
+ private Configuration getProcessGlobalConfiguration() {
+ if (app != null) {
+ return app.getConfiguration();
+ }
+ return mAtmService.getGlobalConfiguration();
+ }
+
/**
* When assessing a configuration change, decide if the changes flags and the new configurations
* should cause the Activity to relaunch.
@@ -3449,7 +3473,7 @@ final class ActivityRecord extends ConfigurationContainer {
mStackSupervisor.activityRelaunchingLocked(this);
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
- new MergedConfiguration(mAtmService.getGlobalConfiguration(),
+ new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ba415861acc6..3d59e66d13ef 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -281,8 +281,9 @@ class ActivityStack extends ConfigurationContainer {
if (display != null && inSplitScreenPrimaryWindowingMode()) {
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
- getStackDockedModeBounds(null, null, mTmpRect2, mTmpRect3);
- mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect2,
+ getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
+ mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
+ mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
mTmpRect2, null, null, PRESERVE_WINDOWS);
}
mRootActivityContainer.updateUIDsPresentOnDisplay();
@@ -396,7 +397,6 @@ class ActivityStack extends ConfigurationContainer {
private final Rect mTmpRect = new Rect();
private final Rect mTmpRect2 = new Rect();
- private final Rect mTmpRect3 = new Rect();
private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
/** List for processing through a set of activities */
@@ -512,7 +512,6 @@ class ActivityStack extends ConfigurationContainer {
mWindowManager = mService.mWindowManager;
mStackId = stackId;
mCurrentUser = mService.mAmInternal.getCurrentUserId();
- mTmpRect2.setEmpty();
// Set display id before setting activity and window type to make sure it won't affect
// stacks on a wrong display.
mDisplayId = display.mDisplayId;
@@ -572,90 +571,87 @@ class ActivityStack extends ConfigurationContainer {
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWindowingMode = getWindowingMode();
final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
- final ActivityDisplay display = getDisplay();
final int prevRotation = getWindowConfiguration().getRotation();
final int prevDensity = getConfiguration().densityDpi;
final int prevScreenW = getConfiguration().screenWidthDp;
final int prevScreenH = getConfiguration().screenHeightDp;
-
- getBounds(mTmpRect); // previous bounds
+ final Rect newBounds = mTmpRect;
+ // Initialize the new bounds by previous bounds as the input and output for calculating
+ // override bounds in pinned (pip) or split-screen mode.
+ getBounds(newBounds);
super.onConfigurationChanged(newParentConfig);
- if (display == null) {
- return;
- }
- if (getTaskStack() == null) {
+ final ActivityDisplay display = getDisplay();
+ if (display == null || getTaskStack() == null) {
return;
}
+ final boolean windowingModeChanged = prevWindowingMode != getWindowingMode();
+ final int overrideWindowingMode = getRequestedOverrideWindowingMode();
// Update bounds if applicable
boolean hasNewOverrideBounds = false;
// Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_PINNED) {
+ if (overrideWindowingMode == WINDOWING_MODE_PINNED) {
// Pinned calculation already includes rotation
- mTmpRect2.set(mTmpRect);
- hasNewOverrideBounds = getTaskStack().calculatePinnedBoundsForConfigChange(mTmpRect2);
- } else {
+ hasNewOverrideBounds = getTaskStack().calculatePinnedBoundsForConfigChange(newBounds);
+ } else if (!matchParentBounds()) {
+ // If the parent (display) has rotated, rotate our bounds to best-fit where their
+ // bounds were on the pre-rotated display.
final int newRotation = getWindowConfiguration().getRotation();
- if (!matchParentBounds()) {
- // If the parent (display) has rotated, rotate our bounds to best-fit where their
- // bounds were on the pre-rotated display.
- if (prevRotation != newRotation) {
- mTmpRect2.set(mTmpRect);
- getDisplay().mDisplayContent
- .rotateBounds(newParentConfig.windowConfiguration.getBounds(),
- prevRotation, newRotation, mTmpRect2);
- hasNewOverrideBounds = true;
- }
+ final boolean rotationChanged = prevRotation != newRotation;
+ if (rotationChanged) {
+ display.mDisplayContent.rotateBounds(
+ newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
+ newBounds);
+ hasNewOverrideBounds = true;
+ }
+ // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
+ if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
// If entering split screen or if something about the available split area changes,
// recalculate the split windows to match the new configuration.
- if (prevRotation != newRotation
+ if (rotationChanged || windowingModeChanged
|| prevDensity != getConfiguration().densityDpi
- || prevWindowingMode != getWindowingMode()
|| prevScreenW != getConfiguration().screenWidthDp
|| prevScreenH != getConfiguration().screenHeightDp) {
- // Use override windowing mode to prevent extra bounds changes if inheriting
- // the mode.
- if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || getRequestedOverrideWindowingMode()
- == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- mTmpRect2.set(mTmpRect);
- getTaskStack()
- .calculateDockedBoundsForConfigChange(newParentConfig, mTmpRect2);
- hasNewOverrideBounds = true;
- }
+ getTaskStack().calculateDockedBoundsForConfigChange(newParentConfig, newBounds);
+ hasNewOverrideBounds = true;
}
}
}
- if (getWindowingMode() != prevWindowingMode) {
+
+ if (windowingModeChanged) {
// Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- getStackDockedModeBounds(null, null, mTmpRect2, mTmpRect3);
+ if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
+ newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
// immediately resize so docked bounds are available in onSplitScreenModeActivated
setTaskDisplayedBounds(null);
- setTaskBounds(mTmpRect2);
- setBounds(mTmpRect2);
- } else if (
- getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ setTaskBounds(newBounds);
+ setBounds(newBounds);
+ newBounds.set(newBounds);
+ } else if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
Rect dockedBounds = display.getSplitScreenPrimaryStack().getBounds();
final boolean isMinimizedDock =
- getDisplay().mDisplayContent.getDockedDividerController().isMinimizedDock();
+ display.mDisplayContent.getDockedDividerController().isMinimizedDock();
if (isMinimizedDock) {
TaskRecord topTask = display.getSplitScreenPrimaryStack().topTask();
if (topTask != null) {
dockedBounds = topTask.getBounds();
}
}
- getStackDockedModeBounds(dockedBounds, null, mTmpRect2, mTmpRect3);
+ getStackDockedModeBounds(dockedBounds, null /* currentTempTaskBounds */,
+ newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
hasNewOverrideBounds = true;
}
- }
- if (prevWindowingMode != getWindowingMode()) {
display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
- mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
+ // Note the resizeStack may enter onConfigurationChanged recursively, so we make a copy
+ // of the temporary bounds (newBounds is mTmpRect) to avoid it being modified.
+ mRootActivityContainer.resizeStack(this, new Rect(newBounds), null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, true /* deferResume */);
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index b8442a887dac..9d08e10c6dea 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -46,7 +46,6 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
-import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -281,13 +280,6 @@ class ActivityStartInterceptor {
mActivityOptions = ActivityOptions.makeBasic();
}
- ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity();
- if (homeActivityRecord != null && homeActivityRecord.getTaskRecord() != null) {
- // Showing credential confirmation activity in home task to avoid stopping
- // multi-windowed mode after showing the full-screen credential confirmation activity.
- mActivityOptions.setLaunchTaskId(homeActivityRecord.getTaskRecord().taskId);
- }
-
final UserInfo parent = mUserManager.getProfileParent(mUserId);
mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
@@ -304,7 +296,7 @@ class ActivityStartInterceptor {
return null;
}
// TODO(b/28935539): should allow certain activities to bypass work challenge
- final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(),
+ final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
final KeyguardManager km = (KeyguardManager) mServiceContext
.getSystemService(KEYGUARD_SERVICE);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 89962a5418c1..772e5e646825 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -39,7 +39,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST;
import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
@@ -6892,15 +6891,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
- FLAG_ACTIVITY_TASK_ON_HOME);
- ActivityOptions activityOptions = options != null
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ final ActivityOptions activityOptions = options != null
? new ActivityOptions(options) : ActivityOptions.makeBasic();
- final ActivityRecord homeActivity =
- mRootActivityContainer.getDefaultDisplayHomeActivity();
- if (homeActivity != null) {
- activityOptions.setLaunchTaskId(homeActivity.getTaskRecord().taskId);
- }
mContext.startActivityAsUser(intent, activityOptions.toBundle(),
UserHandle.CURRENT);
} finally {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fd90f0339e63..3d7e50d91b08 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2005,7 +2005,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
layoutLetterbox(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(mPendingTransaction);
+ mLetterbox.applySurfaceChanges(getPendingTransaction());
}
}
@@ -2782,6 +2782,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
protected void onAnimationFinished() {
super.onAnimationFinished();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished");
mTransit = TRANSIT_UNSET;
mTransitFlags = 0;
mNeedsZBoost = false;
@@ -2816,6 +2817,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
scheduleAnimation();
mActivityRecord.onAnimationFinished();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@Override
@@ -3059,13 +3061,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
- mPendingTransaction.show(mSurfaceControl);
+ getPendingTransaction().show(mSurfaceControl);
} else if (!show && mLastSurfaceShowing) {
- mPendingTransaction.hide(mSurfaceControl);
+ getPendingTransaction().hide(mSurfaceControl);
}
}
if (mThumbnail != null) {
- mThumbnail.setShowing(mPendingTransaction, show);
+ mThumbnail.setShowing(getPendingTransaction(), show);
}
mLastSurfaceShowing = show;
super.prepareSurfaces();
@@ -3225,8 +3227,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
private void updateColorTransform() {
if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
- mPendingTransaction.setColorTransform(mSurfaceControl, mLastAppSaturationInfo.mMatrix,
- mLastAppSaturationInfo.mTranslation);
+ getPendingTransaction().setColorTransform(mSurfaceControl,
+ mLastAppSaturationInfo.mMatrix, mLastAppSaturationInfo.mTranslation);
mWmService.scheduleAnimationLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 58a03b26b959..82ea4fe42799 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1480,7 +1480,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
// By updating the Display info here it will be available to
// #computeScreenConfiguration() later.
- updateDisplayAndOrientation(getConfiguration().uiMode);
+ updateDisplayAndOrientation(getConfiguration().uiMode, null /* outConfig */);
// NOTE: We disable the rotation in the emulator because
// it doesn't support hardware OpenGL emulation yet.
@@ -1578,7 +1578,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* changed.
* Do not call if {@link WindowManagerService#mDisplayReady} == false.
*/
- private DisplayInfo updateDisplayAndOrientation(int uiMode) {
+ private DisplayInfo updateDisplayAndOrientation(int uiMode, Configuration outConfig) {
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == ROTATION_90 || mRotation == ROTATION_270);
final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
@@ -1610,6 +1610,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
}
+ computeSizeRangesAndScreenLayout(mDisplayInfo, rotated, uiMode, dw, dh,
+ mDisplayMetrics.density, outConfig);
+
// We usually set the override info in DisplayManager so that we get consistent display
// metrics values when displays are changing and don't send out new values until WM is aware
// of them. However, we don't do this for displays that serve as containers for ActivityView
@@ -1658,7 +1661,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Do not call if mDisplayReady == false.
*/
void computeScreenConfiguration(Configuration config) {
- final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode);
+ final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
calculateBounds(displayInfo, mTmpBounds);
config.windowConfiguration.setBounds(mTmpBounds);
@@ -1688,9 +1691,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
|| displayInfo.rotation == Surface.ROTATION_270);
- computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh, density,
- config);
-
config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
| ((displayInfo.flags & Display.FLAG_ROUND) != 0
? Configuration.SCREENLAYOUT_ROUND_YES
@@ -1844,6 +1844,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw);
adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh);
adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw);
+
+ if (outConfig == null) {
+ return;
+ }
int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
displayInfo.displayCutout);
@@ -3340,7 +3344,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final SurfaceControl newParent =
shouldAttachToDisplay ? mWindowingLayer : computeImeParent();
if (newParent != null) {
- mPendingTransaction.reparent(mImeWindowsContainers.mSurfaceControl, newParent);
+ getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
scheduleAnimation();
}
}
@@ -3747,7 +3751,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mPortalWindowHandle.touchableRegion.getBounds(mTmpRect);
if (!mTmpBounds.equals(mTmpRect)) {
mPortalWindowHandle.touchableRegion.set(mTmpBounds);
- mPendingTransaction.setInputWindowInfo(mParentSurfaceControl, mPortalWindowHandle);
+ getPendingTransaction().setInputWindowInfo(
+ mParentSurfaceControl, mPortalWindowHandle);
}
}
}
@@ -4846,18 +4851,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
try {
final ScreenRotationAnimation screenRotationAnimation =
mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ final Transaction transaction = getPendingTransaction();
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
- mPendingTransaction.setMatrix(mWindowingLayer,
+ transaction.setMatrix(mWindowingLayer,
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- mPendingTransaction.setPosition(mWindowingLayer,
+ transaction.setPosition(mWindowingLayer,
mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
- mPendingTransaction.setAlpha(mWindowingLayer,
+ transaction.setAlpha(mWindowingLayer,
screenRotationAnimation.getEnterTransformation().getAlpha());
}
super.prepareSurfaces();
+
+ // TODO: Once we totally eliminate global transaction we will pass transaction in here
+ // rather than merging to global.
+ SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -5013,7 +5023,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (mPortalWindowHandle == null) {
mPortalWindowHandle = createPortalWindowHandle(sc.toString());
}
- mPendingTransaction.setInputWindowInfo(sc, mPortalWindowHandle)
+ getPendingTransaction().setInputWindowInfo(sc, mPortalWindowHandle)
.reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
}
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index ef2a21d919e1..b30da5e156e2 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -819,7 +819,7 @@ public class LockTaskController {
} catch (Settings.SettingNotFoundException e) {
// Log to SafetyNet for b/127605586
android.util.EventLog.writeEvent(0x534e4554, "127605586", -1, "");
- return mLockPatternUtils.isSecure(USER_CURRENT);
+ return getLockPatternUtils().isSecure(USER_CURRENT);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bb7867ce53ec..38201069ebca 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -138,6 +138,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
setOrientation(SCREEN_ORIENTATION_UNSET);
}
+ @Override
DisplayContent getDisplayContent() {
return mStack != null ? mStack.getDisplayContent() : null;
}
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index 17e4b897c9da..ee4e462cb85e 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -77,7 +77,7 @@ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
@Override
public SurfaceControl.Transaction getPendingTransaction() {
- return mTask.mPendingTransaction;
+ return mTask.getPendingTransaction();
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 757f6a1c2f94..d83869109ff3 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -93,10 +93,6 @@ public class TaskStack extends WindowContainer<Task> implements
/** Unique identifier */
final int mStackId;
- /** The display this stack sits under. */
- // TODO: Track parent marks like this in WindowContainer.
- private DisplayContent mDisplayContent;
-
/** For comparison with DisplayContent bounds. */
private Rect mTmpRect = new Rect();
private Rect mTmpRect2 = new Rect();
@@ -177,10 +173,6 @@ public class TaskStack extends WindowContainer<Task> implements
EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
}
- DisplayContent getDisplayContent() {
- return mDisplayContent;
- }
-
Task findHomeTask() {
if (!isActivityTypeHome() || mChildren.isEmpty()) {
return null;
@@ -327,7 +319,8 @@ public class TaskStack extends WindowContainer<Task> implements
* Sets the bounds animation target bounds ahead of an animation. This can't currently be done
* in onAnimationStart() since that is started on the UiThread.
*/
- void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen) {
+ private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
+ boolean toFullscreen) {
mBoundsAnimatingRequested = true;
mBoundsAnimatingToFullscreen = toFullscreen;
if (destBounds != null) {
@@ -337,7 +330,11 @@ public class TaskStack extends WindowContainer<Task> implements
}
if (sourceHintBounds != null) {
mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
- } else {
+ } else if (!mBoundsAnimating) {
+ // If the bounds are already animating, we don't want to reset the source hint. This is
+ // because the source hint is sent when starting the animation from the client that
+ // requested to enter pip. Other requests can adjust the pip bounds during an animation,
+ // but could accidentally reset the source hint bounds.
mBoundsAnimationSourceHintBounds.setEmpty();
}
@@ -825,8 +822,7 @@ public class TaskStack extends WindowContainer<Task> implements
throw new IllegalStateException("onDisplayChanged: Already attached");
}
- final boolean movedToNewDisplay = mDisplayContent == null;
- mDisplayContent = dc;
+ super.onDisplayChanged(dc);
updateSurfaceBounds();
if (mAnimationBackgroundSurface == null) {
@@ -834,8 +830,6 @@ public class TaskStack extends WindowContainer<Task> implements
.setName("animation background stackId=" + mStackId)
.build();
}
-
- super.onDisplayChanged(dc);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d5c3e4f34c26..bbef261d17bb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -109,14 +109,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// The owner/creator for this container. No controller if null.
WindowContainerController mController;
+ // The display this window container is on.
+ protected DisplayContent mDisplayContent;
+
protected SurfaceControl mSurfaceControl;
private int mLastLayer = 0;
private SurfaceControl mLastRelativeToLayer = null;
+ // TODO(b/132320879): Remove this from WindowContainers except DisplayContent.
+ private final Transaction mPendingTransaction;
+
/**
* Applied as part of the animation pass in "prepareSurfaces".
*/
- protected final Transaction mPendingTransaction;
protected final SurfaceAnimator mSurfaceAnimator;
protected final WindowManagerService mWmService;
@@ -320,12 +325,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
if (mSurfaceControl != null) {
- mPendingTransaction.remove(mSurfaceControl);
+ getPendingTransaction().remove(mSurfaceControl);
// Merge to parent transaction to ensure the transactions on this WindowContainer are
// applied in native even if WindowContainer is removed.
if (mParent != null) {
- mParent.getPendingTransaction().merge(mPendingTransaction);
+ mParent.getPendingTransaction().merge(getPendingTransaction());
}
mSurfaceControl = null;
@@ -508,12 +513,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @param dc The display this container is on after changes.
*/
void onDisplayChanged(DisplayContent dc) {
+ mDisplayContent = dc;
+ if (dc != null && dc != this) {
+ dc.getPendingTransaction().merge(mPendingTransaction);
+ }
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mChildren.get(i);
child.onDisplayChanged(dc);
}
}
+ DisplayContent getDisplayContent() {
+ return mDisplayContent;
+ }
+
void setWaitingForDrawnIfResizingChanged() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
@@ -1180,13 +1193,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
- /**
- * TODO: Once we totally eliminate global transaction we will pass transaction in here
- * rather than merging to global.
- */
void prepareSurfaces() {
- SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
-
// If a leash has been set when the transaction was committed, then the leash reparent has
// been committed.
mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
@@ -1204,8 +1211,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
- * Trigger a call to prepareSurfaces from the animation thread, such that
- * mPendingTransaction will be applied.
+ * Trigger a call to prepareSurfaces from the animation thread, such that pending transactions
+ * will be applied.
*/
void scheduleAnimation() {
if (mParent != null) {
@@ -1224,6 +1231,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Override
public Transaction getPendingTransaction() {
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent != null && displayContent != this) {
+ return displayContent.getPendingTransaction();
+ }
+ // This WindowContainer has not attached to a display yet or this is a DisplayContent, so we
+ // let the caller to save the surface operations within the local mPendingTransaction.
+ // If this is not a DisplayContent, we will merge it to the pending transaction of its
+ // display once it attaches to it.
return mPendingTransaction;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 12b62b99b3e2..5fc399719aed 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -439,7 +439,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
@Override
protected ConfigurationContainer getParent() {
- return null;
+ // Returning RootActivityContainer as the parent, so that this process controller always
+ // has full configuration and overrides (e.g. from display) are always added on top of
+ // global config.
+ return mAtm.mRootActivityContainer;
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dd3c6004dcad..c35e86645719 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1313,6 +1313,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mOrientationChangeTimedOut = true;
}
+ @Override
DisplayContent getDisplayContent() {
return mToken.getDisplayContent();
}
@@ -3103,7 +3104,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
- new MergedConfiguration(mWmService.mRoot.getConfiguration(),
+ new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration());
setLastReportedMergedConfiguration(mergedConfiguration);
@@ -4602,7 +4603,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
- startAnimation(mPendingTransaction, adapter);
+ startAnimation(getPendingTransaction(), adapter);
commitPendingTransaction();
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f0b9c62f2843..8aee0f2a8308 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -82,9 +82,6 @@ class WindowToken extends WindowContainer<WindowState> {
// windows will be put to the bottom of the list.
boolean sendingToBottom;
- // The display this token is on.
- protected DisplayContent mDisplayContent;
-
/** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */
final boolean mOwnerCanManageAppTokens;
@@ -249,10 +246,6 @@ class WindowToken extends WindowContainer<WindowState> {
return null;
}
- DisplayContent getDisplayContent() {
- return mDisplayContent;
- }
-
@Override
void removeImmediately() {
if (mDisplayContent != null) {
@@ -266,7 +259,6 @@ class WindowToken extends WindowContainer<WindowState> {
@Override
void onDisplayChanged(DisplayContent dc) {
dc.reParentWindowToken(this);
- mDisplayContent = dc;
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index ec7a78beb122..73bb579bd274 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -32,6 +32,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <binder/IServiceManager.h>
+#include <gui/SurfaceComposerClient.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
#include <hidl/ServiceManagement.h>
@@ -147,6 +148,8 @@ static void sendPowerHint(PowerHint hintId, uint32_t data) {
processPowerHalReturn(ret, "powerHint");
}
}
+
+ SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId));
}
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index fb34913a5606..1ab3b98ae78d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -349,7 +349,7 @@ class SecurityLogMonitor implements Runnable {
lastPos++;
} else {
// Two events have the same timestamp, check if they are the same.
- if (lastEvent.equals(curEvent)) {
+ if (lastEvent.eventEquals(curEvent)) {
// Actual overlap, just skip the event.
if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos);
} else {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8c65fa8b53bf..e42ed3b03139 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1049,12 +1049,7 @@ public final class SystemServer {
mDisplayManagerService.windowManagerAndInputReady();
traceEnd();
- // Skip Bluetooth if we have an emulator kernel
- // TODO: Use a more reliable check to see if this product should
- // support Bluetooth - see bug 988521
- if (isEmulator) {
- Slog.i(TAG, "No Bluetooth Service (emulator)");
- } else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+ if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
Slog.i(TAG, "No Bluetooth Service (factory test)");
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
@@ -1167,9 +1162,12 @@ public final class SystemServer {
if (!mOnlyCore) {
traceBeginAndSlog("UpdatePackagesIfNeeded");
try {
+ Watchdog.getInstance().pauseWatchingCurrentThread("dexopt");
mPackageManagerService.updatePackagesIfNeeded();
} catch (Throwable e) {
reportWtf("update packages", e);
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("dexopt");
}
traceEnd();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 6517303842ed..77e2517d7752 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -43,6 +43,8 @@ import static com.android.server.AlarmManagerService.Constants.KEY_LISTENER_TIME
import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.AlarmManagerService.WORKING_INDEX;
import static org.junit.Assert.assertEquals;
@@ -126,13 +128,15 @@ public class AlarmManagerServiceTest {
private MockitoSession mMockingSession;
private Injector mInjector;
private volatile long mNowElapsedTest;
+ private volatile long mNowRtcTest;
@GuardedBy("mTestTimer")
private TestTimer mTestTimer = new TestTimer();
static class TestTimer {
private long mElapsed;
boolean mExpired;
- int mType;
+ private int mType;
+ private int mFlags; // Flags used to decide what needs to be evaluated.
synchronized long getElapsed() {
return mElapsed;
@@ -147,7 +151,16 @@ public class AlarmManagerServiceTest {
return mType;
}
+ synchronized int getFlags() {
+ return mFlags;
+ }
+
synchronized void expire() throws InterruptedException {
+ expire(IS_WAKEUP_MASK); // Default: evaluate eligibility of all alarms
+ }
+
+ synchronized void expire(int flags) throws InterruptedException {
+ mFlags = flags;
mExpired = true;
notifyAll();
// Now wait for the alarm thread to finish execution.
@@ -181,7 +194,7 @@ public class AlarmManagerServiceTest {
}
mTestTimer.mExpired = false;
}
- return AlarmManagerService.IS_WAKEUP_MASK; // Doesn't matter, just evaluate.
+ return mTestTimer.getFlags();
}
@Override
@@ -215,6 +228,11 @@ public class AlarmManagerServiceTest {
}
@Override
+ long getCurrentTimeMillis() {
+ return mNowRtcTest;
+ }
+
+ @Override
AlarmManagerService.ClockReceiver getClockReceiver(AlarmManagerService service) {
return mClockReceiver;
}
@@ -340,7 +358,7 @@ public class AlarmManagerServiceTest {
}
@Test
- public void testSingleAlarmSet() {
+ public void singleElapsedAlarmSet() {
final long triggerTime = mNowElapsedTest + 5000;
final PendingIntent alarmPi = getNewMockPendingIntent();
setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
@@ -348,6 +366,33 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void singleRtcAlarmSet() {
+ mNowElapsedTest = 54;
+ mNowRtcTest = 1243; // arbitrary values of time
+ final long triggerRtc = mNowRtcTest + 5000;
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi);
+ final long triggerElapsed = triggerRtc - (mNowRtcTest - mNowElapsedTest);
+ assertEquals(triggerElapsed, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void timeChangeMovesRtcAlarm() throws Exception {
+ mNowElapsedTest = 42;
+ mNowRtcTest = 4123; // arbitrary values of time
+ final long triggerRtc = mNowRtcTest + 5000;
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi);
+ final long triggerElapsed1 = mTestTimer.getElapsed();
+ final long timeDelta = -123;
+ mNowRtcTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+ final long triggerElapsed2 = mTestTimer.getElapsed();
+ assertEquals("Invalid movement of triggerElapsed following time change", triggerElapsed2,
+ triggerElapsed1 - timeDelta);
+ }
+
+ @Test
public void testSingleAlarmExpiration() throws Exception {
final long triggerTime = mNowElapsedTest + 5000;
final PendingIntent alarmPi = getNewMockPendingIntent();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index f7edf65a499f..18c524ad7a94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -183,15 +183,188 @@ public class JobSchedulerServiceTest {
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
- advanceElapsedClock(45 * MINUTE_IN_MILLIS); // now + 55 minutes
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS); // now + 30 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(job);
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ advanceElapsedClock(25 * MINUTE_IN_MILLIS); // now + 55 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with an extra delay and correct deadline constraint if the periodic job is completed near the
+ * end of its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_closeToEndOfWindow() {
+ JobStatus frequentJob = createJobStatus(
+ "testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS));
+ long now = sElapsedRealtimeClock.millis();
+ long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS;
+ long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS;
+
+ // At the beginning of the window. Next window should be unaffected.
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock((long) (7.5 * MINUTE_IN_MILLIS));
+ rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob);
+ assertEquals(nextWindowStartTime + MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ JobStatus mediumJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + HOUR_IN_MILLIS;
+ nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime + (6 * MINUTE_IN_MILLIS),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ JobStatus longJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(6 * HOUR_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + 6 * HOUR_IN_MILLIS;
+ nextWindowEndTime = now + 12 * HOUR_IN_MILLIS;
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(3 * HOUR_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(2 * HOUR_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window should be unaffected since we're over the shift cap.
+ advanceElapsedClock(15 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime + (30 * MINUTE_IN_MILLIS),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Flex duration close to period duration.
+ JobStatus gameyFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS, 59 * MINUTE_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(29 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime + (5 * MINUTE_IN_MILLIS),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Very short flex duration compared to period duration.
+ JobStatus superFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS, 10 * MINUTE_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + HOUR_IN_MILLIS + 50 * MINUTE_IN_MILLIS;
+ nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(29 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window should be unaffected since the flex duration pushes
+ // the next window start time far enough away.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
}
@@ -265,7 +438,9 @@ public class JobSchedulerServiceTest {
advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
- assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes
@@ -273,7 +448,9 @@ public class JobSchedulerServiceTest {
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
- assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 212d2a845254..a8faa54fe9d6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -15,6 +15,10 @@
*/
package com.android.server.power.batterysaver;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -22,6 +26,8 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationManager;
@@ -37,6 +43,7 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import java.util.HashMap;
import java.util.Objects;
@@ -201,6 +208,8 @@ public class BatterySaverStateMachineTest {
mDevice = new Device();
mTarget = new TestableBatterySaverStateMachine();
+ spyOn(mTarget);
+ doNothing().when(mTarget).triggerStickyDisabledNotification();
mDevice.pushBatteryStatus();
mTarget.onBootCompleted();
@@ -423,7 +432,7 @@ public class BatterySaverStateMachineTest {
assertEquals(70, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
- // Bump ump the threshold.
+ // Bump up the threshold.
mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 70);
mDevice.setBatteryLevel(mPersistedState.batteryLevel);
@@ -545,6 +554,8 @@ public class BatterySaverStateMachineTest {
@Test
public void testAutoBatterySaver_withSticky_withAutoOffEnabled() {
+ InOrder inOrder = inOrder(mTarget);
+
mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1);
mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90);
@@ -569,6 +580,7 @@ public class BatterySaverStateMachineTest {
assertEquals(true, mDevice.batterySaverEnabled); // Stays on.
assertEquals(95, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ inOrder.verify(mTarget, never()).triggerStickyDisabledNotification();
// Scenario 2: User turns BS on manually above the threshold then charges device. BS
// shouldn't turn back on.
@@ -584,6 +596,7 @@ public class BatterySaverStateMachineTest {
assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
assertEquals(97, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ inOrder.verify(mTarget).triggerStickyDisabledNotification();
// Scenario 3: User turns BS on manually above the threshold. Device drains below
// threshold and then charged to below threshold. Sticky BS should activate.
@@ -612,6 +625,7 @@ public class BatterySaverStateMachineTest {
assertEquals(true, mDevice.batterySaverEnabled);
assertEquals(30, mPersistedState.batteryLevel);
assertEquals(true, mPersistedState.batteryLow);
+ inOrder.verify(mTarget, never()).triggerStickyDisabledNotification();
// Scenario 4: User turns BS on manually above the threshold. Device drains below
// threshold and is eventually charged to above threshold. Sticky BS should turn off.
@@ -627,6 +641,7 @@ public class BatterySaverStateMachineTest {
assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
assertEquals(90, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ inOrder.verify(mTarget).triggerStickyDisabledNotification();
// Scenario 5: User turns BS on manually below threshold and charges to below threshold.
// Sticky BS should activate.
@@ -654,6 +669,7 @@ public class BatterySaverStateMachineTest {
assertEquals(true, mDevice.batterySaverEnabled); // Sticky BS still on.
assertEquals(80, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ inOrder.verify(mTarget, never()).triggerStickyDisabledNotification();
// Scenario 6: User turns BS on manually below threshold and eventually charges to above
// threshold. Sticky BS should turn off.
@@ -665,6 +681,7 @@ public class BatterySaverStateMachineTest {
assertEquals(false, mDevice.batterySaverEnabled);
assertEquals(95, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ inOrder.verify(mTarget).triggerStickyDisabledNotification();
// Scenario 7: User turns BS on above threshold and then reboots device. Sticky BS
// shouldn't activate.
@@ -676,6 +693,8 @@ public class BatterySaverStateMachineTest {
assertEquals(false, mDevice.batterySaverEnabled);
assertEquals(93, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ // initDevice() changes the mTarget reference, so inOrder is invalid here.
+ verify(mTarget).triggerStickyDisabledNotification();
// Scenario 8: User turns BS on below threshold and then reboots device without charging.
// Sticky BS should activate.
@@ -690,6 +709,8 @@ public class BatterySaverStateMachineTest {
assertEquals(true, mDevice.batterySaverEnabled);
assertEquals(75, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ // initDevice() changes the mTarget reference, so inOrder is invalid here.
+ verify(mTarget, never()).triggerStickyDisabledNotification();
// Scenario 9: User turns BS on below threshold and then reboots device after charging
// above threshold. Sticky BS shouldn't activate.
@@ -702,6 +723,8 @@ public class BatterySaverStateMachineTest {
assertEquals(false, mDevice.batterySaverEnabled);
assertEquals(100, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ // initDevice() changes the mTarget reference, so inOrder is invalid here.
+ verify(mTarget).triggerStickyDisabledNotification();
// Scenario 10: Somehow autoDisableLevel is set to a value below lowPowerModeTriggerLevel
// and then user enables manually above both thresholds, discharges below
@@ -738,6 +761,8 @@ public class BatterySaverStateMachineTest {
assertEquals(true, mDevice.batterySaverEnabled);
assertEquals(65, mPersistedState.batteryLevel);
assertEquals(true, mPersistedState.batteryLow);
+ // initDevice() changes the mTarget reference, so inOrder is invalid here.
+ verify(mTarget, never()).triggerStickyDisabledNotification();
}
@Test
@@ -780,6 +805,7 @@ public class BatterySaverStateMachineTest {
assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
assertEquals(95, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
+ verify(mTarget).triggerStickyDisabledNotification();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 09e20e04835b..1f5ebe4536d8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -235,6 +235,10 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
mPasswordSlotManager.cleanup();
}
+ protected void flushHandlerTasks() {
+ mService.mHandler.runWithScissors(() -> { }, 0 /*now*/); // Flush runnables on handler
+ }
+
protected void assertNotEquals(long expected, long actual) {
assertTrue(expected != actual);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 10fb3ba938d4..f85e2cc10800 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -110,7 +110,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
- public boolean hasBiometrics() {
+ public boolean hasEnrolledBiometrics() {
return false;
}
@@ -163,5 +163,4 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
return storedData;
}
-
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 8af4edda3b8b..18453aa13264 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -27,6 +27,7 @@ import android.app.trust.TrustManager;
import android.content.pm.UserInfo;
import android.database.sqlite.SQLiteDatabase;
import android.os.FileUtils;
+import android.os.SystemClock;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.platform.test.annotations.Presubmit;
@@ -125,7 +126,7 @@ public class LockSettingsStorageTests extends AndroidTestCase {
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 100; i++) {
final int threadId = i;
- threads.add(new Thread() {
+ threads.add(new Thread("testKeyValue_Concurrency_" + i) {
@Override
public void run() {
synchronized (monitor) {
@@ -134,17 +135,17 @@ public class LockSettingsStorageTests extends AndroidTestCase {
} catch (InterruptedException e) {
return;
}
- mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
}
+ mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
}
});
threads.get(i).start();
@@ -153,12 +154,7 @@ public class LockSettingsStorageTests extends AndroidTestCase {
synchronized (monitor) {
monitor.notifyAll();
}
- for (int i = 0; i < threads.size(); i++) {
- try {
- threads.get(i).join();
- } catch (InterruptedException e) {
- }
- }
+ joinAll(threads, 10000);
assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
mStorage.clearCache();
assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
@@ -515,4 +511,29 @@ public class LockSettingsStorageTests extends AndroidTestCase {
}
return captured[0];
}
+
+ private static void joinAll(List<Thread> threads, long timeoutMillis) {
+ long deadline = SystemClock.uptimeMillis() + timeoutMillis;
+ for (Thread t : threads) {
+ try {
+ t.join(deadline - SystemClock.uptimeMillis());
+ if (t.isAlive()) {
+ t.interrupt();
+ throw new RuntimeException(
+ "Joining " + t + " timed out. Stack: \n" + getStack(t));
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while joining " + t, e);
+ }
+ }
+ }
+
+ private static String getStack(Thread t) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(t.toString()).append('\n');
+ for (StackTraceElement ste : t.getStackTrace()) {
+ sb.append("\tat ").append(ste.toString()).append('\n');
+ }
+ return sb.toString();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 0273f7651207..1cd590c39f49 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -232,7 +232,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
reset(mAuthSecretService);
mService.onUnlockUser(PRIMARY_USER_ID);
- mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
+ flushHandlerTasks();
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
}
@@ -242,7 +242,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
reset(mAuthSecretService);
mService.onUnlockUser(PRIMARY_USER_ID);
- mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
+ flushHandlerTasks();
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
}
@@ -254,7 +254,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
reset(mAuthSecretService);
mService.onUnlockUser(PRIMARY_USER_ID);
- mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
+ flushHandlerTasks();
verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
}
@@ -357,7 +357,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
// Verify DPM gets notified about new device lock
- mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler
+ flushHandlerTasks();
final PasswordMetrics metric = PasswordMetrics.computeForCredential(
LockPatternUtils.CREDENTIAL_TYPE_PATTERN, pattern);
verify(mDevicePolicyManager).setActivePasswordState(metric, PRIMARY_USER_ID);
@@ -384,6 +384,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
handle, token, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ flushHandlerTasks(); // flush the unlockUser() call before changing password again
mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index c30a7dd8321c..a63f49b1fe3d 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -182,10 +183,22 @@ public class AttentionDetectorTest extends AndroidTestCase {
}
@Test
+ public void testOnUserActivity_ignoresIfAlreadyDoneForThatNextScreenDimming() {
+ long when = registerAttention();
+ verify(mAttentionManagerInternal).checkAttention(anyLong(), any());
+ assertThat(when).isLessThan(mNextDimming);
+ clearInvocations(mAttentionManagerInternal);
+
+ long redundantWhen = mAttentionDetector.updateUserActivity(mNextDimming);
+ assertThat(redundantWhen).isEqualTo(mNextDimming);
+ verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
+ }
+
+ @Test
public void testOnUserActivity_skipsIfAlreadyScheduled() {
registerAttention();
reset(mAttentionManagerInternal);
- long when = mAttentionDetector.updateUserActivity(mNextDimming);
+ long when = mAttentionDetector.updateUserActivity(mNextDimming + 1);
verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
assertThat(when).isLessThan(mNextDimming);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d2332bf86e95..3661e89a5d00 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -22,6 +22,7 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIB
import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -119,7 +120,6 @@ import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
-import android.service.notification.NotifyingApp;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
import android.test.suitebuilder.annotation.SmallTest;
@@ -165,10 +165,8 @@ import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.function.Consumer;
@SmallTest
@@ -5012,6 +5010,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
+ // Reset as this is called when the notif is first sent
+ reset(mListeners);
+
// First we were a bubble
StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsBefore.length);
@@ -5021,10 +5022,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
waitForIdle();
- // Now we are not a bubble
- StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
- assertEquals(1, notifsAfter.length);
- assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
+ // Make sure we are not a bubble / reported as such to listeners
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any());
+
+ assertEquals((captor.getValue().getNotification().flags & FLAG_BUBBLE), 0);
+ assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0);
}
@Test
@@ -5054,14 +5058,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
IMPORTANCE_FOREGROUND);
+ // Reset as this is called when the notif is first sent
+ reset(mListeners);
+
// Notify we are now a bubble
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
waitForIdle();
- // Make sure we are a bubble
- StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
- assertEquals(1, notifsAfter.length);
- assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0);
+ // Make sure we are a bubble / reported as such to listeners
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any());
+
+ assertTrue((captor.getValue().getNotification().flags & FLAG_BUBBLE) != 0);
+ assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0);
}
@Test
@@ -5082,6 +5092,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
+ // Reset as this is called when the notif is first sent
+ reset(mListeners);
+
// Would be a normal notification because wouldn't have met requirements to bubble
StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsBefore.length);
@@ -5095,6 +5108,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
+ verify(mListeners, times(0)).notifyPostedLocked(any(), any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 11a177a71a10..f8fd64a8feb2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
@@ -38,6 +40,7 @@ import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -56,10 +59,10 @@ import android.app.servertransaction.PauseActivityItem;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.util.MutableBoolean;
+import android.view.DisplayInfo;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner.Stub;
import android.view.RemoteAnimationAdapter;
@@ -598,6 +601,67 @@ public class ActivityRecordTests extends ActivityTestsBase {
assertNull(mActivity.pendingOptions);
}
+ @Test
+ public void testSetProcessOverridesConfig() {
+ final ActivityRecord defaultDisplayActivity =
+ createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
+ assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges());
+
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+ assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
+ assertEquals(secondaryDisplayActivity.getDisplay().getResolvedOverrideConfiguration(),
+ secondaryDisplayActivity.app.getRequestedOverrideConfiguration());
+
+ assertNotEquals(defaultDisplayActivity.getConfiguration(),
+ secondaryDisplayActivity.getConfiguration());
+ }
+
+ @Test
+ public void testSetProcessDoesntOverrideConfigIfAnotherActivityPresent() {
+ final ActivityRecord defaultDisplayActivity =
+ createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
+ assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges());
+
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, defaultDisplayActivity.app);
+ assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
+ }
+
+ @Test
+ public void testActivityOnDefaultDisplayClearsProcessOverride() {
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+ assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
+
+ final ActivityRecord defaultDisplayActivity =
+ createActivityOnDisplay(true /* defaultDisplay */,
+ secondaryDisplayActivity.app);
+ assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges());
+ assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
+ }
+
+ /**
+ * Creates an activity on display. For non-default display request it will also create a new
+ * display with custom DisplayInfo.
+ */
+ private ActivityRecord createActivityOnDisplay(boolean defaultDisplay,
+ WindowProcessController process) {
+ final ActivityDisplay display;
+ if (defaultDisplay) {
+ display = mRootActivityContainer.getDefaultDisplay();
+ } else {
+ final DisplayInfo info = new DisplayInfo();
+ info.logicalWidth = 100;
+ info.logicalHeight = 100;
+ display = addNewActivityDisplayAt(info, POSITION_TOP);
+ }
+ final TestActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build();
+ }
+
/** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
private void prepareFixedAspectRatioUnresizableActivity() {
setupDisplayContentForCompatDisplayInsets();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 53b0add8c37e..d8c0de741845 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -195,6 +195,7 @@ class ActivityTestsBase {
private ActivityStack mStack;
private int mActivityFlags;
private int mLaunchMode;
+ private WindowProcessController mWpc;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -245,6 +246,11 @@ class ActivityTestsBase {
return this;
}
+ ActivityBuilder setUseProcess(WindowProcessController wpc) {
+ mWpc = wpc;
+ return this;
+ }
+
ActivityRecord build() {
if (mComponent == null) {
final int id = sCurrentActivityId++;
@@ -290,12 +296,18 @@ class ActivityTestsBase {
mTaskRecord.addActivityToTop(activity);
}
- final WindowProcessController wpc = new WindowProcessController(mService,
- mService.mContext.getApplicationInfo(), "name", 12345,
- UserHandle.getUserId(12345), mock(Object.class),
- mock(WindowProcessListener.class));
- wpc.setThread(mock(IApplicationThread.class));
+ final WindowProcessController wpc;
+ if (mWpc != null) {
+ wpc = mWpc;
+ } else {
+ wpc = new WindowProcessController(mService,
+ mService.mContext.getApplicationInfo(), "name", 12345,
+ UserHandle.getUserId(12345), mock(Object.class),
+ mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
+ }
activity.setProcess(wpc);
+ wpc.addActivityIfNeeded(activity);
return activity;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index fe4541183038..8528954a6b5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -153,7 +153,6 @@ public class AppWindowTokenTests extends WindowTestsBase {
@FlakyTest(bugId = 131005232)
public void testLandscapeSeascapeRotationByApp() {
// Some plumbing to get the service ready for rotation updates.
- mWm.mDisplayReady = true;
mWm.mDisplayEnabled = true;
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
@@ -186,7 +185,6 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testLandscapeSeascapeRotationByPolicy() {
// Some plumbing to get the service ready for rotation updates.
- mWm.mDisplayReady = true;
mWm.mDisplayEnabled = true;
final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
@@ -379,6 +377,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
}
@Test
+ @FlakyTest(bugId = 131176283)
public void testCreateRemoveStartingWindow() {
mToken.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 366aceafd7bf..427a92963807 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -41,6 +41,7 @@ import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.hardware.display.DisplayManagerInternal;
import android.net.Uri;
@@ -175,6 +176,12 @@ public class SystemServicesTestRule implements TestRule {
// Display creation is driven by the ActivityManagerService via
// ActivityStackSupervisor. We emulate those steps here.
mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
+ mWindowManagerService.displayReady();
+
+ final Configuration defaultDisplayConfig =
+ mWindowManagerService.computeNewConfiguration(DEFAULT_DISPLAY);
+ doReturn(defaultDisplayConfig).when(atms).getGlobalConfiguration();
+ doReturn(defaultDisplayConfig).when(atms).getGlobalConfigurationForPid(anyInt());
mMockTracker.stopTracking();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index a7c84a1c28b4..7b8fba03b6d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -26,7 +26,9 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
import android.platform.test.annotations.Presubmit;
+import android.view.DisplayInfo;
import org.junit.Test;
@@ -78,6 +80,26 @@ public class WindowProcessControllerTests extends ActivityTestsBase {
assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
}
+ @Test
+ public void testConfigurationForSecondaryScreen() {
+ final WindowProcessController wpc = new WindowProcessController(
+ mService, mock(ApplicationInfo.class), null, 0, -1, null, null);
+ //By default, the process should not listen to any display.
+ assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
+
+ // Register to a new display as a listener.
+ final DisplayInfo info = new DisplayInfo();
+ info.logicalWidth = 100;
+ info.logicalHeight = 100;
+ TestActivityDisplay display = addNewActivityDisplayAt(info, WindowContainer.POSITION_TOP);
+ wpc.registerDisplayConfigurationListenerLocked(display);
+
+ assertEquals(display.mDisplayId, wpc.getDisplayId());
+ final Configuration expectedConfig = mService.mRootActivityContainer.getConfiguration();
+ expectedConfig.updateFrom(display.getConfiguration());
+ assertEquals(expectedConfig, wpc.getConfiguration());
+ }
+
private TestActivityDisplay createTestActivityDisplayInContainer() {
final TestActivityDisplay testActivityDisplay = createNewActivityDisplay();
mRootActivityContainer.addChild(testActivityDisplay, POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 3a702cb9521c..de28b5f0fa4f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -154,7 +154,6 @@ class WindowTestsBase {
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
mWm.mDisplayEnabled = true;
- mWm.mDisplayReady = true;
// Set-up some common windows.
mCommonWindows = new HashSet<>();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 77866279a751..75e8fb5a34fe 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -1356,7 +1356,7 @@ public class AppStandbyController {
private void fetchCarrierPrivilegedAppsLocked() {
TelephonyManager telephonyManager =
mContext.getSystemService(TelephonyManager.class);
- mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+ mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivilegesForAllPhones();
mHaveCarrierPrivilegedApps = true;
if (DEBUG) {
Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index faf6ee23e208..0f3050f9e3d3 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -120,6 +120,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
switch (vol.type) {
+ case VolumeInfo.TYPE_PUBLIC:
case VolumeInfo.TYPE_PRIVATE:
case VolumeInfo.TYPE_EMULATED:
if (newState == VolumeInfo.STATE_MOUNTED) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index e1ffb0f179f8..d77ea6e41cc2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -77,6 +77,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.soundtrigger.SoundTriggerInternal;
@@ -1283,7 +1284,9 @@ public class VoiceInteractionManagerService extends SystemService {
mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL);
UserHandle currentUser = UserHandle.of(LocalServices.getService(
ActivityManagerInternal.class).getCurrentUserId());
- onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT, currentUser);
+ SystemServerInitThreadPool.get().submit(() -> onRoleHoldersChanged(
+ RoleManager.ROLE_ASSISTANT, currentUser),
+ "VoiceInteractionManagerService RoleObserver initialization");
}
private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 93c1b2138ccd..be4729a679b9 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1249,6 +1249,7 @@ public class TelecomManager {
* @deprecated Use
* {@link android.app.role.RoleManager#addRoleHolderAsUser(String, String, int, UserHandle,
* Executor, java.util.function.Consumer)} instead.
+ * @removed
*/
@SystemApi
@Deprecated
@@ -1451,6 +1452,9 @@ public class TelecomManager {
* foreground call is ended.
* <p>
* Requires permission {@link android.Manifest.permission#ANSWER_PHONE_CALLS}.
+ * <p>
+ * Note: this method CANNOT be used to end ongoing emergency calls and will return {@code false}
+ * if an attempt is made to end an emergency call.
*
* @return {@code true} if there is a call which will be rejected or terminated, {@code false}
* otherwise.
@@ -1458,8 +1462,6 @@ public class TelecomManager {
* instead. Apps performing call screening should use the {@link CallScreeningService} API
* instead.
*/
-
-
@RequiresPermission(Manifest.permission.ANSWER_PHONE_CALLS)
@Deprecated
public boolean endCall() {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d00341b3fe92..6ba359b78ee4 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -836,13 +836,6 @@ public class CarrierConfigManager {
"carrier_metered_roaming_apn_types_strings";
/**
- * Default APN types that are metered on IWLAN by the carrier
- * @hide
- */
- public static final String KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS =
- "carrier_metered_iwlan_apn_types_strings";
-
- /**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
*/
@@ -3116,15 +3109,6 @@ public class CarrierConfigManager {
new String[]{"default", "mms", "dun", "supl"});
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
- // By default all APNs should be unmetered if the device is on IWLAN. However, we add
- // default APN as metered here as a workaround for P because in some cases, a data
- // connection was brought up on cellular, but later on the device camped on IWLAN. That
- // data connection was incorrectly treated as unmetered due to the current RAT IWLAN.
- // Marking it as metered for now can workaround the issue.
- // Todo: This will be fixed in Q when IWLAN full refactoring is completed.
- sDefaults.putStringArray(KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
- new String[]{"default"});
-
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[]{
4, /* IS95A */
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 14ae68981745..9d732baf7514 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -18,6 +18,7 @@ package android.telephony;
import android.annotation.IntRange;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -46,7 +47,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P
private int mRssi; // in dBm [-113, -51] or UNAVAILABLE
@UnsupportedAppUsage
private int mBitErrorRate; // bit error rate (0-7, 99) TS 27.007 8.5 or UNAVAILABLE
- @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown
private int mLevel;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 1551ce35d2c4..984d8f705a65 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -337,7 +337,7 @@ public class ServiceState implements Parcelable {
* Reference: 3GPP TS 36.104 5.4.3 */
private int mLteEarfcnRsrpBoost = 0;
- private List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>();
+ private final List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>();
private String mOperatorAlphaLongRaw;
private String mOperatorAlphaShortRaw;
@@ -420,8 +420,10 @@ public class ServiceState implements Parcelable {
mCellBandwidths = s.mCellBandwidths == null ? null :
Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length);
mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
- mNetworkRegistrationInfos = s.mNetworkRegistrationInfos == null ? null :
- s.getNetworkRegistrationInfoList();
+ synchronized (mNetworkRegistrationInfos) {
+ mNetworkRegistrationInfos.clear();
+ mNetworkRegistrationInfos.addAll(s.getNetworkRegistrationInfoList());
+ }
mNrFrequencyRange = s.mNrFrequencyRange;
mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw;
@@ -453,8 +455,9 @@ public class ServiceState implements Parcelable {
mCdmaEriIconMode = in.readInt();
mIsEmergencyOnly = in.readInt() != 0;
mLteEarfcnRsrpBoost = in.readInt();
- mNetworkRegistrationInfos = new ArrayList<>();
- in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader());
+ synchronized (mNetworkRegistrationInfos) {
+ in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader());
+ }
mChannelNumber = in.readInt();
mCellBandwidths = in.createIntArray();
mNrFrequencyRange = in.readInt();
@@ -481,7 +484,9 @@ public class ServiceState implements Parcelable {
out.writeInt(mCdmaEriIconMode);
out.writeInt(mIsEmergencyOnly ? 1 : 0);
out.writeInt(mLteEarfcnRsrpBoost);
- out.writeList(mNetworkRegistrationInfos);
+ synchronized (mNetworkRegistrationInfos) {
+ out.writeList(mNetworkRegistrationInfos);
+ }
out.writeInt(mChannelNumber);
out.writeIntArray(mCellBandwidths);
out.writeInt(mNrFrequencyRange);
@@ -823,31 +828,33 @@ public class ServiceState implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(
- mVoiceRegState,
- mDataRegState,
- mChannelNumber,
- Arrays.hashCode(mCellBandwidths),
- mVoiceOperatorAlphaLong,
- mVoiceOperatorAlphaShort,
- mVoiceOperatorNumeric,
- mDataOperatorAlphaLong,
- mDataOperatorAlphaShort,
- mDataOperatorNumeric,
- mIsManualNetworkSelection,
- mCssIndicator,
- mNetworkId,
- mSystemId,
- mCdmaRoamingIndicator,
- mCdmaDefaultRoamingIndicator,
- mCdmaEriIconIndex,
- mCdmaEriIconMode,
- mIsEmergencyOnly,
- mLteEarfcnRsrpBoost,
- mNetworkRegistrationInfos,
- mNrFrequencyRange,
- mOperatorAlphaLongRaw,
- mOperatorAlphaShortRaw);
+ synchronized (mNetworkRegistrationInfos) {
+ return Objects.hash(
+ mVoiceRegState,
+ mDataRegState,
+ mChannelNumber,
+ Arrays.hashCode(mCellBandwidths),
+ mVoiceOperatorAlphaLong,
+ mVoiceOperatorAlphaShort,
+ mVoiceOperatorNumeric,
+ mDataOperatorAlphaLong,
+ mDataOperatorAlphaShort,
+ mDataOperatorNumeric,
+ mIsManualNetworkSelection,
+ mCssIndicator,
+ mNetworkId,
+ mSystemId,
+ mCdmaRoamingIndicator,
+ mCdmaDefaultRoamingIndicator,
+ mCdmaEriIconIndex,
+ mCdmaEriIconMode,
+ mIsEmergencyOnly,
+ mLteEarfcnRsrpBoost,
+ mNetworkRegistrationInfos,
+ mNrFrequencyRange,
+ mOperatorAlphaLongRaw,
+ mOperatorAlphaShortRaw);
+ }
}
@Override
@@ -855,30 +862,31 @@ public class ServiceState implements Parcelable {
if (!(o instanceof ServiceState)) return false;
ServiceState s = (ServiceState) o;
- return mVoiceRegState == s.mVoiceRegState
- && mDataRegState == s.mDataRegState
- && mIsManualNetworkSelection == s.mIsManualNetworkSelection
- && mChannelNumber == s.mChannelNumber
- && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
- && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
- && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
- && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
- && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
- && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
- && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
- && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
- && equalsHandlesNulls(mNetworkId, s.mNetworkId)
- && equalsHandlesNulls(mSystemId, s.mSystemId)
- && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator)
- && equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
- s.mCdmaDefaultRoamingIndicator)
- && mIsEmergencyOnly == s.mIsEmergencyOnly
- && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw)
- && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw)
- && (mNetworkRegistrationInfos == null
- ? s.mNetworkRegistrationInfos == null : s.mNetworkRegistrationInfos != null
- && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos))
- && mNrFrequencyRange == s.mNrFrequencyRange;
+ synchronized (mNetworkRegistrationInfos) {
+ return mVoiceRegState == s.mVoiceRegState
+ && mDataRegState == s.mDataRegState
+ && mIsManualNetworkSelection == s.mIsManualNetworkSelection
+ && mChannelNumber == s.mChannelNumber
+ && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
+ && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
+ && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
+ && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
+ && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
+ && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
+ && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
+ && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
+ && equalsHandlesNulls(mNetworkId, s.mNetworkId)
+ && equalsHandlesNulls(mSystemId, s.mSystemId)
+ && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator)
+ && equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
+ s.mCdmaDefaultRoamingIndicator)
+ && mIsEmergencyOnly == s.mIsEmergencyOnly
+ && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw)
+ && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw)
+ && mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size()
+ && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)
+ && mNrFrequencyRange == s.mNrFrequencyRange;
+ }
}
/**
@@ -1005,36 +1013,38 @@ public class ServiceState implements Parcelable {
@Override
public String toString() {
- return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
- .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
- .append(", mDataRegState=").append(mDataRegState)
- .append("(" + rilServiceStateToString(mDataRegState) + ")")
- .append(", mChannelNumber=").append(mChannelNumber)
- .append(", duplexMode()=").append(getDuplexMode())
- .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
- .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
- .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
- .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
- .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
- .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
- .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
- .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology())
- .append("(" + rilRadioTechnologyToString(getRilVoiceRadioTechnology()) + ")")
- .append(", getRilDataRadioTechnology=").append(getRilDataRadioTechnology())
- .append("(" + rilRadioTechnologyToString(getRilDataRadioTechnology()) + ")")
- .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
- .append(", mNetworkId=").append(mNetworkId)
- .append(", mSystemId=").append(mSystemId)
- .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
- .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
- .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
- .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation())
- .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
- .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos)
- .append(", mNrFrequencyRange=").append(mNrFrequencyRange)
- .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
- .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
- .append("}").toString();
+ synchronized (mNetworkRegistrationInfos) {
+ return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
+ .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
+ .append(", mDataRegState=").append(mDataRegState)
+ .append("(" + rilServiceStateToString(mDataRegState) + ")")
+ .append(", mChannelNumber=").append(mChannelNumber)
+ .append(", duplexMode()=").append(getDuplexMode())
+ .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
+ .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
+ .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
+ .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
+ .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
+ .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
+ .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
+ .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology())
+ .append("(" + rilRadioTechnologyToString(getRilVoiceRadioTechnology()) + ")")
+ .append(", getRilDataRadioTechnology=").append(getRilDataRadioTechnology())
+ .append("(" + rilRadioTechnologyToString(getRilDataRadioTechnology()) + ")")
+ .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
+ .append(", mNetworkId=").append(mNetworkId)
+ .append(", mSystemId=").append(mSystemId)
+ .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
+ .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
+ .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
+ .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation())
+ .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+ .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos)
+ .append(", mNrFrequencyRange=").append(mNrFrequencyRange)
+ .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
+ .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
+ .append("}").toString();
+ }
}
private void init() {
@@ -1060,17 +1070,19 @@ public class ServiceState implements Parcelable {
mIsEmergencyOnly = false;
mLteEarfcnRsrpBoost = 0;
mNrFrequencyRange = FREQUENCY_RANGE_UNKNOWN;
- mNetworkRegistrationInfos.clear();
- addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
- .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
- .build());
- addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
- .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
- .build());
+ synchronized (mNetworkRegistrationInfos) {
+ mNetworkRegistrationInfos.clear();
+ addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+ .build());
+ addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+ .build());
+ }
mOperatorAlphaLongRaw = null;
mOperatorAlphaShortRaw = null;
}
@@ -1913,10 +1925,13 @@ public class ServiceState implements Parcelable {
*/
public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
ServiceState state = new ServiceState(this);
- if (state.mNetworkRegistrationInfos != null) {
- state.mNetworkRegistrationInfos = state.mNetworkRegistrationInfos.stream()
- .map(NetworkRegistrationInfo::sanitizeLocationInfo)
- .collect(Collectors.toList());
+ synchronized (state.mNetworkRegistrationInfos) {
+ List<NetworkRegistrationInfo> networkRegistrationInfos =
+ state.mNetworkRegistrationInfos.stream()
+ .map(NetworkRegistrationInfo::sanitizeLocationInfo)
+ .collect(Collectors.toList());
+ state.mNetworkRegistrationInfos.clear();
+ state.mNetworkRegistrationInfos.addAll(networkRegistrationInfos);
}
if (!removeCoarseLocation) return state;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index c57f9e63f9db..f31ac2ea5f35 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -173,6 +173,11 @@ public class SubscriptionInfo implements Parcelable {
private ParcelUuid mGroupUUID;
/**
+ * A package name that specifies who created the group. Null if mGroupUUID is null.
+ */
+ private String mGroupOwner;
+
+ /**
* Whether group of the subscription is disabled.
* This is only useful if it's a grouped opportunistic subscription. In this case, if all
* primary (non-opportunistic) subscriptions in the group are deactivated (unplugged pSIM
@@ -203,9 +208,10 @@ public class SubscriptionInfo implements Parcelable {
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardString) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
- false, null, TelephonyManager.UNKNOWN_CARRIER_ID,
- SubscriptionManager.PROFILE_CLASS_DEFAULT);
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
+ false, null, false, TelephonyManager.UNKNOWN_CARRIER_ID,
+ SubscriptionManager.PROFILE_CLASS_DEFAULT,
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
}
/**
@@ -219,7 +225,7 @@ public class SubscriptionInfo implements Parcelable {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
isOpportunistic, groupUUID, false, carrierId, profileClass,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
}
/**
@@ -229,8 +235,8 @@ public class SubscriptionInfo implements Parcelable {
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
- boolean isOpportunistic, @Nullable String groupUUID,
- boolean isGroupDisabled, int carrierId, int profileClass, int subType) {
+ boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+ int carrierId, int profileClass, int subType, @Nullable String groupOwner) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -254,6 +260,7 @@ public class SubscriptionInfo implements Parcelable {
this.mCarrierId = carrierId;
this.mProfileClass = profileClass;
this.mSubscriptionType = subType;
+ this.mGroupOwner = groupOwner;
}
/**
@@ -500,6 +507,15 @@ public class SubscriptionInfo implements Parcelable {
}
/**
+ * Return owner package of group the subscription belongs to.
+ *
+ * @hide
+ */
+ public @Nullable String getGroupOwner() {
+ return mGroupOwner;
+ }
+
+ /**
* @return the profile class of this subscription.
* @hide
*/
@@ -646,11 +662,12 @@ public class SubscriptionInfo implements Parcelable {
int subType = source.readInt();
String[] ehplmns = source.readStringArray();
String[] hplmns = source.readStringArray();
+ String groupOwner = source.readString();
SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
countryIso, isEmbedded, accessRules, cardString, cardId, isOpportunistic,
- groupUUID, isGroupDisabled, carrierid, profileClass, subType);
+ groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner);
info.setAssociatedPlmns(ehplmns, hplmns);
return info;
}
@@ -688,6 +705,7 @@ public class SubscriptionInfo implements Parcelable {
dest.writeInt(mSubscriptionType);
dest.writeStringArray(mEhplmns);
dest.writeStringArray(mHplmns);
+ dest.writeString(mGroupOwner);
}
@Override
@@ -727,7 +745,8 @@ public class SubscriptionInfo implements Parcelable {
+ " profileClass=" + mProfileClass
+ " ehplmns = " + Arrays.toString(mEhplmns)
+ " hplmns = " + Arrays.toString(mHplmns)
- + " subscriptionType=" + mSubscriptionType + "}";
+ + " subscriptionType=" + mSubscriptionType
+ + " mGroupOwner=" + mGroupOwner + "}";
}
@Override
@@ -735,7 +754,7 @@ public class SubscriptionInfo implements Parcelable {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc,
mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
- mIsGroupDisabled, mCarrierId, mProfileClass);
+ mIsGroupDisabled, mCarrierId, mProfileClass, mGroupOwner);
}
@Override
@@ -767,6 +786,7 @@ public class SubscriptionInfo implements Parcelable {
&& Objects.equals(mCountryIso, toCompare.mCountryIso)
&& Objects.equals(mCardString, toCompare.mCardString)
&& Objects.equals(mCardId, toCompare.mCardId)
+ && Objects.equals(mGroupOwner, toCompare.mGroupOwner)
&& TextUtils.equals(mDisplayName, toCompare.mDisplayName)
&& TextUtils.equals(mCarrierName, toCompare.mCarrierName)
&& Arrays.equals(mAccessRules, toCompare.mAccessRules)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 32105ad52753..addd9e0591b0 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -396,6 +396,12 @@ public class SubscriptionManager {
public static final int NAME_SOURCE_USER_INPUT = 2;
/**
+ * The name_source is carrier (carrier app, carrier config, etc.)
+ * @hide
+ */
+ public static final int NAME_SOURCE_CARRIER = 3;
+
+ /**
* TelephonyProvider column name for the color of a SIM.
* <P>Type: INTEGER (int)</P>
*/
@@ -686,6 +692,15 @@ public class SubscriptionManager {
* @hide
*/
public static final String GROUP_UUID = "group_uuid";
+
+ /**
+ * TelephonyProvider column name for group owner. It's the package name who created
+ * the subscription group.
+ *
+ * @hide
+ */
+ public static final String GROUP_OWNER = "group_owner";
+
/**
* TelephonyProvider column name for whether a subscription is metered or not, that is, whether
* the network it connects to charges for subscription or not. For example, paid CBRS or unpaid.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 328a0a7a9512..dab1e6f4abde 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7475,7 +7475,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.checkCarrierPrivilegesForPackage(pkgName);
+ return telephony.checkCarrierPrivilegesForPackage(getSubId(), pkgName);
} catch (RemoteException ex) {
Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex);
} catch (NullPointerException ex) {
@@ -7526,7 +7526,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getPackagesWithCarrierPrivileges();
+ return telephony.getPackagesWithCarrierPrivileges(getPhoneId());
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getPackagesWithCarrierPrivileges RemoteException", ex);
@@ -7537,6 +7537,22 @@ public class TelephonyManager {
}
/** @hide */
+ public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getPackagesWithCarrierPrivilegesForAllPhones();
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones NPE", ex);
+ }
+ return Collections.EMPTY_LIST;
+ }
+
+
+ /** @hide */
@SystemApi
@SuppressLint("Doclava125")
public void dial(String number) {
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index a86fda4454d7..a78bae4514fb 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1273,6 +1273,23 @@ public class ApnSetting implements Parcelable {
}
/**
+ * Get supported APN types
+ *
+ * @return list of APN types
+ * @hide
+ */
+ @ApnType
+ public List<Integer> getApnTypes() {
+ List<Integer> types = new ArrayList<>();
+ for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+ if ((mApnTypeBitmask & type) == type) {
+ types.add(type);
+ }
+ }
+ return types;
+ }
+
+ /**
* @param apnTypeBitmask bitmask of APN types.
* @return comma delimited list of APN types.
* @hide
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e8ce2b457ab4..68fd9ac8d6bd 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1000,7 +1000,7 @@ interface ITelephony {
/**
* Similar to above, but check for the package whose name is pkgName.
*/
- int checkCarrierPrivilegesForPackage(String pkgName);
+ int checkCarrierPrivilegesForPackage(int subId, String pkgName);
/**
* Similar to above, but check across all phones.
@@ -1357,9 +1357,14 @@ interface ITelephony {
in PhoneAccountHandle phoneAccountHandle, boolean enabled);
/**
- * Returns a list of packages that have carrier privileges.
+ * Returns a list of packages that have carrier privileges for the specific phone.
*/
- List<String> getPackagesWithCarrierPrivileges();
+ List<String> getPackagesWithCarrierPrivileges(int phoneId);
+
+ /**
+ * Returns a list of packages that have carrier privileges.
+ */
+ List<String> getPackagesWithCarrierPrivilegesForAllPhones();
/**
* Return the application ID for the app type.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 64c1830c5e5c..2552f045eedf 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -209,18 +209,9 @@ public final class TelephonyPermissions {
context.enforcePermission(
android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
} catch (SecurityException phoneStateException) {
- SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
- Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- int[] activeSubIds = sm.getActiveSubscriptionIdList();
- for (int activeSubId : activeSubIds) {
- // If we don't have the runtime permission, but do have carrier privileges, that
- // suffices for reading phone state.
- if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- return true;
- }
- }
- return false;
+ // If we don't have the runtime permission, but do have carrier privileges, that
+ // suffices for reading phone state.
+ return checkCarrierPrivilegeForAnySubId(context, telephonySupplier, uid);
}
}
@@ -236,9 +227,9 @@ public final class TelephonyPermissions {
*
* <p>This method behaves in one of the following ways:
* <ul>
- * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the
- * calling package passes a DevicePolicyManager Device Owner / Profile Owner device
- * identifier access check,
+ * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the calling
+ * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier
+ * access check, or the calling package has carrier privileges.
* <li>throw SecurityException: if the caller does not meet any of the requirements and is
* targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission.
* <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE
@@ -258,9 +249,9 @@ public final class TelephonyPermissions {
*
* <p>This method behaves in one of the following ways:
* <ul>
- * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the
- * calling package passes a DevicePolicyManager Device Owner / Profile Owner device
- * identifier access check,
+ * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the calling
+ * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier
+ * access check, or the calling package has carrier privileges.
* <li>throw SecurityException: if the caller does not meet any of the requirements and is
* targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission
* or carrier privileges.
@@ -272,23 +263,8 @@ public final class TelephonyPermissions {
*/
public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, int subId,
String callingPackage, String message) {
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
- // if the device identifier check completes successfully then grant access.
- if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) {
- return true;
- }
- // Calling packages with carrier privileges will also have access to device identifiers, but
- // this may be removed in a future release.
- if (checkCarrierPrivilegeForSubId(subId)) {
- return true;
- }
- // else the calling package is not authorized to access the device identifiers; call
- // a central method to report the failure based on the target SDK and if the calling package
- // has the READ_PHONE_STATE permission or carrier privileges that were previously required
- // to access the identifiers.
- return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
- message);
+ return checkReadDeviceIdentifiers(context, TELEPHONY_SUPPLIER, subId,
+ Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
}
/**
@@ -298,7 +274,7 @@ public final class TelephonyPermissions {
* <ul>
* <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the calling
* package passes a DevicePolicyManager Device Owner / Profile Owner device identifier
- * access check, or the calling package has carrier privleges.
+ * access check, or the calling package has carrier privileges.
* <li>throw SecurityException: if the caller does not meet any of the requirements and is
* targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission.
* <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE
@@ -309,19 +285,8 @@ public final class TelephonyPermissions {
*/
public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
String callingPackage, String message) {
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
- // if the device identifiers can be read then grant access to the subscriber identifiers
- if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) {
- return true;
- }
- // If the calling package has carrier privileges then allow access to the subscriber
- // identifiers.
- if (checkCarrierPrivilegeForSubId(subId)) {
- return true;
- }
- return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
- message);
+ return checkReadDeviceIdentifiers(context, TELEPHONY_SUPPLIER, subId,
+ Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
}
/**
@@ -331,8 +296,10 @@ public final class TelephonyPermissions {
* package passes a DevicePolicyManager Device Owner / Profile Owner device identifier access
* check.
*/
- private static boolean checkReadDeviceIdentifiers(Context context, int pid, int uid,
- String callingPackage) {
+ @VisibleForTesting
+ public static boolean checkReadDeviceIdentifiers(Context context,
+ Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
+ String callingPackage, String message) {
// Allow system and root access to the device identifiers.
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
@@ -343,31 +310,36 @@ public final class TelephonyPermissions {
uid) == PackageManager.PERMISSION_GRANTED) {
return true;
}
- // if the calling package is null then return now as there's no way to perform the
- // DevicePolicyManager device / profile owner and AppOp checks
- if (callingPackage == null) {
- return false;
+ // If the calling package has carrier privileges for any subscription then allow access.
+ if (checkCarrierPrivilegeForAnySubId(context, telephonySupplier, uid)) {
+ return true;
}
- // Allow access to an app that has been granted the READ_DEVICE_IDENTIFIERS app op.
- long token = Binder.clearCallingIdentity();
- AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(
- Context.APP_OPS_SERVICE);
- try {
- if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid,
- callingPackage) == AppOpsManager.MODE_ALLOWED) {
+ // if the calling package is not null then perform the DevicePolicyManager device /
+ // profile owner and Appop checks.
+ if (callingPackage != null) {
+ // Allow access to an app that has been granted the READ_DEVICE_IDENTIFIERS app op.
+ long token = Binder.clearCallingIdentity();
+ AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(
+ Context.APP_OPS_SERVICE);
+ try {
+ if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid,
+ callingPackage) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ // Allow access to a device / profile owner app.
+ DevicePolicyManager devicePolicyManager =
+ (DevicePolicyManager) context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccess(
+ callingPackage, pid, uid)) {
return true;
}
- } finally {
- Binder.restoreCallingIdentity(token);
}
- // Allow access to a device / profile owner app.
- DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccess(
- callingPackage, pid, uid)) {
- return true;
- }
- return false;
+ return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
+ message);
}
/**
@@ -403,10 +375,12 @@ public final class TelephonyPermissions {
try {
callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
callingPackage, 0, UserHandle.getUserId(uid));
- if (callingPackageInfo.isSystemApp()) {
- isPreinstalled = true;
- if (callingPackageInfo.isPrivilegedApp()) {
- isPrivApp = true;
+ if (callingPackageInfo != null) {
+ if (callingPackageInfo.isSystemApp()) {
+ isPreinstalled = true;
+ if (callingPackageInfo.isPrivilegedApp()) {
+ isPrivApp = true;
+ }
}
}
} catch (PackageManager.NameNotFoundException e) {
@@ -651,6 +625,26 @@ public final class TelephonyPermissions {
}
}
+ /**
+ * Returns whether the provided uid has carrier privileges for any active subscription ID.
+ */
+ private static boolean checkCarrierPrivilegeForAnySubId(Context context,
+ Supplier<ITelephony> telephonySupplier, int uid) {
+ SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int[] activeSubIds = sm.getActiveSubscriptionIdList();
+ if (activeSubIds != null) {
+ for (int activeSubId : activeSubIds) {
+ if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
private static int getCarrierPrivilegeStatus(
Supplier<ITelephony> telephonySupplier, int subId, int uid) {
ITelephony telephony = telephonySupplier.get();
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 07525a6ea49c..db1ccb446ce3 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -24,6 +24,7 @@ java_library {
"frameworks-net-testutils",
"junit",
"mockito-target-minus-junit4",
+ "platform-test-annotations",
],
libs: [
"android.test.base.stubs",
diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java
index bef66b27df62..38bc744a0a06 100644
--- a/tests/net/common/java/android/net/NetworkTest.java
+++ b/tests/net/common/java/android/net/NetworkTest.java
@@ -25,6 +25,7 @@ import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.Network;
+import android.platform.test.annotations.AppModeFull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -74,6 +75,7 @@ public class NetworkTest {
}
@Test
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testBindSocketOfConnectedDatagramSocketThrows() throws Exception {
final DatagramSocket mDgramSocket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY);
mDgramSocket.connect((InetAddress) Inet6Address.LOOPBACK, 53);
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
index 8ff2de9777c9..6e69b34fe474 100644
--- a/tests/net/java/android/net/IpMemoryStoreTest.java
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -111,13 +111,12 @@ public class IpMemoryStoreTest {
@Test
public void testNetworkAttributes() throws Exception {
- startIpMemoryStore(true);
+ startIpMemoryStore(true /* supplyService */);
final String l2Key = "fakeKey";
mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
- status -> {
- assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
- });
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key),
mNapCaptor.capture(), any());
assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
@@ -135,7 +134,7 @@ public class IpMemoryStoreTest {
@Test
public void testPrivateData() throws RemoteException {
- startIpMemoryStore(true);
+ startIpMemoryStore(true /* supplyService */);
final Blob b = new Blob();
b.data = TEST_BLOB_DATA;
final String l2Key = "fakeKey";
@@ -162,7 +161,7 @@ public class IpMemoryStoreTest {
@Test
public void testFindL2Key()
throws UnknownHostException, RemoteException, Exception {
- startIpMemoryStore(true);
+ startIpMemoryStore(true /* supplyService */);
final String l2Key = "fakeKey";
mStore.findL2Key(TEST_NETWORK_ATTRIBUTES,
@@ -177,7 +176,7 @@ public class IpMemoryStoreTest {
@Test
public void testIsSameNetwork() throws UnknownHostException, RemoteException {
- startIpMemoryStore(true);
+ startIpMemoryStore(true /* supplyService */);
final String l2Key1 = "fakeKey1";
final String l2Key2 = "fakeKey2";
@@ -193,7 +192,7 @@ public class IpMemoryStoreTest {
@Test
public void testEnqueuedIpMsRequests() throws Exception {
- startIpMemoryStore(false);
+ startIpMemoryStore(false /* supplyService */);
final Blob b = new Blob();
b.data = TEST_BLOB_DATA;
@@ -201,9 +200,8 @@ public class IpMemoryStoreTest {
// enqueue multiple ipms requests
mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
- status -> {
- assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
- });
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
mStore.retrieveNetworkAttributes(l2Key,
(status, key, attr) -> {
assertTrue("Retrieve network attributes not successful : "
@@ -212,9 +210,8 @@ public class IpMemoryStoreTest {
assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
});
mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
- status -> {
- assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
- });
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
(status, key, name, data) -> {
assertTrue("Retrieve blob status not successful : " + status.resultCode,
@@ -240,7 +237,7 @@ public class IpMemoryStoreTest {
@Test
public void testEnqueuedIpMsRequestsWithException() throws Exception {
- startIpMemoryStore(true);
+ startIpMemoryStore(true /* supplyService */);
doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any());
final Blob b = new Blob();
@@ -249,9 +246,8 @@ public class IpMemoryStoreTest {
// enqueue multiple ipms requests
mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
- status -> {
- assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
- });
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
mStore.retrieveNetworkAttributes(l2Key,
(status, key, attr) -> {
assertTrue("Retrieve network attributes not successful : "
@@ -260,9 +256,8 @@ public class IpMemoryStoreTest {
assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
});
mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
- status -> {
- assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
- });
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
(status, key, name, data) -> {
assertTrue("Retrieve blob status not successful : " + status.resultCode,
@@ -286,7 +281,7 @@ public class IpMemoryStoreTest {
@Test
public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception {
- startIpMemoryStore(true);
+ startIpMemoryStore(true /* supplyService */);
final Blob b = new Blob();
b.data = TEST_BLOB_DATA;
@@ -294,9 +289,8 @@ public class IpMemoryStoreTest {
// enqueue multiple ipms requests
mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
- status -> {
- assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
- });
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
mStore.retrieveNetworkAttributes(l2Key,
(status, key, attr) -> {
throw new RuntimeException("retrieveNetworkAttributes test");
@@ -320,6 +314,7 @@ public class IpMemoryStoreTest {
inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(),
any());
+ inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any());
inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
eq(b), any());
inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 62a471896579..df1f57f7a011 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -524,7 +524,7 @@ public class PermissionMonitorTest {
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET);
- netdPermissionsAppIds.put(MOCK_UID2, INetd.NO_PERMISSIONS);
+ netdPermissionsAppIds.put(MOCK_UID2, INetd.PERMISSION_NONE);
netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET
| INetd.PERMISSION_UPDATE_DEVICE_STATS);
netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS);
@@ -534,7 +534,7 @@ public class PermissionMonitorTest {
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET,
new int[]{MOCK_UID1});
- mNetdServiceMonitor.expectPermission(INetd.NO_PERMISSIONS, new int[]{MOCK_UID2});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID2});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
| INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS,
@@ -553,8 +553,8 @@ public class PermissionMonitorTest {
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2});
// Revoke permission from SYSTEM_UID1, expect no permission stored.
- mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.NO_PERMISSIONS);
- mNetdServiceMonitor.expectPermission(INetd.NO_PERMISSIONS, new int[]{SYSTEM_UID1});
+ mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.PERMISSION_NONE);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1});
}
private PackageInfo addPackage(String packageName, int uid, String[] permissions)
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index c1fcf5cad240..27ffcdf52168 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -44,6 +44,7 @@ enum {
SDK_O = 26,
SDK_O_MR1 = 27,
SDK_P = 28,
+ SDK_Q = 29,
};
#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 9fa29f25c29c..580daabbd577 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -54,6 +54,7 @@ enum : ApiVersion {
SDK_O = 26,
SDK_O_MR1 = 27,
SDK_P = 28,
+ SDK_Q = 29,
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index f191f6c0ab78..6683e2abb0da 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -490,11 +490,6 @@ def remove_emoji_exclude(source, items):
def flag_sequence(territory_code):
return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code)
-UNSUPPORTED_FLAGS = frozenset({
- flag_sequence('BL'), flag_sequence('BQ'), flag_sequence('MQ'),
- flag_sequence('RE'), flag_sequence('TF'),
-})
-
EQUIVALENT_FLAGS = {
flag_sequence('BV'): flag_sequence('NO'),
flag_sequence('CP'): flag_sequence('FR'),
@@ -600,9 +595,6 @@ def compute_expected_emoji():
for first, second in SAME_FLAG_MAPPINGS:
equivalent_emoji[first] = second
- # Remove unsupported flags
- all_sequences.difference_update(UNSUPPORTED_FLAGS)
-
# Add all tag characters used in flags
sequence_pieces.update(range(0xE0030, 0xE0039 + 1))
sequence_pieces.update(range(0xE0061, 0xE007A + 1))
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 0b1108d56b7f..c0c0361dd92f 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -160,6 +160,11 @@ public class ScanResult implements Parcelable {
public static final int KEY_MGMT_FT_SAE = 11;
/**
* @hide
+ * Security key management scheme: OWE in transition mode.
+ */
+ public static final int KEY_MGMT_OWE_TRANSITION = 12;
+ /**
+ * @hide
* No cipher suite.
*/
public static final int CIPHER_NONE = 0;