summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp20
-rw-r--r--apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java10
-rw-r--r--api/current.txt88
-rw-r--r--api/system-current.txt40
-rw-r--r--cmds/statsd/Android.mk23
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h1
-rw-r--r--cmds/statsd/src/dimension.h4
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp3
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h1
-rw-r--r--cmds/statsd/src/packages/UidMap.cpp5
-rw-r--r--cmds/statsd/src/stats_log_util.h13
-rw-r--r--cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp193
-rw-r--r--cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp59
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp8
-rw-r--r--cmds/statsd/tests/statsd_test_util.h3
-rw-r--r--core/java/android/app/Activity.java78
-rw-r--r--core/java/android/app/ActivityManagerInternal.java10
-rw-r--r--core/java/android/app/ActivityOptions.java33
-rw-r--r--core/java/android/app/ActivityThread.java61
-rw-r--r--core/java/android/app/ClientTransactionHandler.java8
-rw-r--r--core/java/android/app/IActivityManager.aidl6
-rw-r--r--core/java/android/app/ProfilerInfo.java15
-rw-r--r--core/java/android/app/admin/ConnectEvent.java2
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java25
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java147
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java9
-rw-r--r--core/java/android/app/admin/DnsEvent.java2
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl7
-rw-r--r--core/java/android/app/backup/BackupManager.java24
-rw-r--r--core/java/android/app/backup/BackupTransport.java24
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl3
-rw-r--r--core/java/android/app/job/JobParameters.java14
-rw-r--r--core/java/android/app/servertransaction/ActivityLifecycleItem.java38
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java9
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java2
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java2
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java2
-rw-r--r--core/java/android/app/servertransaction/StopActivityItem.java2
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java22
-rw-r--r--core/java/android/app/slice/Slice.java74
-rw-r--r--core/java/android/app/slice/SliceManager.java134
-rw-r--r--core/java/android/app/slice/SliceProvider.java58
-rw-r--r--core/java/android/content/pm/PackageManager.java29
-rw-r--r--core/java/android/content/pm/PackageParser.java25
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java5
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java62
-rw-r--r--core/java/android/net/IIpSecService.aidl6
-rw-r--r--core/java/android/net/IpSecAlgorithm.java8
-rw-r--r--core/java/android/net/IpSecConfig.java187
-rw-r--r--core/java/android/net/IpSecManager.java154
-rw-r--r--core/java/android/net/IpSecTransform.java155
-rw-r--r--core/java/android/net/MacAddress.java27
-rw-r--r--core/java/android/net/Network.java6
-rw-r--r--core/java/android/net/NetworkPolicyManager.java5
-rw-r--r--core/java/android/net/metrics/WakeupStats.java2
-rw-r--r--core/java/android/os/UserManager.java14
-rw-r--r--core/java/android/os/storage/StorageVolume.java28
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/java/android/security/keystore/KeychainProtectionParameter.aidl (renamed from core/java/android/security/keystore/RecoveryData.aidl)2
-rw-r--r--core/java/android/security/keystore/KeychainProtectionParameter.java (renamed from core/java/android/security/keystore/RecoveryMetadata.java)46
-rw-r--r--core/java/android/security/keystore/KeychainSnapshot.aidl (renamed from core/java/android/security/keystore/RecoveryMetadata.aidl)2
-rw-r--r--core/java/android/security/keystore/KeychainSnapshot.java (renamed from core/java/android/security/keystore/RecoveryData.java)83
-rw-r--r--core/java/android/security/keystore/RecoveryManager.java35
-rw-r--r--core/java/android/security/keystore/WrappedApplicationKey.aidl (renamed from core/java/android/security/keystore/EntryRecoveryData.aidl)2
-rw-r--r--core/java/android/security/keystore/WrappedApplicationKey.java (renamed from core/java/android/security/keystore/EntryRecoveryData.java)28
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java7
-rw-r--r--core/java/android/text/Layout.java4
-rw-r--r--core/java/android/text/MeasuredParagraph.java677
-rw-r--r--core/java/android/text/MeasuredText.java738
-rw-r--r--core/java/android/text/PremeasuredText.java272
-rw-r--r--core/java/android/text/StaticLayout.java51
-rw-r--r--core/java/android/text/TextUtils.java16
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/StatsManager.java29
-rw-r--r--core/java/android/util/TimeUtils.java98
-rw-r--r--core/java/android/view/IRemoteAnimationFinishedCallback.aidl27
-rw-r--r--core/java/android/view/IRemoteAnimationRunner.aidl44
-rw-r--r--core/java/android/view/IWindowManager.aidl7
-rw-r--r--core/java/android/view/RemoteAnimationAdapter.aidl19
-rw-r--r--core/java/android/view/RemoteAnimationAdapter.java108
-rw-r--r--core/java/android/view/RemoteAnimationDefinition.aidl19
-rw-r--r--core/java/android/view/RemoteAnimationDefinition.java93
-rw-r--r--core/java/android/view/RemoteAnimationTarget.aidl19
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java151
-rw-r--r--core/java/android/view/View.java89
-rw-r--r--core/java/android/view/ViewRootImpl.java19
-rw-r--r--core/java/android/view/WindowManager.java328
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java5
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java36
-rw-r--r--core/java/android/view/autofill/AutofillManager.java157
-rw-r--r--core/java/android/view/autofill/AutofillPopupWindow.java4
-rw-r--r--core/java/android/view/inputmethod/ExtractedText.java4
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java113
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java50
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java20
-rw-r--r--core/java/android/webkit/WebViewFactory.java3
-rw-r--r--core/java/android/widget/Editor.java17
-rw-r--r--core/java/android/widget/Magnifier.java41
-rw-r--r--core/java/android/widget/TextView.java8
-rw-r--r--core/java/android/widget/VideoView2.java363
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java181
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java4
-rw-r--r--core/java/com/android/internal/policy/KeyguardDismissCallback.java41
-rw-r--r--core/java/com/android/internal/print/DualDumpOutputStream.java276
-rw-r--r--core/java/com/android/internal/print/DumpUtils.java195
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl14
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp20
-rw-r--r--core/jni/android/graphics/ImageDecoder.cpp64
-rw-r--r--core/jni/android_text_MeasuredParagraph.cpp (renamed from core/jni/android_text_MeasuredText.cpp)22
-rw-r--r--core/jni/android_text_StaticLayout.cpp2
-rw-r--r--core/proto/android/providers/settings.proto3
-rw-r--r--core/proto/android/server/forceappstandbytracker.proto9
-rw-r--r--core/proto/android/view/windowlayoutparams.proto1
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/layout/autofill_dataset_picker.xml2
-rw-r--r--core/res/res/layout/notification_template_material_base.xml4
-rw-r--r--core/res/res/layout/notification_template_material_big_base.xml6
-rw-r--r--core/res/res/layout/notification_template_material_big_picture.xml6
-rw-r--r--core/res/res/layout/notification_template_material_big_text.xml6
-rw-r--r--core/res/res/layout/notification_template_material_inbox.xml6
-rw-r--r--core/res/res/layout/notification_template_material_messaging.xml4
-rw-r--r--core/res/res/layout/notification_template_smart_reply_container.xml24
-rw-r--r--core/res/res/values/colors_material.xml6
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java2
-rw-r--r--core/tests/coretests/src/android/text/MeasuredParagraphTest.java (renamed from core/tests/coretests/src/android/text/MeasuredTextTest.java)24
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java7
-rw-r--r--graphics/java/android/graphics/drawable/RippleBackground.java9
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java12
-rw-r--r--media/java/android/media/update/ApiLoader.java4
-rw-r--r--media/java/android/media/update/StaticProvider.java2
-rw-r--r--media/java/android/media/update/VideoView2Provider.java66
-rw-r--r--native/android/net.c2
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java60
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java6
-rw-r--r--packages/SettingsProvider/AndroidManifest.xml1
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java23
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java19
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java4
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java7
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml11
-rw-r--r--packages/SystemUI/res/drawable/ic_face_unlock.xml27
-rw-r--r--packages/SystemUI/res/drawable/qs_background_primary.xml1
-rw-r--r--packages/SystemUI/res/drawable/smart_reply_button_background.xml27
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml2
-rw-r--r--packages/SystemUI/res/layout/smart_reply_button.xml32
-rw-r--r--packages/SystemUI/res/layout/smart_reply_view.xml27
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml12
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml146
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_row.xml92
-rw-r--r--packages/SystemUI/res/values/colors.xml5
-rw-r--r--packages/SystemUI/res/values/dimens.xml10
-rw-r--r--packages/SystemUI/res/values/strings.xml17
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java25
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java34
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java71
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java35
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java22
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java59
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java27
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java108
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java38
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/ChargingView.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/RoundedCorners.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/Estimate.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java370
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java127
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java265
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java187
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java28
-rw-r--r--proto/src/metrics_constants.proto9
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/SaveUi.java3
-rw-r--r--services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java25
-rw-r--r--services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java154
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java4
-rw-r--r--services/backup/java/com/android/server/backup/TransportManager.java822
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformBackupTask.java10
-rw-r--r--services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java33
-rw-r--r--services/backup/java/com/android/server/backup/transport/TransportClient.java47
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/BatteryService.java9
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java12
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java249
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java6
-rw-r--r--services/core/java/com/android/server/EventLogTags.logtags1
-rw-r--r--services/core/java/com/android/server/ForceAppStandbyTracker.java167
-rw-r--r--services/core/java/com/android/server/IpSecService.java338
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java88
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java20
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java26
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java19
-rw-r--r--services/core/java/com/android/server/am/KeyguardController.java14
-rw-r--r--services/core/java/com/android/server/am/UserController.java45
-rw-r--r--services/core/java/com/android/server/am/UserSwitchingDialog.java17
-rw-r--r--services/core/java/com/android/server/content/SyncJobService.java11
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java9
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java15
-rw-r--r--services/core/java/com/android/server/job/JobServiceContext.java9
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java122
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java31
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java28
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java2
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java8
-rw-r--r--services/core/java/com/android/server/media/MediaUpdateService.java151
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java19
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java65
-rw-r--r--services/core/java/com/android/server/pm/Installer.java5
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java1
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java132
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java29
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java5
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java21
-rw-r--r--services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java1
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java75
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java10
-rw-r--r--services/core/java/com/android/server/policy/WindowOrientationListener.java24
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java12
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java161
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerController.java20
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java77
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java2
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java2
-rw-r--r--services/core/java/com/android/server/wm/DragState.java27
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java13
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java206
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java33
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java32
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java197
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java17
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java249
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/print/java/com/android/server/print/PrintManagerService.java32
-rw-r--r--services/print/java/com/android/server/print/RemotePrintService.java46
-rw-r--r--services/print/java/com/android/server/print/RemotePrintSpooler.java36
-rw-r--r--services/print/java/com/android/server/print/UserState.java198
-rw-r--r--services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java468
-rw-r--r--services/robotests/src/com/android/server/backup/TransportManagerTest.java836
-rw-r--r--services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java222
-rw-r--r--services/robotests/src/com/android/server/backup/testing/ShadowBackupPolicyEnforcer.java24
-rw-r--r--services/robotests/src/com/android/server/backup/testing/TestUtils.java68
-rw-r--r--services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java64
-rw-r--r--services/robotests/src/com/android/server/backup/testing/TransportData.java149
-rw-r--r--services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java196
-rw-r--r--services/robotests/src/com/android/server/backup/transport/TransportClientTest.java84
-rw-r--r--services/robotests/src/com/android/server/testing/ShadowEventLog.java71
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java37
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java (renamed from services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java)12
-rw-r--r--services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java116
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java48
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java130
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java137
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java10
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java174
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java18
-rw-r--r--telephony/java/android/provider/Telephony.java29
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java5
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java49
-rw-r--r--telephony/java/android/telephony/data/DataService.java540
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java172
-rw-r--r--telephony/java/android/telephony/data/IDataService.aidl39
-rw-r--r--telephony/java/android/telephony/data/IDataServiceCallback.aidl33
-rw-r--r--telephony/java/android/telephony/euicc/EuiccCardManager.java340
-rw-r--r--telephony/java/android/telephony/euicc/EuiccNotification.aidl19
-rw-r--r--telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl19
-rw-r--r--telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java (renamed from telephony/java/android/telephony/euicc/EuiccRat.java)27
-rw-r--r--telephony/java/android/telephony/ims/ImsService.java19
-rw-r--r--telephony/java/android/telephony/ims/feature/ImsFeature.java2
-rw-r--r--telephony/java/android/telephony/ims/internal/ImsService.java4
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl1
-rw-r--r--telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl2
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java2
-rw-r--r--telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java11
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java (renamed from telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java)56
-rw-r--r--telephony/java/com/android/ims/internal/IImsRegistration.aidl (renamed from telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl)5
-rw-r--r--telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl (renamed from telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl)4
-rw-r--r--telephony/java/com/android/ims/internal/IImsServiceController.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl7
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl6
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyIntents.java6
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl21
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl21
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl33
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl21
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl21
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl21
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl23
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl23
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl21
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl21
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl23
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl23
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl23
-rw-r--r--test-base/Android.bp3
-rw-r--r--test-mock/Android.bp1
-rw-r--r--test-runner/Android.bp2
-rw-r--r--tests/net/java/android/net/IpSecConfigTest.java32
-rw-r--r--tests/net/java/android/net/IpSecManagerTest.java25
-rw-r--r--tests/net/java/android/net/MacAddressTest.java4
-rw-r--r--tests/net/java/android/net/NetworkTest.java6
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java242
-rw-r--r--tests/net/java/com/android/server/IpSecServiceParameterizedTest.java179
-rw-r--r--tests/net/java/com/android/server/IpSecServiceTest.java129
-rw-r--r--tools/aapt2/java/ClassDefinition.cpp14
-rw-r--r--tools/aapt2/java/ManifestClassGenerator_test.cpp5
-rw-r--r--tools/stats_log_api_gen/Collation.cpp71
-rw-r--r--tools/stats_log_api_gen/Collation.h6
-rw-r--r--tools/stats_log_api_gen/main.cpp316
369 files changed, 14112 insertions, 5770 deletions
diff --git a/Android.bp b/Android.bp
index e5d4b8b87c88..75dfbb5d0fc3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -324,6 +324,8 @@ java_library {
"core/java/android/view/IOnKeyguardExitResult.aidl",
"core/java/android/view/IPinnedStackController.aidl",
"core/java/android/view/IPinnedStackListener.aidl",
+ "core/java/android/view/IRemoteAnimationRunner.aidl",
+ "core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
"core/java/android/view/IRotationWatcher.aidl",
"core/java/android/view/IWallpaperVisibilityListener.aidl",
"core/java/android/view/IWindow.aidl",
@@ -464,6 +466,8 @@ java_library {
"telecomm/java/com/android/internal/telecom/IInCallService.aidl",
"telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
"telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
+ "telephony/java/android/telephony/data/IDataService.aidl",
+ "telephony/java/android/telephony/data/IDataServiceCallback.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl",
@@ -471,8 +475,6 @@ java_library {
"telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl",
@@ -492,6 +494,8 @@ java_library {
"telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl",
"telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl",
"telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl",
+ "telephony/java/com/android/ims/internal/IImsRegistration.aidl",
+ "telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl",
"telephony/java/com/android/ims/internal/IImsRcsFeature.aidl",
"telephony/java/com/android/ims/internal/IImsService.aidl",
"telephony/java/com/android/ims/internal/IImsServiceController.aidl",
@@ -519,9 +523,21 @@ java_library {
"telephony/java/com/android/internal/telephony/ITelephony.aidl",
"telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
"telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl",
"telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl",
"telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl",
"telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl",
"wifi/java/android/net/wifi/IWifiManager.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 5653a039a9ed..93a0fc314b7f 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -190,7 +190,7 @@ public class StaticLayoutPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -206,7 +206,7 @@ public class StaticLayoutPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -222,7 +222,7 @@ public class StaticLayoutPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -238,7 +238,7 @@ public class StaticLayoutPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -254,7 +254,7 @@ public class StaticLayoutPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
diff --git a/api/current.txt b/api/current.txt
index 4210db73556b..9398d72615f2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6357,6 +6357,7 @@ package android.app.admin {
field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
field public static final java.lang.String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+ field public static final java.lang.String SUPPORT_TRANSFER_OWNERSHIP_META_DATA = "android.app.support_transfer_ownership";
}
public class DeviceAdminService extends android.app.Service {
@@ -6370,7 +6371,7 @@ package android.app.admin {
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
- method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, java.util.concurrent.Executor);
+ method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener);
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
method public deprecated void clearDeviceOwnerApp(java.lang.String);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
@@ -6400,12 +6401,14 @@ package android.app.admin {
method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
+ method public java.lang.CharSequence getEndUserSessionMessage(android.content.ComponentName);
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public java.util.List<java.lang.String> getKeepUninstalledPackages(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public int getLockTaskFeatures(android.content.ComponentName);
method public java.lang.String[] getLockTaskPackages(android.content.ComponentName);
method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
+ method public android.content.ComponentName getMandatoryBackupTransport();
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public int getOrganizationColor(android.content.ComponentName);
@@ -6434,6 +6437,7 @@ package android.app.admin {
method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public java.util.List<android.os.UserHandle> getSecondaryUsers(android.content.ComponentName);
method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName);
+ method public java.lang.CharSequence getStartUserSessionMessage(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -6496,6 +6500,7 @@ package android.app.admin {
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
+ method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
@@ -6505,6 +6510,7 @@ package android.app.admin {
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
method public void setLogoutEnabled(android.content.ComponentName, boolean);
method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
+ method public void setMandatoryBackupTransport(android.content.ComponentName, android.content.ComponentName);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -6538,6 +6544,7 @@ package android.app.admin {
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setSecurityLoggingEnabled(android.content.ComponentName, boolean);
method public void setShortSupportMessage(android.content.ComponentName, java.lang.CharSequence);
+ method public void setStartUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemSetting(android.content.ComponentName, java.lang.String, java.lang.String);
@@ -7069,8 +7076,8 @@ package android.app.slice {
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7156,7 +7163,10 @@ package android.app.slice {
}
public class SliceManager {
+ method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
@@ -7175,7 +7185,7 @@ package android.app.slice {
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public void onSlicePinned(android.net.Uri);
method public void onSliceUnpinned(android.net.Uri);
@@ -11157,7 +11167,7 @@ package android.content.pm {
field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
field public static final java.lang.String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
- field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
+ field public static final deprecated java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
@@ -20885,7 +20895,6 @@ package android.inputmethodservice {
method public int getMaxWidth();
method public java.lang.CharSequence getTextForImeAction(int);
method public android.app.Dialog getWindow();
- method public void hideSoftInputFromInputMethod(int);
method public void hideStatusIcon();
method public void hideWindow();
method public boolean isExtractViewShown();
@@ -20933,6 +20942,7 @@ package android.inputmethodservice {
method public void onWindowHidden();
method public void onWindowShown();
method public void requestHideSelf(int);
+ method public void requestShowSelf(int);
method public boolean sendDefaultEditorAction(boolean);
method public void sendDownUpKeyEvents(int);
method public void sendKeyChar(char);
@@ -20945,7 +20955,6 @@ package android.inputmethodservice {
method public void setInputMethodAndSubtype(java.lang.String, android.view.inputmethod.InputMethodSubtype);
method public void setInputView(android.view.View);
method public boolean shouldOfferSwitchingToNextInputMethod();
- method public void showSoftInputFromInputMethod(int);
method public void showStatusIcon(int);
method public void showWindow(boolean);
method public void switchInputMethod(java.lang.String);
@@ -26221,12 +26230,18 @@ package android.net {
}
public final class IpSecManager {
- method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
- method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public void applyTransportModeTransform(java.net.Socket, int, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.net.DatagramSocket, int, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.io.FileDescriptor, int, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.Socket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.DatagramSocket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.io.FileDescriptor) throws java.io.IOException;
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
}
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
@@ -26249,18 +26264,15 @@ package android.net {
public final class IpSecTransform implements java.lang.AutoCloseable {
method public void close();
- field public static final int DIRECTION_IN = 0; // 0x0
- field public static final int DIRECTION_OUT = 1; // 0x1
}
public static class IpSecTransform.Builder {
ctor public IpSecTransform.Builder(android.content.Context);
- method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(int, android.net.IpSecAlgorithm);
- method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
- method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setAuthentication(android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(android.net.IpSecAlgorithm);
method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
- method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
}
public class LinkAddress implements android.os.Parcelable {
@@ -26343,10 +26355,10 @@ package android.net {
}
public final class MacAddress implements android.os.Parcelable {
- method public int addressType();
method public int describeContents();
method public static android.net.MacAddress fromBytes(byte[]);
method public static android.net.MacAddress fromString(java.lang.String);
+ method public int getAddressType();
method public boolean isLocallyAssigned();
method public byte[] toByteArray();
method public java.lang.String toOuiString();
@@ -26356,7 +26368,6 @@ package android.net {
field public static final int TYPE_BROADCAST = 3; // 0x3
field public static final int TYPE_MULTICAST = 2; // 0x2
field public static final int TYPE_UNICAST = 1; // 0x1
- field public static final int TYPE_UNKNOWN = 0; // 0x0
}
public class MailTo {
@@ -32465,6 +32476,7 @@ package android.os {
field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+ field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
@@ -42299,20 +42311,9 @@ package android.text {
method public boolean isAllowed(char);
}
- public abstract interface NoCopySpan {
- }
-
- public static class NoCopySpan.Concrete implements android.text.NoCopySpan {
- ctor public NoCopySpan.Concrete();
- }
-
- public abstract interface ParcelableSpan implements android.os.Parcelable {
- method public abstract int getSpanTypeId();
- }
-
- public class PremeasuredText implements android.text.Spanned {
- method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic);
- method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int);
+ public class MeasuredText implements android.text.Spanned {
+ method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic);
+ method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int);
method public char charAt(int);
method public int getEnd();
method public android.text.TextPaint getPaint();
@@ -42331,6 +42332,17 @@ package android.text {
method public java.lang.CharSequence subSequence(int, int);
}
+ public abstract interface NoCopySpan {
+ }
+
+ public static class NoCopySpan.Concrete implements android.text.NoCopySpan {
+ ctor public NoCopySpan.Concrete();
+ }
+
+ public abstract interface ParcelableSpan implements android.os.Parcelable {
+ method public abstract int getSpanTypeId();
+ }
+
public class Selection {
method public static boolean extendDown(android.text.Spannable, android.text.Layout);
method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -47892,7 +47904,6 @@ package android.view {
field public static final int FIRST_APPLICATION_WINDOW = 1; // 0x1
field public static final int FIRST_SUB_WINDOW = 1000; // 0x3e8
field public static final int FIRST_SYSTEM_WINDOW = 2000; // 0x7d0
- field public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 1L; // 0x1L
field public static final int FLAGS_CHANGED = 4; // 0x4
field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
@@ -47930,6 +47941,9 @@ package android.view {
field public static final int LAST_SUB_WINDOW = 1999; // 0x7cf
field public static final int LAST_SYSTEM_WINDOW = 2999; // 0xbb7
field public static final int LAYOUT_CHANGED = 1; // 0x1
+ field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1; // 0x1
+ field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0; // 0x0
+ field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2; // 0x2
field public static final int MEMORY_TYPE_CHANGED = 256; // 0x100
field public static final deprecated int MEMORY_TYPE_GPU = 2; // 0x2
field public static final deprecated int MEMORY_TYPE_HARDWARE = 1; // 0x1
@@ -47987,11 +48001,11 @@ package android.view {
field public float buttonBrightness;
field public float dimAmount;
field public int flags;
- field public long flags2;
field public int format;
field public int gravity;
field public float horizontalMargin;
field public float horizontalWeight;
+ field public int layoutInDisplayCutoutMode;
field public deprecated int memoryType;
field public java.lang.String packageName;
field public int preferredDisplayModeId;
@@ -48039,6 +48053,8 @@ package android.view.accessibility {
method public void setPackageName(java.lang.CharSequence);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+ field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+ field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 002c2bd9d08c..b7518a5ed958 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -430,6 +430,7 @@ package android.app.backup {
method public java.lang.String getCurrentTransport();
method public boolean isAppEligibleForBackup(java.lang.String);
method public boolean isBackupEnabled();
+ method public boolean isBackupServiceActive(android.os.UserHandle);
method public java.lang.String[] listAllTransports();
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver);
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int);
@@ -4454,6 +4455,8 @@ package android.telephony {
method public deprecated void setDataEnabled(int, boolean);
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
+ method public void setSimPowerState(int);
+ method public void setSimPowerStateForSlot(int, int);
method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
method public void setVoiceActivationState(int);
method public deprecated void silenceRinger();
@@ -4463,8 +4466,6 @@ package android.telephony {
method public int[] supplyPukReportResult(java.lang.String, java.lang.String);
method public void toggleRadioOnOff();
method public void updateServiceLocation();
- method public void setSimPowerState(int);
- method public void setSimPowerStateForSlot(int, int);
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -4535,6 +4536,38 @@ package android.telephony.data {
field public static final int TYPE_COMMON = 0; // 0x0
}
+ public abstract class DataService extends android.app.Service {
+ method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
+ field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+ field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+ }
+
+ public class DataService.DataServiceProvider {
+ ctor public DataService.DataServiceProvider(int);
+ method public void deactivateDataCall(int, boolean, boolean, android.telephony.data.DataServiceCallback);
+ method public void getDataCallList(android.telephony.data.DataServiceCallback);
+ method public final int getSlotId();
+ method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method protected void onDestroy();
+ method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
+ method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
+ method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, boolean, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
+ }
+
+ public class DataServiceCallback {
+ method public void onDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onDeactivateDataCallComplete(int);
+ method public void onGetDataCallListComplete(int, java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onSetDataProfileComplete(int);
+ method public void onSetInitialAttachApnComplete(int);
+ method public void onSetupDataCallComplete(int, android.telephony.data.DataCallResponse);
+ field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+ field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+ field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+ field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
}
package android.telephony.ims {
@@ -4647,9 +4680,12 @@ package android.util {
}
public final class StatsManager {
+ method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
+ method public byte[] getData(java.lang.String);
method public byte[] getData(long);
method public byte[] getMetadata();
+ method public boolean removeConfiguration(java.lang.String);
method public boolean removeConfiguration(long);
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index ffe652f72256..2c35d413631b 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -186,7 +186,8 @@ LOCAL_SRC_FILES := \
tests/statsd_test_util.cpp \
tests/e2e/WakelockDuration_e2e_test.cpp \
tests/e2e/MetricConditionLink_e2e_test.cpp \
- tests/e2e/Attribution_e2e_test.cpp
+ tests/e2e/Attribution_e2e_test.cpp \
+ tests/e2e/GaugeMetric_e2e_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
@@ -198,6 +199,24 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := lite
include $(BUILD_NATIVE_TEST)
+##############################
+# stats proto static java lib
+##############################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := statsdprotolite
+
+LOCAL_SRC_FILES := \
+ src/stats_log.proto \
+ src/statsd_config.proto \
+ src/atoms.proto
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ platformprotoslite
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
statsd_common_src:=
statsd_common_aidl_includes:=
@@ -208,4 +227,4 @@ statsd_common_shared_libraries:=
##############################
-include $(call all-makefiles-under,$(LOCAL_PATH)) \ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 09e10a13615f..301e3a535e6d 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -99,6 +99,7 @@ private:
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
+ FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
};
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index 845c13867e5a..d0f96a2abe6b 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -32,6 +32,10 @@ namespace statsd {
const DimensionsValue* getSingleLeafValue(const DimensionsValue* value);
DimensionsValue getSingleLeafValue(const DimensionsValue& value);
+// Appends the leaf node to the parent tree.
+void appendLeafNodeToParent(const Field& field, const DimensionsValue& value,
+ DimensionsValue* parentValue);
+
// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto
// represents a tree. When the input proto has repeated fields and the input "dimensions" wants
// "ANY" locations, it will return multiple trees.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1a4888c31fff..ae47bd898cf9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -114,6 +114,9 @@ GaugeMetricProducer::~GaugeMetricProducer() {
void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
+ ProtoOutputStream pbOutput;
+ onDumpReportLocked(dumpTimeNs, &pbOutput);
+ parseProtoOutputStream(pbOutput, report);
}
void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 08b0981bb14f..a0239fcd1127 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -141,6 +141,7 @@ private:
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
+ FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
};
} // namespace statsd
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index b0c31975c91c..dcb8eed1479d 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -471,10 +471,13 @@ const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0},
{"AID_AUTOMOTIVE_EVS", 1062},
{"AID_LOWPAN", 1063},
{"AID_HSM", 1064},
+ {"AID_RESERVED_DISK", 1065},
+ {"AID_STATSD", 1066},
+ {"AID_INCIDENTD", 1067},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 09a43f5c881f..cee920038eef 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -43,6 +43,19 @@ int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit);
// Helper function to write PulledAtomStats to ProtoOutputStream
void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
util::ProtoOutputStream* protoOutput);
+
+template<class T>
+bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) {
+ std::string pbBytes;
+ auto iter = protoOutput.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ pbBytes.append(reinterpret_cast<const char*>(iter.readBuffer()), toRead);
+ iter.rp()->move(toRead);
+ }
+ return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
+}
+
} // namespace statsd
} // namespace os
} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
new file mode 100644
index 000000000000..10a6c363eab3
--- /dev/null
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -0,0 +1,193 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfigForPushedEvent() {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+ auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_CHANGED);
+ *config.add_atom_matcher() = atomMatcher;
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ auto gaugeMetric = config.add_gauge_metric();
+ gaugeMetric->set_id(123456);
+ gaugeMetric->set_what(atomMatcher.id());
+ gaugeMetric->set_condition(isInBackgroundPredicate.id());
+ gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false);
+ auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
+ fieldMatcher->set_field(android::util::APP_START_CHANGED);
+ fieldMatcher->add_child()->set_field(3); // type (enum)
+ fieldMatcher->add_child()->set_field(4); // activity_name(str)
+ fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64)
+ *gaugeMetric->mutable_dimensions() =
+ CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ });
+ gaugeMetric->set_bucket(ONE_MINUTE);
+
+ auto links = gaugeMetric->add_links();
+ links->set_condition(isInBackgroundPredicate.id());
+ auto dimensionWhat = links->mutable_dimensions_in_what();
+ dimensionWhat->set_field(android::util::APP_START_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ auto dimensionCondition = links->mutable_dimensions_in_condition();
+ dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionCondition->add_child()->set_field(1); // uid field.
+ return config;
+}
+
+std::unique_ptr<LogEvent> CreateAppStartChangedEvent(
+ const int uid, const string& pkg_name, AppStartChanged::TransitionType type,
+ const string& activity_name, const string& calling_pkg_name, const bool is_instant_app,
+ int64_t activity_start_msec, uint64_t timestampNs) {
+ auto logEvent = std::make_unique<LogEvent>(
+ android::util::APP_START_CHANGED, timestampNs);
+ logEvent->write(uid);
+ logEvent->write(pkg_name);
+ logEvent->write(type);
+ logEvent->write(activity_name);
+ logEvent->write(calling_pkg_name);
+ logEvent->write(is_instant_app);
+ logEvent->write(activity_start_msec);
+ logEvent->init();
+ return logEvent;
+}
+
+} // namespace
+
+TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) {
+ auto config = CreateStatsdConfigForPushedEvent();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int appUid1 = 123;
+ int appUid2 = 456;
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + 15));
+ events.push_back(CreateMoveToForegroundEvent(appUid1, bucketStartTimeNs + bucketSizeNs + 250));
+ events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + bucketSizeNs + 350));
+ events.push_back(CreateMoveToForegroundEvent(
+ appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100));
+
+
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::WARM, "activity_name1", "calling_pkg_name1",
+ true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::HOT, "activity_name2", "calling_pkg_name2",
+ true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::COLD, "activity_name3", "calling_pkg_name3",
+ true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::WARM, "activity_name4", "calling_pkg_name4",
+ true /*is_instant_app*/, 104 /*activity_start_msec*/,
+ bucketStartTimeNs + bucketSizeNs + 30));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::COLD, "activity_name5", "calling_pkg_name5",
+ true /*is_instant_app*/, 105 /*activity_start_msec*/,
+ bucketStartTimeNs + 2 * bucketSizeNs));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::HOT, "activity_name6", "calling_pkg_name6",
+ false /*is_instant_app*/, 106 /*activity_start_msec*/,
+ bucketStartTimeNs + 2 * bucketSizeNs + 10));
+
+ events.push_back(CreateMoveToBackgroundEvent(appUid2, bucketStartTimeNs + bucketSizeNs + 10));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid2, "app2", AppStartChanged::COLD, "activity_name7", "calling_pkg_name7",
+ true /*is_instant_app*/, 201 /*activity_start_msec*/,
+ bucketStartTimeNs + 2 * bucketSizeNs + 10));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ EXPECT_EQ(gaugeMetrics.data_size(), 2);
+
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(data.dimension().field(), android::util::APP_START_CHANGED);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1 /* uid field */);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid1);
+ EXPECT_EQ(data.bucket_info_size(), 3);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().type(), AppStartChanged::HOT);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_name(), "activity_name2");
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_start_msec(), 102L);
+
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().type(), AppStartChanged::WARM);
+ EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().activity_name(), "activity_name4");
+ EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().activity_start_msec(), 104L);
+
+ EXPECT_EQ(data.bucket_info(2).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(2).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().type(), AppStartChanged::COLD);
+ EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().activity_name(), "activity_name5");
+ EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().activity_start_msec(), 105L);
+
+ data = gaugeMetrics.data(1);
+ EXPECT_EQ(data.dimension().field(), android::util::APP_START_CHANGED);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1 /* uid field */);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid2);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().type(), AppStartChanged::COLD);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_name(), "activity_name7");
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_start_msec(), 201L);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 278335674130..fcdaafce6627 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -26,6 +26,8 @@ namespace statsd {
#ifdef __ANDROID__
+namespace {
+
StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) {
StatsdConfig config;
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
@@ -56,6 +58,8 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType)
return config;
}
+} // namespace
+
TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
ConfigKey cfgKey;
for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE }) {
@@ -94,6 +98,7 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
auto releaseEvent2 = CreateReleaseWakelockEvent(
attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 15);
+
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(std::move(screenTurnedOnEvent));
@@ -119,18 +124,8 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimension().field(),
- android::util::WAKELOCK_STATE_CHANGED);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
- // Attribution field.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
- // Uid only.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidDimension(
+ data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111);
// Validate bucket info.
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
data = reports.reports(0).metrics(0).duration_metrics().data(0);
@@ -147,18 +142,8 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
data = reports.reports(0).metrics(0).duration_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimension().field(),
- android::util::WAKELOCK_STATE_CHANGED);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
- // Attribution field.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
- // Uid only.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidDimension(
+ data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111);
// Two output buckets.
// The wakelock holding interval in the 1st bucket starts from the screen off event and to
// the end of the 1st bucket.
@@ -167,6 +152,32 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
// The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
// ends at the second screen on event.
EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
+
+ events.clear();
+ events.push_back(CreateScreenStateChangedEvent(
+ ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs + 90));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions1, "wl3", bucketStartTimeNs + 2 * bucketSizeNs + 100));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions1, "wl3", bucketStartTimeNs + 5 * bucketSizeNs + 100));
+ sortLogEventsByTimestamp(&events);
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ ValidateAttributionUidDimension(
+ data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111);
+ // The last wakelock holding spans 4 buckets.
+ EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
+ EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
}
}
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e7882353bec0..718b2e177d52 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -19,6 +19,14 @@ namespace android {
namespace os {
namespace statsd {
+AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId(name));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(atomId);
+ return atom_matcher;
+}
+
AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
WakelockStateChanged::State state) {
AtomMatcher atom_matcher;
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 1bbbd9a30540..7eb93b9d5fcd 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -23,6 +23,9 @@ namespace android {
namespace os {
namespace statsd {
+// Create AtomMatcher proto to simply match a specific atom type.
+AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
+
// Create AtomMatcher proto for acquiring wakelock.
AtomMatcher CreateAcquireWakelockAtomMatcher();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aa099eb19d37..0a5b848e6220 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,7 @@
package android.app;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
@@ -98,6 +99,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.RemoteAnimationDefinition;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
@@ -857,6 +859,7 @@ public class Activity extends ContextThemeWrapper
private boolean mHasCurrentPermissionsRequest;
private boolean mAutoFillResetNeeded;
+ private boolean mAutoFillIgnoreFirstResumePause;
/** The last autofill id that was returned from {@link #getNextAutofillId()} */
private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
@@ -1253,10 +1256,7 @@ public class Activity extends ContextThemeWrapper
getApplication().dispatchActivityStarted(this);
if (mAutoFillResetNeeded) {
- AutofillManager afm = getAutofillManager();
- if (afm != null) {
- afm.onVisibleForAutofill();
- }
+ getAutofillManager().onVisibleForAutofill();
}
}
@@ -1320,6 +1320,20 @@ public class Activity extends ContextThemeWrapper
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
+ if (mAutoFillResetNeeded) {
+ if (!mAutoFillIgnoreFirstResumePause) {
+ View focus = getCurrentFocus();
+ if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
+ // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
+ // window visibility after recreation is INVISIBLE in onResume() and next frame
+ // ViewRootImpl.performTraversals() changes window visibility to VISIBLE.
+ // So we cannot call View.notifyEnterOrExited() which will do nothing
+ // when View.isVisibleToUser() is false.
+ getAutofillManager().notifyViewEntered(focus);
+ }
+ }
+ }
mCalled = true;
}
@@ -1681,6 +1695,19 @@ public class Activity extends ContextThemeWrapper
protected void onPause() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
getApplication().dispatchActivityPaused(this);
+ if (mAutoFillResetNeeded) {
+ if (!mAutoFillIgnoreFirstResumePause) {
+ if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
+ View focus = getCurrentFocus();
+ if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ getAutofillManager().notifyViewExited(focus);
+ }
+ } else {
+ // reset after first pause()
+ if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill got first pause " + this);
+ mAutoFillIgnoreFirstResumePause = false;
+ }
+ }
mCalled = true;
}
@@ -1871,6 +1898,10 @@ public class Activity extends ContextThemeWrapper
mTranslucentCallback = null;
mCalled = true;
+ if (mAutoFillResetNeeded) {
+ getAutofillManager().onInvisibleForAutofill();
+ }
+
if (isFinishing()) {
if (mAutoFillResetNeeded) {
getAutofillManager().onActivityFinished();
@@ -6266,7 +6297,7 @@ public class Activity extends ContextThemeWrapper
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
- final AutofillManager afm = getAutofillManager();
+ final AutofillManager afm = mAutofillManager;
if (afm != null) {
afm.dump(prefix, writer);
} else {
@@ -6616,7 +6647,6 @@ public class Activity extends ContextThemeWrapper
* to run as a {@link android.service.vr.VrListenerService} is not installed, or has
* not been enabled in user settings.
*
- * @see android.content.pm.PackageManager#FEATURE_VR_MODE
* @see android.content.pm.PackageManager#FEATURE_VR_MODE_HIGH_PERFORMANCE
* @see android.service.vr.VrListenerService
* @see android.provider.Settings#ACTION_VR_LISTENER_SETTINGS
@@ -7120,13 +7150,23 @@ public class Activity extends ContextThemeWrapper
}
}
- final void performResume() {
+ final void performResume(boolean followedByPause) {
performRestart(true /* start */);
mFragments.execPendingActions();
mLastNonConfigurationInstances = null;
+ if (mAutoFillResetNeeded) {
+ // When Activity is destroyed in paused state, and relaunch activity, there will be
+ // extra onResume and onPause event, ignore the first onResume and onPause.
+ // see ActivityThread.handleRelaunchActivity()
+ mAutoFillIgnoreFirstResumePause = followedByPause;
+ if (mAutoFillIgnoreFirstResumePause && DEBUG_LIFECYCLE) {
+ Slog.v(TAG, "autofill will ignore first pause when relaunching " + this);
+ }
+ }
+
mCalled = false;
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this);
@@ -7311,7 +7351,7 @@ public class Activity extends ContextThemeWrapper
}
} else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {
Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null;
- getAutofillManager().onAuthenticationResult(requestCode, resultData);
+ getAutofillManager().onAuthenticationResult(requestCode, resultData, getCurrentFocus());
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
@@ -7585,6 +7625,12 @@ public class Activity extends ContextThemeWrapper
return !mStopped;
}
+ /** @hide */
+ @Override
+ public boolean isDisablingEnterExitEventForAutofill() {
+ return mAutoFillIgnoreFirstResumePause || !mResumed;
+ }
+
/**
* If set to true, this indicates to the system that it should never take a
* screenshot of the activity to be used as a representation while it is not in a started state.
@@ -7659,6 +7705,22 @@ public class Activity extends ContextThemeWrapper
}
}
+ /**
+ * Registers remote animations per transition type for this activity.
+ *
+ * @param definition The remote animation definition that defines which transition whould run
+ * which remote animation.
+ * @hide
+ */
+ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ try {
+ ActivityManager.getService().registerRemoteAnimations(mToken, definition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call registerRemoteAnimations", e);
+ }
+ }
+
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 972ffcbf527f..db12c37f2c2d 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -324,4 +324,14 @@ public abstract class ActivityManagerInternal {
* Returns if more users can be started without stopping currently running users.
*/
public abstract boolean canStartMoreUsers();
+
+ /**
+ * Sets the user switcher message for switching from {@link android.os.UserHandle#SYSTEM}.
+ */
+ public abstract void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage);
+
+ /**
+ * Sets the user switcher message for switching to {@link android.os.UserHandle#SYSTEM}.
+ */
+ public abstract void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index e61c5b7c78a1..4bcd677e1f4e 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -16,12 +16,14 @@
package android.app;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
@@ -44,6 +46,7 @@ import android.util.Pair;
import android.util.Slog;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RemoteAnimationAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -241,6 +244,8 @@ public class ActivityOptions {
private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE
= "android:instantapps.installerbundle";
private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture";
+ private static final String KEY_REMOTE_ANIMATION_ADAPTER
+ = "android:activity.remoteAnimationAdapter";
/** @hide */
public static final int ANIM_NONE = 0;
@@ -268,6 +273,8 @@ public class ActivityOptions {
public static final int ANIM_CLIP_REVEAL = 11;
/** @hide */
public static final int ANIM_OPEN_CROSS_PROFILE_APPS = 12;
+ /** @hide */
+ public static final int ANIM_REMOTE_ANIMATION = 13;
private String mPackageName;
private Rect mLaunchBounds;
@@ -304,6 +311,7 @@ public class ActivityOptions {
private int mRotationAnimationHint = -1;
private Bundle mAppVerificationBundle;
private IAppTransitionAnimationSpecsFuture mSpecsFuture;
+ private RemoteAnimationAdapter mRemoteAnimationAdapter;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -826,6 +834,20 @@ public class ActivityOptions {
return opts;
}
+ /**
+ * Create an {@link ActivityOptions} instance that lets the application control the entire
+ * animation using a {@link RemoteAnimationAdapter}.
+ * @hide
+ */
+ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public static ActivityOptions makeRemoteAnimation(
+ RemoteAnimationAdapter remoteAnimationAdapter) {
+ final ActivityOptions opts = new ActivityOptions();
+ opts.mRemoteAnimationAdapter = remoteAnimationAdapter;
+ opts.mAnimationType = ANIM_REMOTE_ANIMATION;
+ return opts;
+ }
+
/** @hide */
public boolean getLaunchTaskBehind() {
return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
@@ -922,6 +944,7 @@ public class ActivityOptions {
mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
KEY_SPECS_FUTURE));
}
+ mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER);
}
/**
@@ -1070,6 +1093,11 @@ public class ActivityOptions {
}
/** @hide */
+ public RemoteAnimationAdapter getRemoteAnimationAdapter() {
+ return mRemoteAnimationAdapter;
+ }
+
+ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null;
}
@@ -1309,6 +1337,7 @@ public class ActivityOptions {
mAnimSpecs = otherOptions.mAnimSpecs;
mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
mSpecsFuture = otherOptions.mSpecsFuture;
+ mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
}
/**
@@ -1403,7 +1432,9 @@ public class ActivityOptions {
if (mAppVerificationBundle != null) {
b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);
}
-
+ if (mRemoteAnimationAdapter != null) {
+ b.putParcelable(KEY_REMOTE_ANIMATION_ADAPTER, mRemoteAnimationAdapter);
+ }
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e610ac4a318b..934b0f3cd4e4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -166,6 +166,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.text.DateFormat;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -220,6 +221,9 @@ public final class ActivityThread extends ClientTransactionHandler {
// Whether to invoke an activity callback after delivering new configuration.
private static final boolean REPORT_TO_ACTIVITY = true;
+ // Maximum number of recent tokens to maintain for debugging purposes
+ private static final int MAX_RECENT_TOKENS = 10;
+
/**
* Denotes an invalid sequence number corresponding to a process state change.
*/
@@ -252,6 +256,8 @@ public final class ActivityThread extends ClientTransactionHandler {
final H mH = new H();
final Executor mExecutor = new HandlerExecutor(mH);
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
+ final ArrayDeque<Integer> mRecentTokens = new ArrayDeque<>();
+
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
ActivityClientRecord mNewActivities = null;
@@ -1752,9 +1758,11 @@ public final class ActivityThread extends ClientTransactionHandler {
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
break;
- case ATTACH_AGENT:
- handleAttachAgent((String) msg.obj);
+ case ATTACH_AGENT: {
+ Application app = getApplication();
+ handleAttachAgent((String) msg.obj, app != null ? app.mLoadedApk : null);
break;
+ }
case APPLICATION_INFO_CHANGED:
mUpdatingSystemConfig = true;
try {
@@ -2166,6 +2174,18 @@ public final class ActivityThread extends ClientTransactionHandler {
pw.println(String.format(format, objs));
}
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mActivities:");
+
+ for (ArrayMap.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
+ pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:"
+ + entry.getValue().toString() + "]");
+ }
+
+ pw.println(prefix + "mRecentTokens:" + mRecentTokens);
+ }
+
public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
int pid, String processName,
@@ -2850,6 +2870,11 @@ public final class ActivityThread extends ClientTransactionHandler {
r.setState(ON_CREATE);
mActivities.put(r.token, r);
+ mRecentTokens.push(r.token.hashCode());
+
+ if (mRecentTokens.size() > MAX_RECENT_TOKENS) {
+ mRecentTokens.removeLast();
+ }
} catch (SuperNotCalledException e) {
throw e;
@@ -3071,7 +3096,7 @@ public final class ActivityThread extends ClientTransactionHandler {
checkAndBlockForNetworkAccess();
deliverNewIntents(r, intents);
if (resumed) {
- r.activity.performResume();
+ r.activity.performResume(false);
r.activity.mTemporaryPause = false;
}
@@ -3246,11 +3271,23 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- static final void handleAttachAgent(String agent) {
+ private static boolean attemptAttachAgent(String agent, ClassLoader classLoader) {
try {
- VMDebug.attachAgent(agent);
+ VMDebug.attachAgent(agent, classLoader);
+ return true;
} catch (IOException e) {
- Slog.e(TAG, "Attaching agent failed: " + agent);
+ Slog.e(TAG, "Attaching agent with " + classLoader + " failed: " + agent);
+ return false;
+ }
+ }
+
+ static void handleAttachAgent(String agent, LoadedApk loadedApk) {
+ ClassLoader classLoader = loadedApk != null ? loadedApk.getClassLoader() : null;
+ if (attemptAttachAgent(agent, classLoader)) {
+ return;
+ }
+ if (classLoader != null) {
+ attemptAttachAgent(agent, null);
}
}
@@ -3681,7 +3718,7 @@ public final class ActivityThread extends ClientTransactionHandler {
deliverResults(r, r.pendingResults);
r.pendingResults = null;
}
- r.activity.performResume();
+ r.activity.performResume(r.startsNotResumed);
synchronized (mResourcesManager) {
// If there is a pending local relaunch that was requested when the activity was
@@ -4400,7 +4437,7 @@ public final class ActivityThread extends ClientTransactionHandler {
checkAndBlockForNetworkAccess();
deliverResults(r, results);
if (resumed) {
- r.activity.performResume();
+ r.activity.performResume(false);
r.activity.mTemporaryPause = false;
}
}
@@ -5542,12 +5579,16 @@ public final class ActivityThread extends ClientTransactionHandler {
mCompatConfiguration = new Configuration(data.config);
mProfiler = new Profiler();
+ String agent = null;
if (data.initProfilerInfo != null) {
mProfiler.profileFile = data.initProfilerInfo.profileFile;
mProfiler.profileFd = data.initProfilerInfo.profileFd;
mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval;
mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler;
mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput;
+ if (data.initProfilerInfo.attachAgentDuringBind) {
+ agent = data.initProfilerInfo.agent;
+ }
}
// send up app name; do this *before* waiting for debugger
@@ -5597,6 +5638,10 @@ public final class ActivityThread extends ClientTransactionHandler {
data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+ if (agent != null) {
+ handleAttachAgent(agent, data.loadedApk);
+ }
+
/**
* Switch this process to density compatibility mode if needed.
*/
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 45c0e0cdfb25..0f66652af76c 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -24,6 +24,7 @@ import android.os.IBinder;
import com.android.internal.content.ReferrerIntent;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -121,4 +122,11 @@ public abstract class ClientTransactionHandler {
* provided token.
*/
public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
+
+ /**
+ * Debugging output.
+ * @param pw {@link PrintWriter} to write logs to.
+ * @param prefix Prefix to prepend to output.
+ */
+ public abstract void dump(PrintWriter pw, String prefix);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 696899f73b96..04ee77d764aa 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -65,6 +65,7 @@ import android.os.PersistableBundle;
import android.os.StrictMode;
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
+import android.view.RemoteAnimationDefinition;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -672,4 +673,9 @@ interface IActivityManager {
* user unlock progress.
*/
boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
+
+ /**
+ * Registers remote animations for a specific activity.
+ */
+ void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
}
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index d5234278da7d..a295c4c61a8c 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -55,14 +55,24 @@ public class ProfilerInfo implements Parcelable {
*/
public final String agent;
+ /**
+ * Whether the {@link agent} should be attached early (before bind-application) or during
+ * bind-application. Agents attached prior to binding cannot be loaded from the app's APK
+ * directly and must be given as an absolute path (or available in the default LD_LIBRARY_PATH).
+ * Agents attached during bind-application will miss early setup (e.g., resource initialization
+ * and classloader generation), but are searched in the app's library search path.
+ */
+ public final boolean attachAgentDuringBind;
+
public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop,
- boolean streaming, String agent) {
+ boolean streaming, String agent, boolean attachAgentDuringBind) {
profileFile = filename;
profileFd = fd;
samplingInterval = interval;
autoStopProfiler = autoStop;
streamingOutput = streaming;
this.agent = agent;
+ this.attachAgentDuringBind = attachAgentDuringBind;
}
public ProfilerInfo(ProfilerInfo in) {
@@ -72,6 +82,7 @@ public class ProfilerInfo implements Parcelable {
autoStopProfiler = in.autoStopProfiler;
streamingOutput = in.streamingOutput;
agent = in.agent;
+ attachAgentDuringBind = in.attachAgentDuringBind;
}
/**
@@ -110,6 +121,7 @@ public class ProfilerInfo implements Parcelable {
out.writeInt(autoStopProfiler ? 1 : 0);
out.writeInt(streamingOutput ? 1 : 0);
out.writeString(agent);
+ out.writeBoolean(attachAgentDuringBind);
}
public static final Parcelable.Creator<ProfilerInfo> CREATOR =
@@ -132,6 +144,7 @@ public class ProfilerInfo implements Parcelable {
autoStopProfiler = in.readInt() != 0;
streamingOutput = in.readInt() != 0;
agent = in.readString();
+ attachAgentDuringBind = in.readBoolean();
}
@Override
diff --git a/core/java/android/app/admin/ConnectEvent.java b/core/java/android/app/admin/ConnectEvent.java
index f06a9257b7f8..d511c57b1f51 100644
--- a/core/java/android/app/admin/ConnectEvent.java
+++ b/core/java/android/app/admin/ConnectEvent.java
@@ -68,7 +68,7 @@ public final class ConnectEvent extends NetworkEvent implements Parcelable {
@Override
public String toString() {
- return String.format("ConnectEvent(%s, %d, %d, %s)", mIpAddress, mPort, mTimestamp,
+ return String.format("ConnectEvent(%d, %s, %d, %d, %s)", mId, mIpAddress, mPort, mTimestamp,
mPackageName);
}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index aa05b7630c9d..302d52f1ae72 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -467,6 +467,31 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
public static final String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE =
"android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+ /**
+ * Name under which a device administration component indicates whether it supports transfer of
+ * ownership. This meta-data is of type <code>boolean</code>. A value of <code>true</code>
+ * allows this administrator to be used as a target administrator for a transfer. If the value
+ * is <code>false</code>, ownership cannot be transferred to this administrator. The default
+ * value is <code>false</code>.
+ * <p>This metadata is used to avoid ownership transfer migration to an administrator with a
+ * version which does not yet support it.
+ * <p>Usage:
+ * <pre>
+ * &lt;receiver name="..." android:permission="android.permission.BIND_DEVICE_ADMIN"&gt;
+ * &lt;meta-data
+ * android:name="android.app.device_admin"
+ * android:resource="@xml/..." /&gt;
+ * &lt;meta-data
+ * android:name="android.app.support_transfer_ownership"
+ * android:value="true" /&gt;
+ * &lt;/receiver&gt;
+ * </pre>
+ *
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+ */
+ public static final String SUPPORT_TRANSFER_OWNERSHIP_META_DATA =
+ "android.app.support_transfer_ownership";
+
private DevicePolicyManager mManager;
private ComponentName mWho;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e334aab7005e..9329d56a8de9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,7 +18,6 @@ package android.app.admin;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
-import android.annotation.Condemned;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,8 +49,6 @@ import android.graphics.Bitmap;
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -8633,6 +8630,13 @@ public class DevicePolicyManager {
*
* <p> Backup service is off by default when device owner is present.
*
+ * <p> If backups are made mandatory by specifying a non-null mandatory backup transport using
+ * the {@link DevicePolicyManager#setMandatoryBackupTransport} method, the backup service is
+ * automatically enabled.
+ *
+ * <p> If the backup service is disabled using this method after the mandatory backup transport
+ * has been set, the mandatory backup transport is cleared.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled {@code true} to enable the backup service, {@code false} to disable it.
* @throws SecurityException if {@code admin} is not a device owner.
@@ -8664,6 +8668,43 @@ public class DevicePolicyManager {
}
/**
+ * Makes backups mandatory and enforces the usage of the specified backup transport.
+ *
+ * <p>When a {@code null} backup transport is specified, backups are made optional again.
+ * <p>Only device owner can call this method.
+ * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
+ * specified, backups will be enabled.
+ *
+ * @param admin admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setMandatoryBackupTransport(
+ @NonNull ComponentName admin, @Nullable ComponentName backupTransportComponent) {
+ try {
+ mService.setMandatoryBackupTransport(admin, backupTransportComponent);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the backup transport which has to be used for backups if backups are mandatory or
+ * {@code null} if backups are not mandatory.
+ *
+ * @return a {@link ComponentName} of the backup transport layer to be used if backups are
+ * mandatory or {@code null} if backups are not mandatory.
+ */
+ public ComponentName getMandatoryBackupTransport() {
+ try {
+ return mService.getMandatoryBackupTransport();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
* Called by a device owner to control the network logging feature.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
@@ -8939,15 +8980,6 @@ public class DevicePolicyManager {
}
}
- /** {@hide} */
- @Condemned
- @Deprecated
- public boolean clearApplicationUserData(@NonNull ComponentName admin,
- @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
- @NonNull Handler handler) {
- return clearApplicationUserData(admin, packageName, listener, new HandlerExecutor(handler));
- }
-
/**
* Called by the device owner or profile owner to clear application user data of a given
* package. The behaviour of this is equivalent to the target application calling
@@ -8958,14 +8990,14 @@ public class DevicePolicyManager {
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param packageName The name of the package which will have its user data wiped.
- * @param listener A callback object that will inform the caller when the clearing is done.
* @param executor The executor through which the listener should be invoked.
+ * @param listener A callback object that will inform the caller when the clearing is done.
* @throws SecurityException if the caller is not the device owner/profile owner.
* @return whether the clearing succeeded.
*/
public boolean clearApplicationUserData(@NonNull ComponentName admin,
- @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
- @NonNull @CallbackExecutor Executor executor) {
+ @NonNull String packageName, @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnClearApplicationUserDataListener listener) {
throwIfParentInstance("clearAppData");
Preconditions.checkNotNull(executor);
try {
@@ -9064,6 +9096,11 @@ public class DevicePolicyManager {
* will be received in the
* {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)} callback.
*
+ * <p>The incoming target administrator must have the
+ * {@link DeviceAdminReceiver#SUPPORT_TRANSFER_OWNERSHIP_META_DATA} <code>meta-data</code> tag
+ * included in its corresponding <code>receiver</code> component with a value of {@code true}.
+ * Otherwise an {@link IllegalArgumentException} will be thrown.
+ *
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param target which {@link DeviceAdminReceiver} we want the new administrator to be
* @param bundle data to be sent to the new administrator
@@ -9080,4 +9117,84 @@ public class DevicePolicyManager {
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Called by a device owner to specify the user session start message. This may be displayed
+ * during a user switch.
+ * <p>
+ * The message should be limited to a short statement or it may be truncated.
+ * <p>
+ * If the message needs to be localized, it is the responsibility of the
+ * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
+ * and set a new version of this message accordingly.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param startUserSessionMessage message for starting user session, or {@code null} to use
+ * system default message.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setStartUserSessionMessage(
+ @NonNull ComponentName admin, @Nullable CharSequence startUserSessionMessage) {
+ throwIfParentInstance("setStartUserSessionMessage");
+ try {
+ mService.setStartUserSessionMessage(admin, startUserSessionMessage);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by a device owner to specify the user session end message. This may be displayed
+ * during a user switch.
+ * <p>
+ * The message should be limited to a short statement or it may be truncated.
+ * <p>
+ * If the message needs to be localized, it is the responsibility of the
+ * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
+ * and set a new version of this message accordingly.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param endUserSessionMessage message for ending user session, or {@code null} to use system
+ * default message.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setEndUserSessionMessage(
+ @NonNull ComponentName admin, @Nullable CharSequence endUserSessionMessage) {
+ throwIfParentInstance("setEndUserSessionMessage");
+ try {
+ mService.setEndUserSessionMessage(admin, endUserSessionMessage);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the user session start message.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public CharSequence getStartUserSessionMessage(@NonNull ComponentName admin) {
+ throwIfParentInstance("getStartUserSessionMessage");
+ try {
+ return mService.getStartUserSessionMessage(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the user session end message.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public CharSequence getEndUserSessionMessage(@NonNull ComponentName admin) {
+ throwIfParentInstance("getEndUserSessionMessage");
+ try {
+ return mService.getEndUserSessionMessage(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index b692ffd95e72..531bef014c0b 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -123,4 +123,13 @@ public abstract class DevicePolicyManagerInternal {
* @param userId User ID of the profile.
*/
public abstract void reportSeparateProfileChallengeChanged(@UserIdInt int userId);
+
+ /**
+ * Check whether the user could have their password reset in an untrusted manor due to there
+ * being an admin which can call {@link #resetPassword} to reset the password without knowledge
+ * of the previous password.
+ *
+ * @param userId The user in question
+ */
+ public abstract boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId);
}
diff --git a/core/java/android/app/admin/DnsEvent.java b/core/java/android/app/admin/DnsEvent.java
index 4ddf13e07344..a2d704b86649 100644
--- a/core/java/android/app/admin/DnsEvent.java
+++ b/core/java/android/app/admin/DnsEvent.java
@@ -96,7 +96,7 @@ public final class DnsEvent extends NetworkEvent implements Parcelable {
@Override
public String toString() {
- return String.format("DnsEvent(%s, %s, %d, %d, %s)", mHostname,
+ return String.format("DnsEvent(%d, %s, %s, %d, %d, %s)", mId, mHostname,
(mIpAddresses == null) ? "NONE" : String.join(" ", mIpAddresses),
mIpAddressesCount, mTimestamp, mPackageName);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7154053f593c..eac7f7ed4b3e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -359,6 +359,8 @@ interface IDevicePolicyManager {
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
+ void setMandatoryBackupTransport(in ComponentName admin, in ComponentName backupTransportComponent);
+ ComponentName getMandatoryBackupTransport();
void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
boolean isNetworkLoggingEnabled(in ComponentName admin);
@@ -389,4 +391,9 @@ interface IDevicePolicyManager {
List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
void transferOwnership(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
+
+ void setStartUserSessionMessage(in ComponentName admin, in CharSequence startUserSessionMessage);
+ void setEndUserSessionMessage(in ComponentName admin, in CharSequence endUserSessionMessage);
+ CharSequence getStartUserSessionMessage(in ComponentName admin);
+ CharSequence getEndUserSessionMessage(in ComponentName admin);
}
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 3a6a5b20edf7..12f4483141f4 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -27,6 +27,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -387,6 +388,29 @@ public class BackupManager {
}
/**
+ * Report whether the backup mechanism is currently active.
+ * When it is inactive, the device will not perform any backup operations, nor will it
+ * deliver data for restore, although clients can still safely call BackupManager methods.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BACKUP)
+ public boolean isBackupServiceActive(UserHandle user) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "isBackupServiceActive");
+ checkServiceBinder();
+ if (sService != null) {
+ try {
+ return sService.isBackupServiceActive(user.getIdentifier());
+ } catch (RemoteException e) {
+ Log.e(TAG, "isBackupEnabled() couldn't connect");
+ }
+ }
+ return false;
+ }
+
+ /**
* Enable/disable data restore at application install time. When enabled, app
* installation will include an attempt to fetch the app's historical data from
* the archival restore dataset (if any). When disabled, no such attempt will
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index da81d19ca3cb..3558e3430470 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -55,6 +55,22 @@ public class BackupTransport {
// Transport should ignore its own moratoriums for call with this flag set.
public static final int FLAG_USER_INITIATED = 1;
+ /**
+ * For key value backup, indicates that the backup data is a diff from a previous backup. The
+ * transport must apply this diff to an existing backup to build the new backup set.
+ *
+ * @hide
+ */
+ public static final int FLAG_INCREMENTAL = 1 << 1;
+
+ /**
+ * For key value backup, indicates that the backup data is a complete set, not a diff from a
+ * previous backup. The transport should clear any previous backup when storing this backup.
+ *
+ * @hide
+ */
+ public static final int FLAG_NON_INCREMENTAL = 1 << 2;
+
IBackupTransport mBinderImpl = new TransportImpl();
public IBinder getBinder() {
@@ -231,12 +247,18 @@ public class BackupTransport {
* {@link #TRANSPORT_OK}, {@link #finishBackup} will then be called to ensure the data
* is sent and recorded successfully.
*
+ * If the backup data is a diff against the previous backup then the flag {@link
+ * BackupTransport#FLAG_INCREMENTAL} will be set. Otherwise, if the data is a complete backup
+ * set then {@link BackupTransport#FLAG_NON_INCREMENTAL} will be set. Before P neither flag will
+ * be set regardless of whether the backup is incremental or not.
+ *
* @param packageInfo The identity of the application whose data is being backed up.
* This specifically includes the signature list for the package.
* @param inFd Descriptor of file with data that resulted from invoking the application's
* BackupService.doBackup() method. This may be a pipe rather than a file on
* persistent media, so it may not be seekable.
- * @param flags {@link BackupTransport#FLAG_USER_INITIATED} or 0.
+ * @param flags a combination of {@link BackupTransport#FLAG_USER_INITIATED}, {@link
+ * BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL}, or 0.
* @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far),
* {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} (to suppress backup of this
* specific package, but allow others to proceed),
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 792cb5f29f9c..f3ca74656e8c 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -294,7 +294,8 @@ interface IBackupManager {
*
* @param transport ComponentName of the service hosting the transport. This is different from
* the transport's name that is returned by {@link BackupTransport#name()}.
- * @param listener A listener object to get a callback on the transport being selected.
+ * @param listener A listener object to get a callback on the transport being selected. It may
+ * be {@code null}.
*
* @hide
*/
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 5053dc6fdf05..c71bf2e65731 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -70,6 +70,7 @@ public class JobParameters implements Parcelable {
private final Network network;
private int stopReason; // Default value of stopReason is REASON_CANCELED
+ private String debugStopReason; // Human readable stop reason for debugging.
/** @hide */
public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
@@ -104,6 +105,14 @@ public class JobParameters implements Parcelable {
}
/**
+ * Reason onStopJob() was called on this job.
+ * @hide
+ */
+ public String getDebugStopReason() {
+ return debugStopReason;
+ }
+
+ /**
* @return The extras you passed in when constructing this job with
* {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
* never be null. If you did not set any extras this will be an empty bundle.
@@ -288,11 +297,13 @@ public class JobParameters implements Parcelable {
network = null;
}
stopReason = in.readInt();
+ debugStopReason = in.readString();
}
/** @hide */
- public void setStopReason(int reason) {
+ public void setStopReason(int reason, String debugStopReason) {
stopReason = reason;
+ this.debugStopReason = debugStopReason;
}
@Override
@@ -323,6 +334,7 @@ public class JobParameters implements Parcelable {
dest.writeInt(0);
}
dest.writeInt(stopReason);
+ dest.writeString(debugStopReason);
}
public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 0fdc7c56fd01..9a50a009ce34 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -17,7 +17,9 @@
package android.app.servertransaction;
import android.annotation.IntDef;
+import android.os.Parcel;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -26,6 +28,7 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
public abstract class ActivityLifecycleItem extends ClientTransactionItem {
+ private String mDescription;
@IntDef(prefix = { "UNDEFINED", "PRE_", "ON_" }, value = {
UNDEFINED,
@@ -53,4 +56,39 @@ public abstract class ActivityLifecycleItem extends ClientTransactionItem {
/** A final lifecycle state that an activity should reach. */
@LifecycleState
public abstract int getTargetState();
+
+
+ protected ActivityLifecycleItem() {
+ }
+
+ protected ActivityLifecycleItem(Parcel in) {
+ mDescription = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mDescription);
+ }
+
+ /**
+ * Sets a description that can be retrieved later for debugging purposes.
+ * @param description Description to set.
+ * @return The {@link ActivityLifecycleItem}.
+ */
+ public ActivityLifecycleItem setDescription(String description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Retrieves description if set through {@link #setDescription(String)}.
+ */
+ public String getDescription() {
+ return mDescription;
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "target state:" + getTargetState());
+ pw.println(prefix + "description: " + mDescription);
+ }
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 08ad2f055774..fc078798f6b9 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -26,6 +26,7 @@ import android.os.RemoteException;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -237,4 +238,12 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
return result;
}
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mActivityToken:" + mActivityToken.hashCode());
+ pw.println(prefix + "mLifecycleStateRequest:");
+ if (mLifecycleStateRequest != null) {
+ mLifecycleStateRequest.dump(pw, prefix + " ");
+ }
+ }
}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 83da5f33c62a..cbcf6c750fed 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -76,12 +76,14 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
dest.writeInt(mConfigChanges);
}
/** Read from Parcel. */
private DestroyActivityItem(Parcel in) {
+ super(in);
mFinished = in.readBoolean();
mConfigChanges = in.readInt();
}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 880fef73c6f2..70a4755f99af 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -114,6 +114,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
dest.writeBoolean(mUserLeaving);
dest.writeInt(mConfigChanges);
@@ -122,6 +123,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
/** Read from Parcel. */
private PauseActivityItem(Parcel in) {
+ super(in);
mFinished = in.readBoolean();
mUserLeaving = in.readBoolean();
mConfigChanges = in.readInt();
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index 9249c6e8ed54..ed90f2cb1013 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -113,6 +113,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeInt(mProcState);
dest.writeBoolean(mUpdateProcState);
dest.writeBoolean(mIsForward);
@@ -120,6 +121,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
/** Read from Parcel. */
private ResumeActivityItem(Parcel in) {
+ super(in);
mProcState = in.readInt();
mUpdateProcState = in.readBoolean();
mIsForward = in.readBoolean();
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 5c5c3041344f..b814d1ae1392 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -83,12 +83,14 @@ public class StopActivityItem extends ActivityLifecycleItem {
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeBoolean(mShowWindow);
dest.writeInt(mConfigChanges);
}
/** Read from Parcel. */
private StopActivityItem(Parcel in) {
+ super(in);
mShowWindow = in.readBoolean();
mConfigChanges = in.readInt();
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 5b0ea6b1f9d4..78b393a831f9 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -33,6 +33,8 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.List;
/**
@@ -122,6 +124,21 @@ public class TransactionExecutor {
final IBinder token = transaction.getActivityToken();
final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+ // TODO(b/71506345): Remove once root cause is found.
+ if (r == null) {
+ final StringWriter stringWriter = new StringWriter();
+ final PrintWriter pw = new PrintWriter(stringWriter);
+ final String prefix = " ";
+
+ pw.println("Lifecycle transaction does not have valid ActivityClientRecord.");
+ pw.println("Transaction:");
+ transaction.dump(pw, prefix);
+ pw.println("Executor:");
+ dump(pw, prefix);
+
+ Slog.wtf(TAG, stringWriter.toString());
+ }
+
// Cycle to the state right before the final requested state.
cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
@@ -245,4 +262,9 @@ public class TransactionExecutor {
private static void log(String message) {
if (DEBUG_RESOLVER) Slog.d(TAG, message);
}
+
+ private void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mTransactionHandler:");
+ mTransactionHandler.dump(pw, prefix + " ");
+ }
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index b8fb2e34d083..27cd6e56dc0c 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,12 +21,10 @@ import android.annotation.Nullable;
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
-import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -553,16 +551,11 @@ public final class Slice implements Parcelable {
}
/**
- * Turns a slice Uri into slice content.
- *
- * @param resolver ContentResolver to be used.
- * @param uri The URI to a slice provider
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
- */
- public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
- List<SliceSpec> supportedSpecs) {
+ * @deprecated TO BE REMOVED.
+ */
+ @Deprecated
+ public static @Nullable Slice bindSlice(ContentResolver resolver,
+ @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = resolver.acquireProvider(uri);
if (provider == null) {
@@ -590,60 +583,11 @@ public final class Slice implements Parcelable {
}
/**
- * Turns a slice intent into slice content. Expects an explicit intent. If there is no
- * {@link ContentProvider} associated with the given intent this will throw
- * {@link IllegalArgumentException}.
- *
- * @param context The context to use.
- * @param intent The intent associated with a slice.
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
- * @see SliceProvider#onMapIntentToUri(Intent)
- * @see Intent
+ * @deprecated TO BE REMOVED.
*/
+ @Deprecated
public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
- List<SliceSpec> supportedSpecs) {
- Preconditions.checkNotNull(intent, "intent");
- Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
- "Slice intent must be explicit " + intent);
- ContentResolver resolver = context.getContentResolver();
-
- // Check if the intent has data for the slice uri on it and use that
- final Uri intentData = intent.getData();
- if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return bindSlice(resolver, intentData, supportedSpecs);
- }
- // Otherwise ask the app
- List<ResolveInfo> providers =
- context.getPackageManager().queryIntentContentProviders(intent, 0);
- if (providers == null) {
- throw new IllegalArgumentException("Unable to resolve intent " + intent);
- }
- String authority = providers.get(0).providerInfo.authority;
- Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
- extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
- new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(),
- SliceProvider.METHOD_MAP_INTENT, null, extras);
- if (res == null) {
- return null;
- }
- return res.getParcelable(SliceProvider.EXTRA_SLICE);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- resolver.releaseProvider(provider);
- }
+ @NonNull List<SliceSpec> supportedSpecs) {
+ return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
}
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 0c5f225d515e..74864cb1a371 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -17,17 +17,29 @@
package android.app.slice;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Pair;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -39,6 +51,8 @@ import java.util.concurrent.Executor;
@SystemService(Context.SLICE_SERVICE)
public class SliceManager {
+ private static final String TAG = "SliceManager";
+
private final ISliceManager mService;
private final Context mContext;
private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
@@ -224,6 +238,126 @@ public class SliceManager {
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Not all slice providers will implement this functionality, in which case,
+ * an empty collection will be returned.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceProvider#onGetSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get slice descendants", e);
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Turns a slice Uri into slice content.
+ *
+ * @param uri The URI to a slice provider
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ */
+ public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(uri, "uri");
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+ null, extras);
+ Bundle.setDefusable(res, true);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Turns a slice intent into slice content. Expects an explicit intent. If there is no
+ * {@link android.content.ContentProvider} associated with the given intent this will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param intent The intent associated with a slice.
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @see SliceProvider#onMapIntentToUri(Intent)
+ * @see Intent
+ */
+ public @Nullable Slice bindSlice(@NonNull Intent intent,
+ @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(intent, "intent");
+ Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+ "Slice intent must be explicit " + intent);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // Check if the intent has data for the slice uri on it and use that
+ final Uri intentData = intent.getData();
+ if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+ return bindSlice(intentData, supportedSpecs);
+ }
+ // Otherwise ask the app
+ List<ResolveInfo> providers =
+ mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+ if (providers == null) {
+ throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ }
+ String authority = providers.get(0).providerInfo.authority;
+ Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).build();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_MAP_INTENT, null, extras);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
* Class that listens to changes in {@link Slice}s.
*/
public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 8483931ceaec..aa41f14d8cb5 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -36,6 +36,9 @@ import android.os.StrictMode.ThreadPolicy;
import android.os.UserHandle;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -113,11 +116,19 @@ public abstract class SliceProvider extends ContentProvider {
/**
* @hide
*/
+ public static final String METHOD_GET_DESCENDANTS = "get_descendants";
+ /**
+ * @hide
+ */
public static final String EXTRA_INTENT = "slice_intent";
/**
* @hide
*/
public static final String EXTRA_SLICE = "slice";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
private static final boolean DEBUG = false;
@@ -139,14 +150,6 @@ public abstract class SliceProvider extends ContentProvider {
* @see {@link Slice#HINT_PARTIAL}
*/
public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
- return onBindSlice(sliceUri);
- }
-
- /**
- * @deprecated migrating to {@link #onBindSlice(Uri, List)}
- */
- @Deprecated
- public Slice onBindSlice(Uri sliceUri) {
return null;
}
@@ -183,6 +186,20 @@ public abstract class SliceProvider extends ContentProvider {
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Implementing this is optional for a SliceProvider, but does provide a good
+ * discovery mechanism for finding slice Uris.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceManager#getSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> onGetSliceDescendants(@NonNull Uri uri) {
+ return Collections.emptyList();
+ }
+
+ /**
* This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
* In that case, this method can be called and is expected to return a non-null Uri representing
* a slice. Otherwise this will throw {@link UnsupportedOperationException}.
@@ -290,10 +307,35 @@ public abstract class SliceProvider extends ContentProvider {
"Slice binding requires the permission BIND_SLICE");
}
handleUnpinSlice(uri);
+ } else if (method.equals(METHOD_GET_DESCENDANTS)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Bundle b = new Bundle();
+ b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
+ new ArrayList<>(handleGetDescendants(uri)));
+ return b;
}
return super.call(method, arg, extras);
}
+ private Collection<Uri> handleGetDescendants(Uri uri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return onGetSliceDescendants(uri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Collection<Uri>[] output = new Collection[1];
+ Handler.getMain().post(() -> {
+ output[0] = onGetSliceDescendants(uri);
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ return output[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private void handlePinSlice(Uri sliceUri) {
if (Looper.myLooper() == Looper.getMainLooper()) {
onSlicePinned(sliceUri);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index deb8dfb11094..44b4a33976d0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2535,31 +2535,22 @@ public abstract class PackageManager {
* Devices declaring this feature must include an application implementing a
* {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
* {@link android.app.Activity#setVrModeEnabled}.
+ * @deprecated use {@link #FEATURE_VR_MODE_HIGH_PERFORMANCE} instead.
*/
+ @Deprecated
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE = "android.software.vr.mode";
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device implements {@link #FEATURE_VR_MODE} but additionally meets extra CDD requirements
- * to provide a high-quality VR experience. In general, devices declaring this feature will
- * additionally:
- * <ul>
- * <li>Deliver consistent performance at a high framerate over an extended period of time
- * for typical VR application CPU/GPU workloads with a minimal number of frame drops for VR
- * applications that have called
- * {@link android.view.Window#setSustainedPerformanceMode}.</li>
- * <li>Implement {@link #FEATURE_HIFI_SENSORS} and have a low sensor latency.</li>
- * <li>Include optimizations to lower display persistence while running VR applications.</li>
- * <li>Implement an optimized render path to minimize latency to draw to the device's main
- * display.</li>
- * <li>Include the following EGL extensions: EGL_ANDROID_create_native_client_buffer,
- * EGL_ANDROID_front_buffer_auto_refresh, EGL_EXT_protected_content,
- * EGL_KHR_mutable_render_buffer, EGL_KHR_reusable_sync, and EGL_KHR_wait_sync.</li>
- * <li>Provide at least one CPU core that is reserved for use solely by the top, foreground
- * VR application process for critical render threads while such an application is
- * running.</li>
- * </ul>
+ * The device implements an optimized mode for virtual reality (VR) applications that handles
+ * stereoscopic rendering of notifications, disables most monocular system UI components
+ * while a VR application has user focus and meets extra CDD requirements to provide a
+ * high-quality VR experience.
+ * Devices declaring this feature must include an application implementing a
+ * {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
+ * {@link android.app.Activity#setVrModeEnabled}.
+ * and must meet CDD requirements to provide a high-quality VR experience.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a18f22e1184b..6d6c02a47082 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6285,6 +6285,31 @@ public class PackageParser {
+ " " + packageName + "}";
}
+ public String dumpState_temp() {
+ String flags = "";
+ flags += ((applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
+ flags += ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
+ if ("".equals(flags)) {
+ flags = "-";
+ }
+ String privFlags = "";
+ privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
+ privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
+ privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
+ if ("".equals(privFlags)) {
+ privFlags = "-";
+ }
+ return "Package{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + packageName
+ + ", ver:" + getLongVersionCode()
+ + ", path: " + codePath
+ + ", flags: " + flags
+ + ", privFlags: " + privFlags
+ + ", extra: " + (mExtras == null ? "<<NULL>>" : Integer.toHexString(System.identityHashCode(mExtras)) + "}")
+ + "}";
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 3f6dd2e757ed..078958ad881a 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -179,6 +179,11 @@ public abstract class DisplayManagerInternal {
public abstract void persistBrightnessSliderEvents();
/**
+ * Notifies the display manager that resource overlays have changed.
+ */
+ public abstract void onOverlayChanged();
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index e941279f4d25..7528bc3989c2 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1082,33 +1082,6 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Close/hide the input method's soft input area, so the user no longer
- * sees it or can interact with it. This can only be called
- * from the currently active input method, as validated by the given token.
- *
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY},
- * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
- */
- public void hideSoftInputFromInputMethod(int flags) {
- mImm.hideSoftInputFromInputMethodInternal(mToken, flags);
- }
-
- /**
- * Show the input method's soft input area, so the user
- * sees the input method window and can interact with it.
- * This can only be called from the currently active input method,
- * as validated by the given token.
- *
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} or
- * {@link InputMethodManager#SHOW_FORCED} bit set.
- */
- public void showSoftInputFromInputMethod(int flags) {
- mImm.showSoftInputFromInputMethodInternal(mToken, flags);
- }
-
- /**
* Force switch to the last used input method and subtype. If the last input method didn't have
* any subtypes, the framework will simply switch to the last input method with no subtype
* specified.
@@ -1738,7 +1711,7 @@ public class InputMethodService extends AbstractInputMethodService {
// Rethrow the exception to preserve the existing behavior. Some IMEs may have directly
// called this method and relied on this exception for some clean-up tasks.
// TODO: Give developers a clear guideline of whether it's OK to call this method or
- // InputMethodManager#showSoftInputFromInputMethod() should always be used instead.
+ // InputMethodService#requestShowSelf(int) should always be used instead.
throw e;
} finally {
// TODO: Is it OK to set true when we get BadTokenException?
@@ -2060,27 +2033,30 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* Close this input method's soft input area, removing it from the display.
- * The input method will continue running, but the user can no longer use
- * it to generate input by touching the screen.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
- * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
+ *
+ * The input method will continue running, but the user can no longer use it to generate input
+ * by touching the screen.
+ *
+ * @see InputMethodManager#HIDE_IMPLICIT_ONLY
+ * @see InputMethodManager#HIDE_NOT_ALWAYS
+ * @param flags Provides additional operating flags.
*/
public void requestHideSelf(int flags) {
- mImm.hideSoftInputFromInputMethod(mToken, flags);
+ mImm.hideSoftInputFromInputMethodInternal(mToken, flags);
}
-
+
/**
- * Show the input method. This is a call back to the
- * IMF to handle showing the input method.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#SHOW_FORCED
- * InputMethodManager.} bit set.
+ * Show the input method's soft input area, so the user sees the input method window and can
+ * interact with it.
+ *
+ * @see InputMethodManager#SHOW_IMPLICIT
+ * @see InputMethodManager#SHOW_FORCED
+ * @param flags Provides additional operating flags.
*/
- private void requestShowSelf(int flags) {
- mImm.showSoftInputFromInputMethod(mToken, flags);
+ public void requestShowSelf(int flags) {
+ mImm.showSoftInputFromInputMethodInternal(mToken, flags);
}
-
+
private boolean handleBack(boolean doIt) {
if (mShowInputRequested) {
// If the soft input area is shown, back closes it and we
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index d9b57db18071..790c80b1d934 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -31,7 +31,7 @@ import android.os.ParcelFileDescriptor;
interface IIpSecService
{
IpSecSpiResponse allocateSecurityParameterIndex(
- int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
+ in String destinationAddress, int requestedSpi, in IBinder binder);
void releaseSecurityParameterIndex(int resourceId);
@@ -43,7 +43,7 @@ interface IIpSecService
void deleteTransportModeTransform(int transformId);
- void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+ void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
- void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+ void removeTransportModeTransforms(in ParcelFileDescriptor socket);
}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 7d752e89e6f6..c69a4d4c0bee 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -256,13 +256,19 @@ public final class IpSecAlgorithm implements Parcelable {
return getName().equals(AUTH_CRYPT_AES_GCM);
}
+ // Because encryption keys are sensitive and userdebug builds are used by large user pools
+ // such as beta testers, we only allow sensitive info such as keys on eng builds.
+ private static boolean isUnsafeBuild() {
+ return Build.IS_DEBUGGABLE && Build.IS_ENG;
+ }
+
@Override
public String toString() {
return new StringBuilder()
.append("{mName=")
.append(mName)
.append(", mKey=")
- .append(Build.IS_DEBUGGABLE ? HexDump.toHexString(mKey) : "<hidden>")
+ .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
.append(", mTruncLenBits=")
.append(mTruncLenBits)
.append("}")
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index f54ceb5c142a..80b0af33735b 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -32,59 +32,29 @@ public final class IpSecConfig implements Parcelable {
// MODE_TRANSPORT or MODE_TUNNEL
private int mMode = IpSecTransform.MODE_TRANSPORT;
- // Needs to be valid only for tunnel mode
// Preventing this from being null simplifies Java->Native binder
- private String mLocalAddress = "";
+ private String mSourceAddress = "";
// Preventing this from being null simplifies Java->Native binder
- private String mRemoteAddress = "";
+ private String mDestinationAddress = "";
// The underlying Network that represents the "gateway" Network
// for outbound packets. It may also be used to select packets.
private Network mNetwork;
- /**
- * This class captures the parameters that specifically apply to inbound or outbound traffic.
- */
- public static class Flow {
- // Minimum requirements for identifying a transform
- // SPI identifying the IPsec flow in packet processing
- // and a remote IP address
- private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
-
- // Encryption Algorithm
- private IpSecAlgorithm mEncryption;
-
- // Authentication Algorithm
- private IpSecAlgorithm mAuthentication;
-
- // Authenticated Encryption Algorithm
- private IpSecAlgorithm mAuthenticatedEncryption;
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{mSpiResourceId=")
- .append(mSpiResourceId)
- .append(", mEncryption=")
- .append(mEncryption)
- .append(", mAuthentication=")
- .append(mAuthentication)
- .append(", mAuthenticatedEncryption=")
- .append(mAuthenticatedEncryption)
- .append("}")
- .toString();
- }
-
- static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
- if (lhs == null || rhs == null) return (lhs == rhs);
- return (lhs.mSpiResourceId == rhs.mSpiResourceId
- && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
- && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
- }
- }
+ // Minimum requirements for identifying a transform
+ // SPI identifying the IPsec SA in packet processing
+ // and a destination IP address
+ private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
+
+ // Encryption Algorithm
+ private IpSecAlgorithm mEncryption;
+
+ // Authentication Algorithm
+ private IpSecAlgorithm mAuthentication;
- private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()};
+ // Authenticated Encryption Algorithm
+ private IpSecAlgorithm mAuthenticatedEncryption;
// For tunnel mode IPv4 UDP Encapsulation
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
@@ -100,36 +70,37 @@ public final class IpSecConfig implements Parcelable {
mMode = mode;
}
- /** Set the local IP address for Tunnel mode */
- public void setLocalAddress(String localAddress) {
- mLocalAddress = localAddress;
+ /** Set the source IP addres for this IPsec transform */
+ public void setSourceAddress(String sourceAddress) {
+ mSourceAddress = sourceAddress;
}
- /** Set the remote IP address for this IPsec transform */
- public void setRemoteAddress(String remoteAddress) {
- mRemoteAddress = remoteAddress;
+ /** Set the destination IP address for this IPsec transform */
+ public void setDestinationAddress(String destinationAddress) {
+ mDestinationAddress = destinationAddress;
}
- /** Set the SPI for a given direction by resource ID */
- public void setSpiResourceId(int direction, int resourceId) {
- mFlow[direction].mSpiResourceId = resourceId;
+ /** Set the SPI by resource ID */
+ public void setSpiResourceId(int resourceId) {
+ mSpiResourceId = resourceId;
}
- /** Set the encryption algorithm for a given direction */
- public void setEncryption(int direction, IpSecAlgorithm encryption) {
- mFlow[direction].mEncryption = encryption;
+ /** Set the encryption algorithm */
+ public void setEncryption(IpSecAlgorithm encryption) {
+ mEncryption = encryption;
}
- /** Set the authentication algorithm for a given direction */
- public void setAuthentication(int direction, IpSecAlgorithm authentication) {
- mFlow[direction].mAuthentication = authentication;
+ /** Set the authentication algorithm */
+ public void setAuthentication(IpSecAlgorithm authentication) {
+ mAuthentication = authentication;
}
- /** Set the authenticated encryption algorithm for a given direction */
- public void setAuthenticatedEncryption(int direction, IpSecAlgorithm authenticatedEncryption) {
- mFlow[direction].mAuthenticatedEncryption = authenticatedEncryption;
+ /** Set the authenticated encryption algorithm */
+ public void setAuthenticatedEncryption(IpSecAlgorithm authenticatedEncryption) {
+ mAuthenticatedEncryption = authenticatedEncryption;
}
+ /** Set the underlying network that will carry traffic for this transform */
public void setNetwork(Network network) {
mNetwork = network;
}
@@ -155,28 +126,28 @@ public final class IpSecConfig implements Parcelable {
return mMode;
}
- public String getLocalAddress() {
- return mLocalAddress;
+ public String getSourceAddress() {
+ return mSourceAddress;
}
- public int getSpiResourceId(int direction) {
- return mFlow[direction].mSpiResourceId;
+ public int getSpiResourceId() {
+ return mSpiResourceId;
}
- public String getRemoteAddress() {
- return mRemoteAddress;
+ public String getDestinationAddress() {
+ return mDestinationAddress;
}
- public IpSecAlgorithm getEncryption(int direction) {
- return mFlow[direction].mEncryption;
+ public IpSecAlgorithm getEncryption() {
+ return mEncryption;
}
- public IpSecAlgorithm getAuthentication(int direction) {
- return mFlow[direction].mAuthentication;
+ public IpSecAlgorithm getAuthentication() {
+ return mAuthentication;
}
- public IpSecAlgorithm getAuthenticatedEncryption(int direction) {
- return mFlow[direction].mAuthenticatedEncryption;
+ public IpSecAlgorithm getAuthenticatedEncryption() {
+ return mAuthenticatedEncryption;
}
public Network getNetwork() {
@@ -209,17 +180,13 @@ public final class IpSecConfig implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mMode);
- out.writeString(mLocalAddress);
- out.writeString(mRemoteAddress);
+ out.writeString(mSourceAddress);
+ out.writeString(mDestinationAddress);
out.writeParcelable(mNetwork, flags);
- out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption, flags);
- out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption, flags);
+ out.writeInt(mSpiResourceId);
+ out.writeParcelable(mEncryption, flags);
+ out.writeParcelable(mAuthentication, flags);
+ out.writeParcelable(mAuthenticatedEncryption, flags);
out.writeInt(mEncapType);
out.writeInt(mEncapSocketResourceId);
out.writeInt(mEncapRemotePort);
@@ -231,22 +198,15 @@ public final class IpSecConfig implements Parcelable {
private IpSecConfig(Parcel in) {
mMode = in.readInt();
- mLocalAddress = in.readString();
- mRemoteAddress = in.readString();
+ mSourceAddress = in.readString();
+ mDestinationAddress = in.readString();
mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
- mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
- mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
+ mSpiResourceId = in.readInt();
+ mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
+ mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption =
+ mAuthenticatedEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
mEncapType = in.readInt();
mEncapSocketResourceId = in.readInt();
@@ -260,10 +220,10 @@ public final class IpSecConfig implements Parcelable {
strBuilder
.append("{mMode=")
.append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
- .append(", mLocalAddress=")
- .append(mLocalAddress)
- .append(", mRemoteAddress=")
- .append(mRemoteAddress)
+ .append(", mSourceAddress=")
+ .append(mSourceAddress)
+ .append(", mDestinationAddress=")
+ .append(mDestinationAddress)
.append(", mNetwork=")
.append(mNetwork)
.append(", mEncapType=")
@@ -274,10 +234,14 @@ public final class IpSecConfig implements Parcelable {
.append(mEncapRemotePort)
.append(", mNattKeepaliveInterval=")
.append(mNattKeepaliveInterval)
- .append(", mFlow[OUT]=")
- .append(mFlow[IpSecTransform.DIRECTION_OUT])
- .append(", mFlow[IN]=")
- .append(mFlow[IpSecTransform.DIRECTION_IN])
+ .append("{mSpiResourceId=")
+ .append(mSpiResourceId)
+ .append(", mEncryption=")
+ .append(mEncryption)
+ .append(", mAuthentication=")
+ .append(mAuthentication)
+ .append(", mAuthenticatedEncryption=")
+ .append(mAuthenticatedEncryption)
.append("}");
return strBuilder.toString();
@@ -299,17 +263,18 @@ public final class IpSecConfig implements Parcelable {
public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
if (lhs == null || rhs == null) return (lhs == rhs);
return (lhs.mMode == rhs.mMode
- && lhs.mLocalAddress.equals(rhs.mLocalAddress)
- && lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
+ && lhs.mSourceAddress.equals(rhs.mSourceAddress)
+ && lhs.mDestinationAddress.equals(rhs.mDestinationAddress)
&& ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
|| (lhs.mNetwork == rhs.mNetwork))
&& lhs.mEncapType == rhs.mEncapType
&& lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
&& lhs.mEncapRemotePort == rhs.mEncapRemotePort
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
- && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
- rhs.mFlow[IpSecTransform.DIRECTION_OUT])
- && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
- rhs.mFlow[IpSecTransform.DIRECTION_IN]));
+ && lhs.mSpiResourceId == rhs.mSpiResourceId
+ && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
+ && IpSecAlgorithm.equals(
+ lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+ && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
}
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 34cfa9b2153d..2cda58c99a61 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,6 +17,7 @@ package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -33,6 +34,8 @@ import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
@@ -53,6 +56,23 @@ public final class IpSecManager {
private static final String TAG = "IpSecManager";
/**
+ * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+ * applies to traffic towards the host.
+ */
+ public static final int DIRECTION_IN = 0;
+
+ /**
+ * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+ * applies to traffic from the host.
+ */
+ public static final int DIRECTION_OUT = 1;
+
+ /** @hide */
+ @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PolicyDirection {}
+
+ /**
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
*
* <p>No IPsec packet may contain an SPI of 0.
@@ -125,7 +145,7 @@ public final class IpSecManager {
*/
public static final class SecurityParameterIndex implements AutoCloseable {
private final IIpSecService mService;
- private final InetAddress mRemoteAddress;
+ private final InetAddress mDestinationAddress;
private final CloseGuard mCloseGuard = CloseGuard.get();
private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
private int mResourceId = INVALID_RESOURCE_ID;
@@ -164,14 +184,14 @@ public final class IpSecManager {
}
private SecurityParameterIndex(
- @NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)
+ @NonNull IIpSecService service, InetAddress destinationAddress, int spi)
throws ResourceUnavailableException, SpiUnavailableException {
mService = service;
- mRemoteAddress = remoteAddress;
+ mDestinationAddress = destinationAddress;
try {
IpSecSpiResponse result =
mService.allocateSecurityParameterIndex(
- direction, remoteAddress.getHostAddress(), spi, new Binder());
+ destinationAddress.getHostAddress(), spi, new Binder());
if (result == null) {
throw new NullPointerException("Received null response from IpSecService");
@@ -216,25 +236,23 @@ public final class IpSecManager {
}
/**
- * Reserve a random SPI for traffic bound to or from the specified remote address.
+ * Reserve a random SPI for traffic bound to or from the specified destination address.
*
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
* SecurityParameterIndex#close()}.
*
- * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
- * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+ * @param destinationAddress the destination address for traffic bearing the requested SPI.
+ * For inbound traffic, the destination should be an address currently assigned on-device.
* @return the reserved SecurityParameterIndex
- * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
- * for this user
- * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
+ * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * currently allocated for this user
*/
- public SecurityParameterIndex allocateSecurityParameterIndex(
- int direction, InetAddress remoteAddress) throws ResourceUnavailableException {
+ public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
+ throws ResourceUnavailableException {
try {
return new SecurityParameterIndex(
mService,
- direction,
- remoteAddress,
+ destinationAddress,
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
} catch (SpiUnavailableException unlikely) {
throw new ResourceUnavailableException("No SPIs available");
@@ -242,26 +260,27 @@ public final class IpSecManager {
}
/**
- * Reserve the requested SPI for traffic bound to or from the specified remote address.
+ * Reserve the requested SPI for traffic bound to or from the specified destination address.
*
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
* SecurityParameterIndex#close()}.
*
- * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
- * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+ * @param destinationAddress the destination address for traffic bearing the requested SPI.
+ * For inbound traffic, the destination should be an address currently assigned on-device.
* @param requestedSpi the requested SPI, or '0' to allocate a random SPI
* @return the reserved SecurityParameterIndex
- * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
- * for this user
- * @throws SpiUnavailableException indicating that the requested SPI could not be reserved
+ * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * currently allocated for this user
+ * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
+ * reserved
*/
public SecurityParameterIndex allocateSecurityParameterIndex(
- int direction, InetAddress remoteAddress, int requestedSpi)
+ InetAddress destinationAddress, int requestedSpi)
throws SpiUnavailableException, ResourceUnavailableException {
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
}
- return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi);
+ return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
}
/**
@@ -269,14 +288,14 @@ public final class IpSecManager {
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -287,15 +306,14 @@ public final class IpSecManager {
* in-flight packets have been received.
*
* @param socket a stream socket
+ * @param direction the policy direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
- * @hide
*/
- public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
+ public void applyTransportModeTransform(
+ Socket socket, int direction, IpSecTransform transform)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
+ applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
/**
@@ -303,14 +321,14 @@ public final class IpSecManager {
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -321,15 +339,13 @@ public final class IpSecManager {
* in-flight packets have been received.
*
* @param socket a datagram socket
+ * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
- * @hide
*/
- public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
- throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
+ public void applyTransportModeTransform(
+ DatagramSocket socket, int direction, IpSecTransform transform) throws IOException {
+ applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
/**
@@ -337,14 +353,14 @@ public final class IpSecManager {
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -355,24 +371,17 @@ public final class IpSecManager {
* in-flight packets have been received.
*
* @param socket a socket file descriptor
+ * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
*/
- public void applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+ public void applyTransportModeTransform(
+ FileDescriptor socket, int direction, IpSecTransform transform)
throws IOException {
// We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
- // constructor takes control and closes the user's FD when we exit the method
- // This is behaviorally the same as the other versions, but the PFD constructor does not
- // dup() automatically, whereas PFD.fromSocket() and PDF.fromDatagramSocket() do dup().
+ // constructor takes control and closes the user's FD when we exit the method.
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
- }
-
- /* Call down to activate a transform */
- private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
- try {
- mService.applyTransportModeTransform(pfd, transform.getResourceId());
+ mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -396,75 +405,56 @@ public final class IpSecManager {
/**
* Remove an IPsec transform from a stream socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
- * @hide
*/
- public void removeTransportModeTransform(Socket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(Socket socket)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a datagram socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
- * @hide
*/
- public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(DatagramSocket socket)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(FileDescriptor socket)
throws IOException {
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
- }
-
- /* Call down to remove a transform */
- private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
- try {
- mService.removeTransportModeTransform(pfd, transform.getResourceId());
+ mService.removeTransportModeTransforms(pfd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 102ba6d94faa..7b9b4830929d 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -38,13 +38,11 @@ import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
/**
- * This class represents an IPsec transform, which comprises security associations in one or both
- * directions.
+ * This class represents a transform, which roughly corresponds to an IPsec Security Association.
*
* <p>Transforms are created using {@link IpSecTransform.Builder}. Each {@code IpSecTransform}
- * object encapsulates the properties and state of an inbound and outbound IPsec security
- * association. That includes, but is not limited to, algorithm choice, key material, and allocated
- * system resources.
+ * object encapsulates the properties and state of an IPsec security association. That includes,
+ * but is not limited to, algorithm choice, key material, and allocated system resources.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
* Internet Protocol</a>
@@ -52,23 +50,6 @@ import java.net.InetAddress;
public final class IpSecTransform implements AutoCloseable {
private static final String TAG = "IpSecTransform";
- /**
- * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
- * applies to traffic towards the host.
- */
- public static final int DIRECTION_IN = 0;
-
- /**
- * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
- * applies to traffic from the host.
- */
- public static final int DIRECTION_OUT = 1;
-
- /** @hide */
- @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface TransformDirection {}
-
/** @hide */
public static final int MODE_TRANSPORT = 0;
@@ -170,7 +151,7 @@ public final class IpSecTransform implements AutoCloseable {
*
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
* that socket. Make sure to remove transforms by calling {@link
- * IpSecManager#removeTransportModeTransform}. Note, removing an {@code IpSecTransform} from a
+ * IpSecManager#removeTransportModeTransforms}. Note, removing an {@code IpSecTransform} from a
* socket will not deactivate it (because one transform may be applied to multiple sockets).
*
* <p>It is safe to call this method on a transform that has already been deactivated.
@@ -272,85 +253,49 @@ public final class IpSecTransform implements AutoCloseable {
private IpSecConfig mConfig;
/**
- * Set the encryption algorithm for the given direction.
- *
- * <p>If encryption is set for a direction without also providing an SPI for that direction,
- * creation of an {@code IpSecTransform} will fail when attempting to build the transform.
+ * Set the encryption algorithm.
*
* <p>Encryption is mutually exclusive with authenticated encryption.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
*/
- public IpSecTransform.Builder setEncryption(
- @TransformDirection int direction, IpSecAlgorithm algo) {
+ public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
- mConfig.setEncryption(direction, algo);
+ Preconditions.checkNotNull(algo);
+ mConfig.setEncryption(algo);
return this;
}
/**
- * Set the authentication (integrity) algorithm for the given direction.
- *
- * <p>If authentication is set for a direction without also providing an SPI for that
- * direction, creation of an {@code IpSecTransform} will fail when attempting to build the
- * transform.
+ * Set the authentication (integrity) algorithm.
*
* <p>Authentication is mutually exclusive with authenticated encryption.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
*/
- public IpSecTransform.Builder setAuthentication(
- @TransformDirection int direction, IpSecAlgorithm algo) {
+ public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
- mConfig.setAuthentication(direction, algo);
+ Preconditions.checkNotNull(algo);
+ mConfig.setAuthentication(algo);
return this;
}
/**
- * Set the authenticated encryption algorithm for the given direction.
- *
- * <p>If an authenticated encryption algorithm is set for a given direction without also
- * providing an SPI for that direction, creation of an {@code IpSecTransform} will fail when
- * attempting to build the transform.
+ * Set the authenticated encryption algorithm.
*
- * <p>The Authenticated Encryption (AE) class of algorithms are also known as Authenticated
- * Encryption with Associated Data (AEAD) algorithms, or Combined mode algorithms (as
- * referred to in <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
+ * <p>The Authenticated Encryption (AE) class of algorithms are also known as
+ * Authenticated Encryption with Associated Data (AEAD) algorithms, or Combined mode
+ * algorithms (as referred to in
+ * <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
*
* <p>Authenticated encryption is mutually exclusive with encryption and authentication.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
* be applied.
*/
- public IpSecTransform.Builder setAuthenticatedEncryption(
- @TransformDirection int direction, IpSecAlgorithm algo) {
- mConfig.setAuthenticatedEncryption(direction, algo);
- return this;
- }
-
- /**
- * Set the SPI for the given direction.
- *
- * <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies
- * packets to a given destination address. To prevent SPI collisions, values should be
- * reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}.
- *
- * <p>If the SPI and algorithms are omitted for one direction, traffic in that direction
- * will not be encrypted or authenticated.
- *
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
- * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
- * traffic
- */
- public IpSecTransform.Builder setSpi(
- @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
- if (spi.getResourceId() == INVALID_RESOURCE_ID) {
- throw new IllegalArgumentException("Invalid SecurityParameterIndex");
- }
- mConfig.setSpiResourceId(direction, spi.getResourceId());
+ public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
+ Preconditions.checkNotNull(algo);
+ mConfig.setAuthenticatedEncryption(algo);
return this;
}
@@ -363,7 +308,8 @@ public final class IpSecTransform implements AutoCloseable {
* @hide
*/
@SystemApi
- public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
+ public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) {
+ Preconditions.checkNotNull(net);
mConfig.setNetwork(net);
return this;
}
@@ -382,7 +328,8 @@ public final class IpSecTransform implements AutoCloseable {
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
*/
public IpSecTransform.Builder setIpv4Encapsulation(
- IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+ @NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+ Preconditions.checkNotNull(localSocket);
mConfig.setEncapType(ENCAP_ESPINUDP);
if (localSocket.getResourceId() == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Invalid UdpEncapsulationSocket");
@@ -419,24 +366,33 @@ public final class IpSecTransform implements AutoCloseable {
* will not affect any network traffic until it has been applied to one or more sockets.
*
* @see IpSecManager#applyTransportModeTransform
- * @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use
- * this transform
+ * @param sourceAddress the source {@code InetAddress} of traffic on sockets that will use
+ * this transform; this address must belong to the Network used by all sockets that
+ * utilize this transform; if provided, then only traffic originating from the
+ * specified source address will be processed.
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid
- * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms are
- * active
+ * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+ * are active
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
* collides with an existing transform
* @throws IOException indicating other errors
*/
- public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
+ public IpSecTransform buildTransportModeTransform(
+ @NonNull InetAddress sourceAddress,
+ @NonNull IpSecManager.SecurityParameterIndex spi)
throws IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException, IOException {
- if (remoteAddress == null) {
- throw new IllegalArgumentException("Remote address may not be null or empty!");
+ Preconditions.checkNotNull(sourceAddress);
+ Preconditions.checkNotNull(spi);
+ if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+ throw new IllegalArgumentException("Invalid SecurityParameterIndex");
}
mConfig.setMode(MODE_TRANSPORT);
- mConfig.setRemoteAddress(remoteAddress.getHostAddress());
+ mConfig.setSourceAddress(sourceAddress.getHostAddress());
+ mConfig.setSpiResourceId(spi.getResourceId());
// FIXME: modifying a builder after calling build can change the built transform.
return new IpSecTransform(mContext, mConfig).activate();
}
@@ -445,26 +401,33 @@ public final class IpSecTransform implements AutoCloseable {
* Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
* parameters have interdependencies that are checked at build time.
*
- * @param localAddress the {@link InetAddress} that provides the local endpoint for this
+ * @param sourceAddress the {@link InetAddress} that provides the source address for this
* IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
* that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
- * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
- * IPsec tunnel.
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid.
+ * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+ * are active
+ * @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
+ * collides with an existing transform
+ * @throws IOException indicating other errors
* @hide
*/
public IpSecTransform buildTunnelModeTransform(
- InetAddress localAddress, InetAddress remoteAddress) {
- if (localAddress == null) {
- throw new IllegalArgumentException("Local address may not be null or empty!");
- }
- if (remoteAddress == null) {
- throw new IllegalArgumentException("Remote address may not be null or empty!");
+ @NonNull InetAddress sourceAddress,
+ @NonNull IpSecManager.SecurityParameterIndex spi)
+ throws IpSecManager.ResourceUnavailableException,
+ IpSecManager.SpiUnavailableException, IOException {
+ Preconditions.checkNotNull(sourceAddress);
+ Preconditions.checkNotNull(spi);
+ if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+ throw new IllegalArgumentException("Invalid SecurityParameterIndex");
}
- mConfig.setLocalAddress(localAddress.getHostAddress());
- mConfig.setRemoteAddress(remoteAddress.getHostAddress());
mConfig.setMode(MODE_TUNNEL);
+ mConfig.setSourceAddress(sourceAddress.getHostAddress());
+ mConfig.setSpiResourceId(spi.getResourceId());
return new IpSecTransform(mContext, mConfig);
}
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index d6992aaede5f..287bdc88dd3e 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,7 +61,7 @@ public final class MacAddress implements Parcelable {
})
public @interface MacAddressType { }
- /** Indicates a MAC address of unknown type. */
+ /** @hide Indicates a MAC address of unknown type. */
public static final int TYPE_UNKNOWN = 0;
/** Indicates a MAC address is a unicast address. */
public static final int TYPE_UNICAST = 1;
@@ -92,7 +93,7 @@ public final class MacAddress implements Parcelable {
*
* @return the int constant representing the MAC address type of this MacAddress.
*/
- public @MacAddressType int addressType() {
+ public @MacAddressType int getAddressType() {
if (equals(BROADCAST_ADDRESS)) {
return TYPE_BROADCAST;
}
@@ -120,12 +121,12 @@ public final class MacAddress implements Parcelable {
/**
* @return a byte array representation of this MacAddress.
*/
- public byte[] toByteArray() {
+ public @NonNull byte[] toByteArray() {
return byteAddrFromLongAddr(mAddr);
}
@Override
- public String toString() {
+ public @NonNull String toString() {
return stringAddrFromLongAddr(mAddr);
}
@@ -133,7 +134,7 @@ public final class MacAddress implements Parcelable {
* @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal
* numbers in [0,ff] joined by ':' characters.
*/
- public String toOuiString() {
+ public @NonNull String toOuiString() {
return String.format(
"%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff);
}
@@ -197,7 +198,7 @@ public final class MacAddress implements Parcelable {
if (!isMacAddress(addr)) {
return TYPE_UNKNOWN;
}
- return MacAddress.fromBytes(addr).addressType();
+ return MacAddress.fromBytes(addr).getAddressType();
}
/**
@@ -211,7 +212,7 @@ public final class MacAddress implements Parcelable {
*
* @hide
*/
- public static byte[] byteAddrFromStringAddr(String addr) {
+ public static @NonNull byte[] byteAddrFromStringAddr(String addr) {
Preconditions.checkNotNull(addr);
String[] parts = addr.split(":");
if (parts.length != ETHER_ADDR_LEN) {
@@ -239,7 +240,7 @@ public final class MacAddress implements Parcelable {
*
* @hide
*/
- public static String stringAddrFromByteAddr(byte[] addr) {
+ public static @NonNull String stringAddrFromByteAddr(byte[] addr) {
if (!isMacAddress(addr)) {
return null;
}
@@ -291,7 +292,7 @@ public final class MacAddress implements Parcelable {
// Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
// that avoids the allocation of an intermediary byte[].
- private static String stringAddrFromLongAddr(long addr) {
+ private static @NonNull String stringAddrFromLongAddr(long addr) {
return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
(addr >> 40) & 0xff,
(addr >> 32) & 0xff,
@@ -310,7 +311,7 @@ public final class MacAddress implements Parcelable {
* @return the MacAddress corresponding to the given String representation.
* @throws IllegalArgumentException if the given String is not a valid representation.
*/
- public static MacAddress fromString(String addr) {
+ public static @NonNull MacAddress fromString(@NonNull String addr) {
return new MacAddress(longAddrFromStringAddr(addr));
}
@@ -322,7 +323,7 @@ public final class MacAddress implements Parcelable {
* @return the MacAddress corresponding to the given byte array representation.
* @throws IllegalArgumentException if the given byte array is not a valid representation.
*/
- public static MacAddress fromBytes(byte[] addr) {
+ public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) {
return new MacAddress(longAddrFromByteAddr(addr));
}
@@ -336,7 +337,7 @@ public final class MacAddress implements Parcelable {
*
* @hide
*/
- public static MacAddress createRandomUnicastAddress() {
+ public static @NonNull MacAddress createRandomUnicastAddress() {
return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
}
@@ -352,7 +353,7 @@ public final class MacAddress implements Parcelable {
*
* @hide
*/
- public static MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
+ public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
addr = addr | LOCALLY_ASSIGNED_MASK;
addr = addr & ~MULTICAST_MASK;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 1a3ce9124b48..5df168d20586 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -357,13 +357,13 @@ public class Network implements Parcelable {
// Multiple Provisioning Domains API recommendations, as made by the
// IETF mif working group.
//
- // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+ // The handleMagic value MUST be kept in sync with the corresponding
// value in the native/android/net.c NDK implementation.
if (netId == 0) {
return 0L; // make this zero condition obvious for debugging
}
- final long HANDLE_MAGIC = 0xfacade;
- return (((long) netId) << 32) | HANDLE_MAGIC;
+ final long handleMagic = 0xcafed00dL;
+ return (((long) netId) << 32) | handleMagic;
}
// implement the Parcelable interface
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 81c49a339d53..9ef26a9f5a5b 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -29,7 +29,6 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.telephony.SubscriptionPlan;
import android.util.DebugUtils;
import android.util.Pair;
@@ -329,7 +328,7 @@ public class NetworkPolicyManager {
* to access network when the device is idle or in battery saver mode. Otherwise, false.
*/
public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
- return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ return procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
}
/**
@@ -337,7 +336,7 @@ public class NetworkPolicyManager {
* to access network when the device is in data saver mode. Otherwise, false.
*/
public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
- return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ return procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
}
public static String resolveNetworkId(WifiConfiguration config) {
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 7277ba34534b..bb36536fe2ce 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -80,7 +80,7 @@ public class WakeupStats {
break;
}
- switch (ev.dstHwAddr.addressType()) {
+ switch (ev.dstHwAddr.getAddressType()) {
case MacAddress.TYPE_UNICAST:
l2UnicastCount++;
break;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 21974843613f..7b0c153f5234 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -238,6 +238,20 @@ public class UserManager {
public static final String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display";
/**
+ * Specifies if a user is disallowed from changing screen off timeout.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>This user restriction has no effect on managed profiles.
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
+
+ /**
* Specifies if a user is disallowed from enabling the
* "Unknown Sources" setting, that allows installation of apps from unknown sources.
* The default value is <code>false</code>.
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 070b8c1b0008..839a8bf42b10 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -394,4 +394,32 @@ public final class StorageVolume implements Parcelable {
parcel.writeString(mFsUuid);
parcel.writeString(mState);
}
+
+ /** {@hide} */
+ public static final class ScopedAccessProviderContract {
+
+ private ScopedAccessProviderContract() {
+ throw new UnsupportedOperationException("contains constants only");
+ }
+
+ public static final String AUTHORITY = "com.android.documentsui.scopedAccess";
+
+ public static final String TABLE_PACKAGES = "packages";
+ public static final String TABLE_PERMISSIONS = "permissions";
+
+ public static final String COL_PACKAGE = "package_name";
+ public static final String COL_VOLUME_UUID = "volume_uuid";
+ public static final String COL_DIRECTORY = "directory";
+ public static final String COL_GRANTED = "granted";
+
+ public static final String[] TABLE_PACKAGES_COLUMNS = new String[] { COL_PACKAGE };
+ public static final String[] TABLE_PERMISSIONS_COLUMNS =
+ new String[] { COL_PACKAGE, COL_VOLUME_UUID, COL_DIRECTORY, COL_GRANTED };
+
+ public static final int TABLE_PACKAGES_COL_PACKAGE = 0;
+ public static final int TABLE_PERMISSIONS_COL_PACKAGE = 0;
+ public static final int TABLE_PERMISSIONS_COL_VOLUME_UUID = 1;
+ public static final int TABLE_PERMISSIONS_COL_DIRECTORY = 2;
+ public static final int TABLE_PERMISSIONS_COL_GRANTED = 3;
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 850aedd517a9..24e56c0598b2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6830,7 +6830,7 @@ public final class Settings {
* @hide
*/
public static final int SHOW_ROTATION_SUGGESTIONS_DEFAULT =
- SHOW_ROTATION_SUGGESTIONS_DISABLED;
+ SHOW_ROTATION_SUGGESTIONS_ENABLED;
/**
* Read only list of the service components that the current user has explicitly allowed to
@@ -9852,6 +9852,15 @@ public final class Settings {
public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
/**
+ * Whether or not to enable Forced App Standby on small battery devices.
+ * Type: int (0 for false, 1 for true)
+ * Default: 0
+ * @hide
+ */
+ public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
+ = "forced_app_standby_for_small_battery_enabled";
+
+ /**
* Whether or not Network Watchlist feature is enabled.
* Type: int (0 for false, 1 for true)
* Default: 0
@@ -11300,6 +11309,13 @@ public final class Settings {
*/
public static final String ZRAM_ENABLED =
"zram_enabled";
+
+ /**
+ * Whether smart replies in notifications are enabled.
+ * @hide
+ */
+ public static final String ENABLE_SMART_REPLIES_IN_NOTIFICATIONS =
+ "enable_smart_replies_in_notifications";
}
/**
diff --git a/core/java/android/security/keystore/RecoveryData.aidl b/core/java/android/security/keystore/KeychainProtectionParameter.aidl
index 4200de1637a8..1e2c365d4e69 100644
--- a/core/java/android/security/keystore/RecoveryData.aidl
+++ b/core/java/android/security/keystore/KeychainProtectionParameter.aidl
@@ -17,4 +17,4 @@
package android.security.keystore;
/* @hide */
-parcelable RecoveryData;
+parcelable KeychainProtectionParameter;
diff --git a/core/java/android/security/keystore/RecoveryMetadata.java b/core/java/android/security/keystore/KeychainProtectionParameter.java
index 3f0945557a5f..2319ef5e3129 100644
--- a/core/java/android/security/keystore/RecoveryMetadata.java
+++ b/core/java/android/security/keystore/KeychainProtectionParameter.java
@@ -28,12 +28,26 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
/**
- * Helper class with data necessary to recover Keystore on a new device.
- * It defines UI shown to the user and a way to derive a cryptographic key from user output.
+ * A {@link KeychainSnapshot} is protected with a key derived from the user's lock screen. This
+ * class wraps all the data necessary to derive the same key on a recovering device:
+ *
+ * <ul>
+ * <li>UI parameters for the user's lock screen - so that if e.g., the user was using a pattern,
+ * the recovering device can display the pattern UI to the user when asking them to enter
+ * the lock screen from their previous device.
+ * <li>The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt.
+ * </ul>
+ *
+ * <p>As such, this data is sent along with the {@link KeychainSnapshot} when syncing the current
+ * version of the keychain.
+ *
+ * <p>For now, the recoverable keychain only supports a single layer of protection, which is the
+ * user's lock screen. In the future, the keychain will support multiple layers of protection
+ * (e.g. an additional keychain password, along with the lock screen).
*
* @hide
*/
-public final class RecoveryMetadata implements Parcelable {
+public final class KeychainProtectionParameter implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
@@ -88,7 +102,7 @@ public final class RecoveryMetadata implements Parcelable {
* @link {#clearSecret} to overwrite its value in memory.
* @hide
*/
- public RecoveryMetadata(@UserSecretType int userSecretType,
+ public KeychainProtectionParameter(@UserSecretType int userSecretType,
@LockScreenUiFormat int lockScreenUiFormat,
@NonNull KeyDerivationParams keyDerivationParams,
@NonNull byte[] secret) {
@@ -98,7 +112,7 @@ public final class RecoveryMetadata implements Parcelable {
mSecret = Preconditions.checkNotNull(secret);
}
- private RecoveryMetadata() {
+ private KeychainProtectionParameter() {
}
@@ -141,10 +155,10 @@ public final class RecoveryMetadata implements Parcelable {
}
/**
- * Builder for creating {@link RecoveryMetadata}.
+ * Builder for creating {@link KeychainProtectionParameter}.
*/
public static class Builder {
- private RecoveryMetadata mInstance = new RecoveryMetadata();
+ private KeychainProtectionParameter mInstance = new KeychainProtectionParameter();
/**
* Sets user secret type.
@@ -198,14 +212,14 @@ public final class RecoveryMetadata implements Parcelable {
/**
- * Creates a new {@link RecoveryMetadata} instance.
+ * Creates a new {@link KeychainProtectionParameter} instance.
* The instance will include default values, if {@link setSecret}
* or {@link setUserSecretType} were not called.
*
* @return new instance
* @throws NullPointerException if some required fields were not set.
*/
- public @NonNull RecoveryMetadata build() {
+ @NonNull public KeychainProtectionParameter build() {
if (mInstance.mUserSecretType == null) {
mInstance.mUserSecretType = TYPE_LOCKSCREEN;
}
@@ -235,14 +249,14 @@ public final class RecoveryMetadata implements Parcelable {
Arrays.fill(mSecret, (byte) 0);
}
- public static final Parcelable.Creator<RecoveryMetadata> CREATOR =
- new Parcelable.Creator<RecoveryMetadata>() {
- public RecoveryMetadata createFromParcel(Parcel in) {
- return new RecoveryMetadata(in);
+ public static final Parcelable.Creator<KeychainProtectionParameter> CREATOR =
+ new Parcelable.Creator<KeychainProtectionParameter>() {
+ public KeychainProtectionParameter createFromParcel(Parcel in) {
+ return new KeychainProtectionParameter(in);
}
- public RecoveryMetadata[] newArray(int length) {
- return new RecoveryMetadata[length];
+ public KeychainProtectionParameter[] newArray(int length) {
+ return new KeychainProtectionParameter[length];
}
};
@@ -260,7 +274,7 @@ public final class RecoveryMetadata implements Parcelable {
/**
* @hide
*/
- protected RecoveryMetadata(Parcel in) {
+ protected KeychainProtectionParameter(Parcel in) {
mUserSecretType = in.readInt();
mLockScreenUiFormat = in.readInt();
mKeyDerivationParams = in.readTypedObject(KeyDerivationParams.CREATOR);
diff --git a/core/java/android/security/keystore/RecoveryMetadata.aidl b/core/java/android/security/keystore/KeychainSnapshot.aidl
index 8e342b485044..b35713f329d6 100644
--- a/core/java/android/security/keystore/RecoveryMetadata.aidl
+++ b/core/java/android/security/keystore/KeychainSnapshot.aidl
@@ -17,4 +17,4 @@
package android.security.keystore;
/* @hide */
-parcelable RecoveryMetadata;
+parcelable KeychainSnapshot;
diff --git a/core/java/android/security/keystore/RecoveryData.java b/core/java/android/security/keystore/KeychainSnapshot.java
index 897aa18a0e18..71a808a41f57 100644
--- a/core/java/android/security/keystore/RecoveryData.java
+++ b/core/java/android/security/keystore/KeychainSnapshot.java
@@ -25,42 +25,48 @@ import com.android.internal.util.Preconditions;
import java.util.List;
/**
- * Helper class which returns data necessary to recover keys.
- * Contains
+ * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot:
*
* <ul>
- * <li>Snapshot version.
- * <li>Recovery metadata with UI and key derivation parameters.
- * <li>List of application keys encrypted by recovery key.
- * <li>Encrypted recovery key.
+ * <li>The user's lock screen changes. (A key derived from the user's lock screen is used to
+ * protected the keychain, which is why this forces a new snapshot.)
+ * <li>A key is added to or removed from the recoverable keychain.
* </ul>
*
+ * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even
+ * the recovery agent itself should not be able to decipher the data. The recovery agent sends an
+ * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a
+ * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then
+ * sends it to the framework, where it is decrypted using the user's lock screen from their previous
+ * device.
+ *
* @hide
*/
-public final class RecoveryData implements Parcelable {
+public final class KeychainSnapshot implements Parcelable {
private int mSnapshotVersion;
- private List<RecoveryMetadata> mRecoveryMetadata;
- private List<EntryRecoveryData> mEntryRecoveryData;
+ private List<KeychainProtectionParameter> mKeychainProtectionParams;
+ private List<WrappedApplicationKey> mEntryRecoveryData;
private byte[] mEncryptedRecoveryKeyBlob;
/**
* @hide
* Deprecated, consider using builder.
*/
- public RecoveryData(
+ public KeychainSnapshot(
int snapshotVersion,
- @NonNull List<RecoveryMetadata> recoveryMetadata,
- @NonNull List<EntryRecoveryData> entryRecoveryData,
+ @NonNull List<KeychainProtectionParameter> keychainProtectionParams,
+ @NonNull List<WrappedApplicationKey> wrappedApplicationKeys,
@NonNull byte[] encryptedRecoveryKeyBlob) {
mSnapshotVersion = snapshotVersion;
- mRecoveryMetadata =
- Preconditions.checkCollectionElementsNotNull(recoveryMetadata, "recoveryMetadata");
- mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(entryRecoveryData,
- "entryRecoveryData");
+ mKeychainProtectionParams =
+ Preconditions.checkCollectionElementsNotNull(keychainProtectionParams,
+ "keychainProtectionParams");
+ mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys,
+ "wrappedApplicationKeys");
mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
}
- private RecoveryData() {
+ private KeychainSnapshot() {
}
@@ -75,15 +81,15 @@ public final class RecoveryData implements Parcelable {
/**
* UI and key derivation parameters. Note that combination of secrets may be used.
*/
- public @NonNull List<RecoveryMetadata> getRecoveryMetadata() {
- return mRecoveryMetadata;
+ public @NonNull List<KeychainProtectionParameter> getKeychainProtectionParams() {
+ return mKeychainProtectionParams;
}
/**
* List of application keys, with key material encrypted by
* the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
*/
- public @NonNull List<EntryRecoveryData> getEntryRecoveryData() {
+ public @NonNull List<WrappedApplicationKey> getWrappedApplicationKeys() {
return mEntryRecoveryData;
}
@@ -94,22 +100,22 @@ public final class RecoveryData implements Parcelable {
return mEncryptedRecoveryKeyBlob;
}
- public static final Parcelable.Creator<RecoveryData> CREATOR =
- new Parcelable.Creator<RecoveryData>() {
- public RecoveryData createFromParcel(Parcel in) {
- return new RecoveryData(in);
+ public static final Parcelable.Creator<KeychainSnapshot> CREATOR =
+ new Parcelable.Creator<KeychainSnapshot>() {
+ public KeychainSnapshot createFromParcel(Parcel in) {
+ return new KeychainSnapshot(in);
}
- public RecoveryData[] newArray(int length) {
- return new RecoveryData[length];
+ public KeychainSnapshot[] newArray(int length) {
+ return new KeychainSnapshot[length];
}
};
/**
- * Builder for creating {@link RecoveryData}.
+ * Builder for creating {@link KeychainSnapshot}.
*/
public static class Builder {
- private RecoveryData mInstance = new RecoveryData();
+ private KeychainSnapshot mInstance = new KeychainSnapshot();
/**
* Snapshot version for given account.
@@ -128,8 +134,9 @@ public final class RecoveryData implements Parcelable {
* @param recoveryMetadata The UI and key derivation parameters
* @return This builder.
*/
- public Builder setRecoveryMetadata(@NonNull List<RecoveryMetadata> recoveryMetadata) {
- mInstance.mRecoveryMetadata = recoveryMetadata;
+ public Builder setKeychainProtectionParams(
+ @NonNull List<KeychainProtectionParameter> recoveryMetadata) {
+ mInstance.mKeychainProtectionParams = recoveryMetadata;
return this;
}
@@ -139,7 +146,7 @@ public final class RecoveryData implements Parcelable {
* @param entryRecoveryData List of application keys
* @return This builder.
*/
- public Builder setEntryRecoveryData(List<EntryRecoveryData> entryRecoveryData) {
+ public Builder setWrappedApplicationKeys(List<WrappedApplicationKey> entryRecoveryData) {
mInstance.mEntryRecoveryData = entryRecoveryData;
return this;
}
@@ -157,13 +164,13 @@ public final class RecoveryData implements Parcelable {
/**
- * Creates a new {@link RecoveryData} instance.
+ * Creates a new {@link KeychainSnapshot} instance.
*
* @return new instance
* @throws NullPointerException if some required fields were not set.
*/
- public @NonNull RecoveryData build() {
- Preconditions.checkCollectionElementsNotNull(mInstance.mRecoveryMetadata,
+ @NonNull public KeychainSnapshot build() {
+ Preconditions.checkCollectionElementsNotNull(mInstance.mKeychainProtectionParams,
"recoveryMetadata");
Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
"entryRecoveryData");
@@ -178,7 +185,7 @@ public final class RecoveryData implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mSnapshotVersion);
- out.writeTypedList(mRecoveryMetadata);
+ out.writeTypedList(mKeychainProtectionParams);
out.writeByteArray(mEncryptedRecoveryKeyBlob);
out.writeTypedList(mEntryRecoveryData);
}
@@ -186,11 +193,11 @@ public final class RecoveryData implements Parcelable {
/**
* @hide
*/
- protected RecoveryData(Parcel in) {
+ protected KeychainSnapshot(Parcel in) {
mSnapshotVersion = in.readInt();
- mRecoveryMetadata = in.createTypedArrayList(RecoveryMetadata.CREATOR);
+ mKeychainProtectionParams = in.createTypedArrayList(KeychainProtectionParameter.CREATOR);
mEncryptedRecoveryKeyBlob = in.createByteArray();
- mEntryRecoveryData = in.createTypedArrayList(EntryRecoveryData.CREATOR);
+ mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR);
}
@Override
diff --git a/core/java/android/security/keystore/RecoveryManager.java b/core/java/android/security/keystore/RecoveryManager.java
index 99bd284e4d80..bddf3e849182 100644
--- a/core/java/android/security/keystore/RecoveryManager.java
+++ b/core/java/android/security/keystore/RecoveryManager.java
@@ -99,11 +99,11 @@ public class RecoveryManager {
* @return Data necessary to recover keystore.
* @hide
*/
- public @NonNull RecoveryData getRecoveryData(@NonNull byte[] account)
+ @NonNull public KeychainSnapshot getRecoveryData(@NonNull byte[] account)
throws RecoveryManagerException {
try {
- RecoveryData recoveryData = mBinder.getRecoveryData(account);
- return recoveryData;
+ KeychainSnapshot keychainSnapshot = mBinder.getRecoveryData(account);
+ return keychainSnapshot;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -136,7 +136,7 @@ public class RecoveryManager {
* version. Version zero is used, if no snapshots were created for the account.
*
* @return Map from recovery agent accounts to snapshot versions.
- * @see RecoveryData#getSnapshotVersion
+ * @see KeychainSnapshot#getSnapshotVersion
* @hide
*/
public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
@@ -156,7 +156,7 @@ public class RecoveryManager {
/**
* Server parameters used to generate new recovery key blobs. This value will be included in
- * {@code RecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
+ * {@code KeychainSnapshot.getEncryptedRecoveryKeyBlob()}. The same value must be included
* in vaultParams {@link #startRecoverySession}
*
* @param serverParams included in recovery key blob.
@@ -230,11 +230,11 @@ public class RecoveryManager {
* Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
* is necessary to recover data.
*
- * @param secretTypes {@link RecoveryMetadata#TYPE_LOCKSCREEN} or {@link
- * RecoveryMetadata#TYPE_CUSTOM_PASSWORD}
+ * @param secretTypes {@link KeychainProtectionParameter#TYPE_LOCKSCREEN} or {@link
+ * KeychainProtectionParameter#TYPE_CUSTOM_PASSWORD}
*/
public void setRecoverySecretTypes(
- @NonNull @RecoveryMetadata.UserSecretType int[] secretTypes)
+ @NonNull @KeychainProtectionParameter.UserSecretType int[] secretTypes)
throws RecoveryManagerException {
try {
mBinder.setRecoverySecretTypes(secretTypes);
@@ -247,12 +247,12 @@ public class RecoveryManager {
/**
* Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
- * necessary to generate RecoveryData.
+ * necessary to generate KeychainSnapshot.
*
* @return list of recovery secret types
- * @see RecoveryData
+ * @see KeychainSnapshot
*/
- public @NonNull @RecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
+ @NonNull public @KeychainProtectionParameter.UserSecretType int[] getRecoverySecretTypes()
throws RecoveryManagerException {
try {
return mBinder.getRecoverySecretTypes();
@@ -271,7 +271,8 @@ public class RecoveryManager {
* @return list of recovery secret types
* @hide
*/
- public @NonNull @RecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
+ @NonNull
+ public @KeychainProtectionParameter.UserSecretType int[] getPendingRecoverySecretTypes()
throws RecoveryManagerException {
try {
return mBinder.getPendingRecoverySecretTypes();
@@ -285,14 +286,14 @@ public class RecoveryManager {
/**
* Method notifies KeyStore that a user-generated secret is available. This method generates a
* symmetric session key which a trusted remote device can use to return a recovery key. Caller
- * should use {@link RecoveryMetadata#clearSecret} to override the secret value in
+ * should use {@link KeychainProtectionParameter#clearSecret} to override the secret value in
* memory.
*
* @param recoverySecret user generated secret together with parameters necessary to regenerate
* it on a new device.
* @hide
*/
- public void recoverySecretAvailable(@NonNull RecoveryMetadata recoverySecret)
+ public void recoverySecretAvailable(@NonNull KeychainProtectionParameter recoverySecret)
throws RecoveryManagerException {
try {
mBinder.recoverySecretAvailable(recoverySecret);
@@ -326,7 +327,7 @@ public class RecoveryManager {
@NonNull byte[] verifierPublicKey,
@NonNull byte[] vaultParams,
@NonNull byte[] vaultChallenge,
- @NonNull List<RecoveryMetadata> secrets)
+ @NonNull List<KeychainProtectionParameter> secrets)
throws RecoveryManagerException {
try {
byte[] recoveryClaim =
@@ -352,13 +353,13 @@ public class RecoveryManager {
* @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
* @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
* and session. KeyStore only uses package names from the application info in {@link
- * EntryRecoveryData}. Caller is responsibility to perform certificates check.
+ * WrappedApplicationKey}. Caller is responsibility to perform certificates check.
* @return Map from alias to raw key material.
*/
public Map<String, byte[]> recoverKeys(
@NonNull String sessionId,
@NonNull byte[] recoveryKeyBlob,
- @NonNull List<EntryRecoveryData> applicationKeys)
+ @NonNull List<WrappedApplicationKey> applicationKeys)
throws RecoveryManagerException {
try {
return (Map<String, byte[]>) mBinder.recoverKeys(
diff --git a/core/java/android/security/keystore/EntryRecoveryData.aidl b/core/java/android/security/keystore/WrappedApplicationKey.aidl
index c6c20e337bc8..a6294fee03b3 100644
--- a/core/java/android/security/keystore/EntryRecoveryData.aidl
+++ b/core/java/android/security/keystore/WrappedApplicationKey.aidl
@@ -17,4 +17,4 @@
package android.security.keystore;
/* @hide */
-parcelable EntryRecoveryData;
+parcelable WrappedApplicationKey;
diff --git a/core/java/android/security/keystore/EntryRecoveryData.java b/core/java/android/security/keystore/WrappedApplicationKey.java
index aaca3fe8b2cf..522bb9557b8d 100644
--- a/core/java/android/security/keystore/EntryRecoveryData.java
+++ b/core/java/android/security/keystore/WrappedApplicationKey.java
@@ -35,16 +35,16 @@ import com.android.internal.util.Preconditions;
*
* @hide
*/
-public final class EntryRecoveryData implements Parcelable {
+public final class WrappedApplicationKey implements Parcelable {
private String mAlias;
// The only supported format is AES-256 symmetric key.
private byte[] mEncryptedKeyMaterial;
/**
- * Builder for creating {@link EntryRecoveryData}.
+ * Builder for creating {@link WrappedApplicationKey}.
*/
public static class Builder {
- private EntryRecoveryData mInstance = new EntryRecoveryData();
+ private WrappedApplicationKey mInstance = new WrappedApplicationKey();
/**
* Sets Application-specific alias of the key.
@@ -70,19 +70,19 @@ public final class EntryRecoveryData implements Parcelable {
}
/**
- * Creates a new {@link EntryRecoveryData} instance.
+ * Creates a new {@link WrappedApplicationKey} instance.
*
* @return new instance
* @throws NullPointerException if some required fields were not set.
*/
- public @NonNull EntryRecoveryData build() {
+ @NonNull public WrappedApplicationKey build() {
Preconditions.checkNotNull(mInstance.mAlias);
Preconditions.checkNotNull(mInstance.mEncryptedKeyMaterial);
return mInstance;
}
}
- private EntryRecoveryData() {
+ private WrappedApplicationKey() {
}
@@ -90,7 +90,7 @@ public final class EntryRecoveryData implements Parcelable {
* Deprecated - consider using Builder.
* @hide
*/
- public EntryRecoveryData(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
+ public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
mAlias = Preconditions.checkNotNull(alias);
mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
}
@@ -109,14 +109,14 @@ public final class EntryRecoveryData implements Parcelable {
return mEncryptedKeyMaterial;
}
- public static final Parcelable.Creator<EntryRecoveryData> CREATOR =
- new Parcelable.Creator<EntryRecoveryData>() {
- public EntryRecoveryData createFromParcel(Parcel in) {
- return new EntryRecoveryData(in);
+ public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
+ new Parcelable.Creator<WrappedApplicationKey>() {
+ public WrappedApplicationKey createFromParcel(Parcel in) {
+ return new WrappedApplicationKey(in);
}
- public EntryRecoveryData[] newArray(int length) {
- return new EntryRecoveryData[length];
+ public WrappedApplicationKey[] newArray(int length) {
+ return new WrappedApplicationKey[length];
}
};
@@ -132,7 +132,7 @@ public final class EntryRecoveryData implements Parcelable {
/**
* @hide
*/
- protected EntryRecoveryData(Parcel in) {
+ protected WrappedApplicationKey(Parcel in) {
mAlias = in.readString();
mEncryptedKeyMaterial = in.createByteArray();
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 20cd90679195..b7b2b2de35e5 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -55,6 +55,7 @@ import android.util.Log;
import android.widget.RemoteViews;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import java.lang.annotation.Retention;
@@ -1543,7 +1544,11 @@ public abstract class NotificationListenerService extends Service {
return mShowBadge;
}
- private void populate(String key, int rank, boolean matchesInterruptionFilter,
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void populate(String key, int rank, boolean matchesInterruptionFilter,
int visibilityOverride, int suppressedVisualEffects, int importance,
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index bf4b6ac5194f..aa97b2aba749 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1917,10 +1917,10 @@ public abstract class Layout {
private static float measurePara(TextPaint paint, CharSequence text, int start, int end,
TextDirectionHeuristic textDir) {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
TextLine tl = TextLine.obtain();
try {
- mt = MeasuredText.buildForBidi(text, start, end, textDir, mt);
+ mt = MeasuredParagraph.buildForBidi(text, start, end, textDir, mt);
final char[] chars = mt.getChars();
final int len = chars.length;
final Directions directions = mt.getDirections(0, len);
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
new file mode 100644
index 000000000000..c93e0365d58d
--- /dev/null
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Paint;
+import android.text.AutoGrowArray.ByteArray;
+import android.text.AutoGrowArray.FloatArray;
+import android.text.AutoGrowArray.IntArray;
+import android.text.Layout.Directions;
+import android.text.style.MetricAffectingSpan;
+import android.text.style.ReplacementSpan;
+import android.util.Pools.SynchronizedPool;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.Arrays;
+
+/**
+ * MeasuredParagraph provides text information for rendering purpose.
+ *
+ * The first motivation of this class is identify the text directions and retrieving individual
+ * character widths. However retrieving character widths is slower than identifying text directions.
+ * Thus, this class provides several builder methods for specific purposes.
+ *
+ * - buildForBidi:
+ * Compute only text directions.
+ * - buildForMeasurement:
+ * Compute text direction and all character widths.
+ * - buildForStaticLayout:
+ * This is bit special. StaticLayout also needs to know text direction and character widths for
+ * line breaking, but all things are done in native code. Similarly, text measurement is done
+ * in native code. So instead of storing result to Java array, this keeps the result in native
+ * code since there is no good reason to move the results to Java layer.
+ *
+ * In addition to the character widths, some additional information is computed for each purposes,
+ * e.g. whole text length for measurement or font metrics for static layout.
+ *
+ * MeasuredParagraph is NOT a thread safe object.
+ * @hide
+ */
+public class MeasuredParagraph {
+ private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
+ private MeasuredParagraph() {} // Use build static functions instead.
+
+ private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
+
+ private static @NonNull MeasuredParagraph obtain() { // Use build static functions instead.
+ final MeasuredParagraph mt = sPool.acquire();
+ return mt != null ? mt : new MeasuredParagraph();
+ }
+
+ /**
+ * Recycle the MeasuredParagraph.
+ *
+ * Do not call any methods after you call this method.
+ */
+ public void recycle() {
+ release();
+ sPool.release(this);
+ }
+
+ // The casted original text.
+ //
+ // This may be null if the passed text is not a Spanned.
+ private @Nullable Spanned mSpanned;
+
+ // The start offset of the target range in the original text (mSpanned);
+ private @IntRange(from = 0) int mTextStart;
+
+ // The length of the target range in the original text.
+ private @IntRange(from = 0) int mTextLength;
+
+ // The copied character buffer for measuring text.
+ //
+ // The length of this array is mTextLength.
+ private @Nullable char[] mCopiedBuffer;
+
+ // The whole paragraph direction.
+ private @Layout.Direction int mParaDir;
+
+ // True if the text is LTR direction and doesn't contain any bidi characters.
+ private boolean mLtrWithoutBidi;
+
+ // The bidi level for individual characters.
+ //
+ // This is empty if mLtrWithoutBidi is true.
+ private @NonNull ByteArray mLevels = new ByteArray();
+
+ // The whole width of the text.
+ // See getWholeWidth comments.
+ private @FloatRange(from = 0.0f) float mWholeWidth;
+
+ // Individual characters' widths.
+ // See getWidths comments.
+ private @Nullable FloatArray mWidths = new FloatArray();
+
+ // The span end positions.
+ // See getSpanEndCache comments.
+ private @Nullable IntArray mSpanEndCache = new IntArray(4);
+
+ // The font metrics.
+ // See getFontMetrics comments.
+ private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
+
+ // The native MeasuredParagraph.
+ // See getNativePtr comments.
+ // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
+ private /* Maybe Zero */ long mNativePtr = 0;
+ private @Nullable Runnable mNativeObjectCleaner;
+
+ // Associate the native object to this Java object.
+ private void bindNativeObject(/* Non Zero*/ long nativePtr) {
+ mNativePtr = nativePtr;
+ mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
+ }
+
+ // Decouple the native object from this Java object and release the native object.
+ private void unbindNativeObject() {
+ if (mNativePtr != 0) {
+ mNativeObjectCleaner.run();
+ mNativePtr = 0;
+ }
+ }
+
+ // Following two objects are for avoiding object allocation.
+ private @NonNull TextPaint mCachedPaint = new TextPaint();
+ private @Nullable Paint.FontMetricsInt mCachedFm;
+
+ /**
+ * Releases internal buffers.
+ */
+ public void release() {
+ reset();
+ mLevels.clearWithReleasingLargeArray();
+ mWidths.clearWithReleasingLargeArray();
+ mFontMetrics.clearWithReleasingLargeArray();
+ mSpanEndCache.clearWithReleasingLargeArray();
+ }
+
+ /**
+ * Resets the internal state for starting new text.
+ */
+ private void reset() {
+ mSpanned = null;
+ mCopiedBuffer = null;
+ mWholeWidth = 0;
+ mLevels.clear();
+ mWidths.clear();
+ mFontMetrics.clear();
+ mSpanEndCache.clear();
+ unbindNativeObject();
+ }
+
+ /**
+ * Returns the characters to be measured.
+ *
+ * This is always available.
+ */
+ public @NonNull char[] getChars() {
+ return mCopiedBuffer;
+ }
+
+ /**
+ * Returns the paragraph direction.
+ *
+ * This is always available.
+ */
+ public @Layout.Direction int getParagraphDir() {
+ return mParaDir;
+ }
+
+ /**
+ * Returns the directions.
+ *
+ * This is always available.
+ */
+ public Directions getDirections(@IntRange(from = 0) int start, // inclusive
+ @IntRange(from = 0) int end) { // exclusive
+ if (mLtrWithoutBidi) {
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ }
+
+ final int length = end - start;
+ return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
+ length);
+ }
+
+ /**
+ * Returns the whole text width.
+ *
+ * This is available only if the MeasureText is computed with computeForMeasurement.
+ * Returns 0 in other cases.
+ */
+ public @FloatRange(from = 0.0f) float getWholeWidth() {
+ return mWholeWidth;
+ }
+
+ /**
+ * Returns the individual character's width.
+ *
+ * This is available only if the MeasureText is computed with computeForMeasurement.
+ * Returns empty array in other cases.
+ */
+ public @NonNull FloatArray getWidths() {
+ return mWidths;
+ }
+
+ /**
+ * Returns the MetricsAffectingSpan end indices.
+ *
+ * If the input text is not a spanned string, this has one value that is the length of the text.
+ *
+ * This is available only if the MeasureText is computed with computeForStaticLayout.
+ * Returns empty array in other cases.
+ */
+ public @NonNull IntArray getSpanEndCache() {
+ return mSpanEndCache;
+ }
+
+ /**
+ * Returns the int array which holds FontMetrics.
+ *
+ * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
+ *
+ * This is available only if the MeasureText is computed with computeForStaticLayout.
+ * Returns empty array in other cases.
+ */
+ public @NonNull IntArray getFontMetrics() {
+ return mFontMetrics;
+ }
+
+ /**
+ * Returns the native ptr of the MeasuredParagraph.
+ *
+ * This is available only if the MeasureText is computed with computeForStaticLayout.
+ * Returns 0 in other cases.
+ */
+ public /* Maybe Zero */ long getNativePtr() {
+ return mNativePtr;
+ }
+
+ /**
+ * Generates new MeasuredParagraph for Bidi computation.
+ *
+ * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+ * result to recycle and returns recycle.
+ *
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ * @param recycle pass existing MeasuredParagraph if you want to recycle it.
+ *
+ * @return measured text
+ */
+ public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ @Nullable MeasuredParagraph recycle) {
+ final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
+ mt.resetAndAnalyzeBidi(text, start, end, textDir);
+ return mt;
+ }
+
+ /**
+ * Generates new MeasuredParagraph for measuring texts.
+ *
+ * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+ * result to recycle and returns recycle.
+ *
+ * @param paint the paint to be used for rendering the text.
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ * @param recycle pass existing MeasuredParagraph if you want to recycle it.
+ *
+ * @return measured text
+ */
+ public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint,
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ @Nullable MeasuredParagraph recycle) {
+ final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
+ mt.resetAndAnalyzeBidi(text, start, end, textDir);
+
+ mt.mWidths.resize(mt.mTextLength);
+ if (mt.mTextLength == 0) {
+ return mt;
+ }
+
+ if (mt.mSpanned == null) {
+ // No style change by MetricsAffectingSpan. Just measure all text.
+ mt.applyMetricsAffectingSpan(
+ paint, null /* spans */, start, end, 0 /* native static layout ptr */);
+ } else {
+ // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
+ int spanEnd;
+ for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
+ spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
+ MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
+ MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
+ mt.applyMetricsAffectingSpan(
+ paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+ }
+ }
+ return mt;
+ }
+
+ /**
+ * Generates new MeasuredParagraph for StaticLayout.
+ *
+ * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+ * result to recycle and returns recycle.
+ *
+ * @param paint the paint to be used for rendering the text.
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ * @param recycle pass existing MeasuredParagraph if you want to recycle it.
+ *
+ * @return measured text
+ */
+ public static @NonNull MeasuredParagraph buildForStaticLayout(
+ @NonNull TextPaint paint,
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ @Nullable MeasuredParagraph recycle) {
+ final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
+ mt.resetAndAnalyzeBidi(text, start, end, textDir);
+ if (mt.mTextLength == 0) {
+ // Need to build empty native measured text for StaticLayout.
+ // TODO: Stop creating empty measured text for empty lines.
+ long nativeBuilderPtr = nInitBuilder();
+ try {
+ mt.bindNativeObject(
+ nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
+ } finally {
+ nFreeBuilder(nativeBuilderPtr);
+ }
+ return mt;
+ }
+
+ long nativeBuilderPtr = nInitBuilder();
+ try {
+ if (mt.mSpanned == null) {
+ // No style change by MetricsAffectingSpan. Just measure all text.
+ mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
+ mt.mSpanEndCache.append(end);
+ } else {
+ // There may be a MetricsAffectingSpan. Split into span transitions and apply
+ // styles.
+ int spanEnd;
+ for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
+ spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
+ MetricAffectingSpan.class);
+ MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
+ MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
+ MetricAffectingSpan.class);
+ mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
+ nativeBuilderPtr);
+ mt.mSpanEndCache.append(spanEnd);
+ }
+ }
+ mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
+ } finally {
+ nFreeBuilder(nativeBuilderPtr);
+ }
+
+ return mt;
+ }
+
+ /**
+ * Reset internal state and analyzes text for bidirectional runs.
+ *
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ */
+ private void resetAndAnalyzeBidi(@NonNull CharSequence text,
+ @IntRange(from = 0) int start, // inclusive
+ @IntRange(from = 0) int end, // exclusive
+ @NonNull TextDirectionHeuristic textDir) {
+ reset();
+ mSpanned = text instanceof Spanned ? (Spanned) text : null;
+ mTextStart = start;
+ mTextLength = end - start;
+
+ if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
+ mCopiedBuffer = new char[mTextLength];
+ }
+ TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
+
+ // Replace characters associated with ReplacementSpan to U+FFFC.
+ if (mSpanned != null) {
+ ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
+
+ for (int i = 0; i < spans.length; i++) {
+ int startInPara = mSpanned.getSpanStart(spans[i]) - start;
+ int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
+ // The span interval may be larger and must be restricted to [start, end)
+ if (startInPara < 0) startInPara = 0;
+ if (endInPara > mTextLength) endInPara = mTextLength;
+ Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
+ }
+ }
+
+ if ((textDir == TextDirectionHeuristics.LTR
+ || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
+ || textDir == TextDirectionHeuristics.ANYRTL_LTR)
+ && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
+ mLevels.clear();
+ mParaDir = Layout.DIR_LEFT_TO_RIGHT;
+ mLtrWithoutBidi = true;
+ } else {
+ final int bidiRequest;
+ if (textDir == TextDirectionHeuristics.LTR) {
+ bidiRequest = Layout.DIR_REQUEST_LTR;
+ } else if (textDir == TextDirectionHeuristics.RTL) {
+ bidiRequest = Layout.DIR_REQUEST_RTL;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
+ bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
+ bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
+ } else {
+ final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
+ bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
+ }
+ mLevels.resize(mTextLength);
+ mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
+ mLtrWithoutBidi = false;
+ }
+ }
+
+ private void applyReplacementRun(@NonNull ReplacementSpan replacement,
+ @IntRange(from = 0) int start, // inclusive, in copied buffer
+ @IntRange(from = 0) int end, // exclusive, in copied buffer
+ /* Maybe Zero */ long nativeBuilderPtr) {
+ // Use original text. Shouldn't matter.
+ // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
+ // backward compatibility? or Should we initialize them for getFontMetricsInt?
+ final float width = replacement.getSize(
+ mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
+ if (nativeBuilderPtr == 0) {
+ // Assigns all width to the first character. This is the same behavior as minikin.
+ mWidths.set(start, width);
+ if (end > start + 1) {
+ Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
+ }
+ mWholeWidth += width;
+ } else {
+ nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
+ width);
+ }
+ }
+
+ private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
+ @IntRange(from = 0) int end, // exclusive, in copied buffer
+ /* Maybe Zero */ long nativeBuilderPtr) {
+ if (nativeBuilderPtr != 0) {
+ mCachedPaint.getFontMetricsInt(mCachedFm);
+ }
+
+ if (mLtrWithoutBidi) {
+ // If the whole text is LTR direction, just apply whole region.
+ if (nativeBuilderPtr == 0) {
+ mWholeWidth += mCachedPaint.getTextRunAdvances(
+ mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
+ mWidths.getRawArray(), start);
+ } else {
+ nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
+ false /* isRtl */);
+ }
+ } else {
+ // If there is multiple bidi levels, split into individual bidi level and apply style.
+ byte level = mLevels.get(start);
+ // Note that the empty text or empty range won't reach this method.
+ // Safe to search from start + 1.
+ for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
+ if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point
+ final boolean isRtl = (level & 0x1) != 0;
+ if (nativeBuilderPtr == 0) {
+ final int levelLength = levelEnd - levelStart;
+ mWholeWidth += mCachedPaint.getTextRunAdvances(
+ mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
+ isRtl, mWidths.getRawArray(), levelStart);
+ } else {
+ nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
+ levelEnd, isRtl);
+ }
+ if (levelEnd == end) {
+ break;
+ }
+ levelStart = levelEnd;
+ level = mLevels.get(levelEnd);
+ }
+ }
+ }
+ }
+
+ private void applyMetricsAffectingSpan(
+ @NonNull TextPaint paint,
+ @Nullable MetricAffectingSpan[] spans,
+ @IntRange(from = 0) int start, // inclusive, in original text buffer
+ @IntRange(from = 0) int end, // exclusive, in original text buffer
+ /* Maybe Zero */ long nativeBuilderPtr) {
+ mCachedPaint.set(paint);
+ // XXX paint should not have a baseline shift, but...
+ mCachedPaint.baselineShift = 0;
+
+ final boolean needFontMetrics = nativeBuilderPtr != 0;
+
+ if (needFontMetrics && mCachedFm == null) {
+ mCachedFm = new Paint.FontMetricsInt();
+ }
+
+ ReplacementSpan replacement = null;
+ if (spans != null) {
+ for (int i = 0; i < spans.length; i++) {
+ MetricAffectingSpan span = spans[i];
+ if (span instanceof ReplacementSpan) {
+ // The last ReplacementSpan is effective for backward compatibility reasons.
+ replacement = (ReplacementSpan) span;
+ } else {
+ // TODO: No need to call updateMeasureState for ReplacementSpan as well?
+ span.updateMeasureState(mCachedPaint);
+ }
+ }
+ }
+
+ final int startInCopiedBuffer = start - mTextStart;
+ final int endInCopiedBuffer = end - mTextStart;
+
+ if (replacement != null) {
+ applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
+ nativeBuilderPtr);
+ } else {
+ applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+ }
+
+ if (needFontMetrics) {
+ if (mCachedPaint.baselineShift < 0) {
+ mCachedFm.ascent += mCachedPaint.baselineShift;
+ mCachedFm.top += mCachedPaint.baselineShift;
+ } else {
+ mCachedFm.descent += mCachedPaint.baselineShift;
+ mCachedFm.bottom += mCachedPaint.baselineShift;
+ }
+
+ mFontMetrics.append(mCachedFm.top);
+ mFontMetrics.append(mCachedFm.bottom);
+ mFontMetrics.append(mCachedFm.ascent);
+ mFontMetrics.append(mCachedFm.descent);
+ }
+ }
+
+ /**
+ * Returns the maximum index that the accumulated width not exceeds the width.
+ *
+ * If forward=false is passed, returns the minimum index from the end instead.
+ *
+ * This only works if the MeasuredParagraph is computed with computeForMeasurement.
+ * Undefined behavior in other case.
+ */
+ @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
+ float[] w = mWidths.getRawArray();
+ if (forwards) {
+ int i = 0;
+ while (i < limit) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i++;
+ }
+ while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
+ return i;
+ } else {
+ int i = limit - 1;
+ while (i >= 0) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i--;
+ }
+ while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
+ i++;
+ }
+ return limit - i - 1;
+ }
+ }
+
+ /**
+ * Returns the length of the substring.
+ *
+ * This only works if the MeasuredParagraph is computed with computeForMeasurement.
+ * Undefined behavior in other case.
+ */
+ @FloatRange(from = 0.0f) float measure(int start, int limit) {
+ float width = 0;
+ float[] w = mWidths.getRawArray();
+ for (int i = start; i < limit; ++i) {
+ width += w[i];
+ }
+ return width;
+ }
+
+ private static native /* Non Zero */ long nInitBuilder();
+
+ /**
+ * Apply style to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param isRtl True if the text is RTL.
+ */
+ private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ boolean isRtl);
+
+ /**
+ * Apply ReplacementRun to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param width The width of the replacement.
+ */
+ private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @FloatRange(from = 0) float width);
+
+ private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
+ @NonNull char[] text);
+
+ private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+
+ @CriticalNative
+ private static native /* Non Zero */ long nGetReleaseFunc();
+}
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 14d6f9e8a9ef..2c30360b81bc 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,661 +16,255 @@
package android.text;
-import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Paint;
-import android.text.AutoGrowArray.ByteArray;
-import android.text.AutoGrowArray.FloatArray;
-import android.text.AutoGrowArray.IntArray;
-import android.text.Layout.Directions;
-import android.text.style.MetricAffectingSpan;
-import android.text.style.ReplacementSpan;
-import android.util.Pools.SynchronizedPool;
+import android.util.IntArray;
-import dalvik.annotation.optimization.CriticalNative;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
-import libcore.util.NativeAllocationRegistry;
-
-import java.util.Arrays;
+import java.util.ArrayList;
/**
- * MeasuredText provides text information for rendering purpose.
- *
- * The first motivation of this class is identify the text directions and retrieving individual
- * character widths. However retrieving character widths is slower than identifying text directions.
- * Thus, this class provides several builder methods for specific purposes.
- *
- * - buildForBidi:
- * Compute only text directions.
- * - buildForMeasurement:
- * Compute text direction and all character widths.
- * - buildForStaticLayout:
- * This is bit special. StaticLayout also needs to know text direction and character widths for
- * line breaking, but all things are done in native code. Similarly, text measurement is done
- * in native code. So instead of storing result to Java array, this keeps the result in native
- * code since there is no good reason to move the results to Java layer.
- *
- * In addition to the character widths, some additional information is computed for each purposes,
- * e.g. whole text length for measurement or font metrics for static layout.
- *
- * MeasuredText is NOT a thread safe object.
- * @hide
+ * A text which has already been measured.
*/
-public class MeasuredText {
- private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
-
- private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
- private MeasuredText() {} // Use build static functions instead.
-
- private static final SynchronizedPool<MeasuredText> sPool = new SynchronizedPool<>(1);
-
- private static @NonNull MeasuredText obtain() { // Use build static functions instead.
- final MeasuredText mt = sPool.acquire();
- return mt != null ? mt : new MeasuredText();
- }
-
- /**
- * Recycle the MeasuredText.
- *
- * Do not call any methods after you call this method.
- */
- public void recycle() {
- release();
- sPool.release(this);
- }
+public class MeasuredText implements Spanned {
+ private static final char LINE_FEED = '\n';
- // The casted original text.
- //
- // This may be null if the passed text is not a Spanned.
- private @Nullable Spanned mSpanned;
-
- // The start offset of the target range in the original text (mSpanned);
- private @IntRange(from = 0) int mTextStart;
+ // The original text.
+ private final @NonNull CharSequence mText;
- // The length of the target range in the original text.
- private @IntRange(from = 0) int mTextLength;
-
- // The copied character buffer for measuring text.
- //
- // The length of this array is mTextLength.
- private @Nullable char[] mCopiedBuffer;
+ // The inclusive start offset of the measuring target.
+ private final @IntRange(from = 0) int mStart;
- // The whole paragraph direction.
- private @Layout.Direction int mParaDir;
+ // The exclusive end offset of the measuring target.
+ private final @IntRange(from = 0) int mEnd;
- // True if the text is LTR direction and doesn't contain any bidi characters.
- private boolean mLtrWithoutBidi;
+ // The TextPaint used for measurement.
+ private final @NonNull TextPaint mPaint;
- // The bidi level for individual characters.
- //
- // This is empty if mLtrWithoutBidi is true.
- private @NonNull ByteArray mLevels = new ByteArray();
-
- // The whole width of the text.
- // See getWholeWidth comments.
- private @FloatRange(from = 0.0f) float mWholeWidth;
-
- // Individual characters' widths.
- // See getWidths comments.
- private @Nullable FloatArray mWidths = new FloatArray();
-
- // The span end positions.
- // See getSpanEndCache comments.
- private @Nullable IntArray mSpanEndCache = new IntArray(4);
-
- // The font metrics.
- // See getFontMetrics comments.
- private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
-
- // The native MeasuredText.
- // See getNativePtr comments.
- // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
- private /* Maybe Zero */ long mNativePtr = 0;
- private @Nullable Runnable mNativeObjectCleaner;
-
- // Associate the native object to this Java object.
- private void bindNativeObject(/* Non Zero*/ long nativePtr) {
- mNativePtr = nativePtr;
- mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
- }
+ // The requested text direction.
+ private final @NonNull TextDirectionHeuristic mTextDir;
- // Decouple the native object from this Java object and release the native object.
- private void unbindNativeObject() {
- if (mNativePtr != 0) {
- mNativeObjectCleaner.run();
- mNativePtr = 0;
- }
- }
+ // The measured paragraph texts.
+ private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
- // Following two objects are for avoiding object allocation.
- private @NonNull TextPaint mCachedPaint = new TextPaint();
- private @Nullable Paint.FontMetricsInt mCachedFm;
-
- /**
- * Releases internal buffers.
- */
- public void release() {
- reset();
- mLevels.clearWithReleasingLargeArray();
- mWidths.clearWithReleasingLargeArray();
- mFontMetrics.clearWithReleasingLargeArray();
- mSpanEndCache.clearWithReleasingLargeArray();
- }
-
- /**
- * Resets the internal state for starting new text.
- */
- private void reset() {
- mSpanned = null;
- mCopiedBuffer = null;
- mWholeWidth = 0;
- mLevels.clear();
- mWidths.clear();
- mFontMetrics.clear();
- mSpanEndCache.clear();
- unbindNativeObject();
- }
+ // The sorted paragraph end offsets.
+ private final @NonNull int[] mParagraphBreakPoints;
/**
- * Returns the characters to be measured.
+ * Build MeasuredText from the text.
*
- * This is always available.
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ * @param textDir The text direction.
+ * @return The measured text.
*/
- public @NonNull char[] getChars() {
- return mCopiedBuffer;
+ public static @NonNull MeasuredText build(@NonNull CharSequence text,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir) {
+ return MeasuredText.build(text, paint, textDir, 0, text.length());
}
/**
- * Returns the paragraph direction.
+ * Build MeasuredText from the specific range of the text..
*
- * This is always available.
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ * @param textDir The text direction.
+ * @param start The inclusive start offset of the text.
+ * @param end The exclusive start offset of the text.
+ * @return The measured text.
*/
- public @Layout.Direction int getParagraphDir() {
- return mParaDir;
- }
+ public static @NonNull MeasuredText build(@NonNull CharSequence text,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+ Preconditions.checkNotNull(textDir);
+ Preconditions.checkArgumentInRange(start, 0, text.length(), "start");
+ Preconditions.checkArgumentInRange(end, 0, text.length(), "end");
+
+ final IntArray paragraphEnds = new IntArray();
+ final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+
+ int paraEnd = 0;
+ for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
+ paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
+ if (paraEnd < 0) {
+ // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end.
+ paraEnd = end;
+ } else {
+ paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
+ }
- /**
- * Returns the directions.
- *
- * This is always available.
- */
- public Directions getDirections(@IntRange(from = 0) int start, // inclusive
- @IntRange(from = 0) int end) { // exclusive
- if (mLtrWithoutBidi) {
- return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ paragraphEnds.add(paraEnd);
+ measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
+ paint, text, paraStart, paraEnd, textDir, null /* no recycle */));
}
- final int length = end - start;
- return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
- length);
+ return new MeasuredText(text, start, end, paint, textDir,
+ measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
+ paragraphEnds.toArray());
}
- /**
- * Returns the whole text width.
- *
- * This is available only if the MeasureText is computed with computeForMeasurement.
- * Returns 0 in other cases.
- */
- public @FloatRange(from = 0.0f) float getWholeWidth() {
- return mWholeWidth;
+ // Use MeasuredText.build instead.
+ private MeasuredText(@NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir,
+ @NonNull MeasuredParagraph[] measuredTexts,
+ @NonNull int[] paragraphBreakPoints) {
+ mText = text;
+ mStart = start;
+ mEnd = end;
+ mPaint = paint;
+ mMeasuredParagraphs = measuredTexts;
+ mParagraphBreakPoints = paragraphBreakPoints;
+ mTextDir = textDir;
}
/**
- * Returns the individual character's width.
- *
- * This is available only if the MeasureText is computed with computeForMeasurement.
- * Returns empty array in other cases.
+ * Return the underlying text.
*/
- public @NonNull FloatArray getWidths() {
- return mWidths;
+ public @NonNull CharSequence getText() {
+ return mText;
}
/**
- * Returns the MetricsAffectingSpan end indices.
- *
- * If the input text is not a spanned string, this has one value that is the length of the text.
- *
- * This is available only if the MeasureText is computed with computeForStaticLayout.
- * Returns empty array in other cases.
+ * Returns the inclusive start offset of measured region.
*/
- public @NonNull IntArray getSpanEndCache() {
- return mSpanEndCache;
+ public @IntRange(from = 0) int getStart() {
+ return mStart;
}
/**
- * Returns the int array which holds FontMetrics.
- *
- * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
- *
- * This is available only if the MeasureText is computed with computeForStaticLayout.
- * Returns empty array in other cases.
+ * Returns the exclusive end offset of measured region.
*/
- public @NonNull IntArray getFontMetrics() {
- return mFontMetrics;
+ public @IntRange(from = 0) int getEnd() {
+ return mEnd;
}
/**
- * Returns the native ptr of the MeasuredText.
- *
- * This is available only if the MeasureText is computed with computeForStaticLayout.
- * Returns 0 in other cases.
+ * Returns the text direction associated with char sequence.
*/
- public /* Maybe Zero */ long getNativePtr() {
- return mNativePtr;
+ public @NonNull TextDirectionHeuristic getTextDir() {
+ return mTextDir;
}
/**
- * Generates new MeasuredText for Bidi computation.
- *
- * If recycle is null, this returns new instance. If recycle is not null, this fills computed
- * result to recycle and returns recycle.
- *
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
- * @param recycle pass existing MeasuredText if you want to recycle it.
- *
- * @return measured text
+ * Returns the paint used to measure this text.
*/
- public static @NonNull MeasuredText buildForBidi(@NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir,
- @Nullable MeasuredText recycle) {
- final MeasuredText mt = recycle == null ? obtain() : recycle;
- mt.resetAndAnalyzeBidi(text, start, end, textDir);
- return mt;
+ public @NonNull TextPaint getPaint() {
+ return mPaint;
}
/**
- * Generates new MeasuredText for measuring texts.
- *
- * If recycle is null, this returns new instance. If recycle is not null, this fills computed
- * result to recycle and returns recycle.
- *
- * @param paint the paint to be used for rendering the text.
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
- * @param recycle pass existing MeasuredText if you want to recycle it.
- *
- * @return measured text
+ * Returns the length of the paragraph of this text.
*/
- public static @NonNull MeasuredText buildForMeasurement(@NonNull TextPaint paint,
- @NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir,
- @Nullable MeasuredText recycle) {
- final MeasuredText mt = recycle == null ? obtain() : recycle;
- mt.resetAndAnalyzeBidi(text, start, end, textDir);
-
- mt.mWidths.resize(mt.mTextLength);
- if (mt.mTextLength == 0) {
- return mt;
- }
-
- if (mt.mSpanned == null) {
- // No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(
- paint, null /* spans */, start, end, 0 /* native static layout ptr */);
- } else {
- // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
- int spanEnd;
- for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
- spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
- MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
- MetricAffectingSpan.class);
- spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
- mt.applyMetricsAffectingSpan(
- paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
- }
- }
- return mt;
+ public @IntRange(from = 0) int getParagraphCount() {
+ return mParagraphBreakPoints.length;
}
/**
- * Generates new MeasuredText for StaticLayout.
- *
- * If recycle is null, this returns new instance. If recycle is not null, this fills computed
- * result to recycle and returns recycle.
- *
- * @param paint the paint to be used for rendering the text.
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
- * @param recycle pass existing MeasuredText if you want to recycle it.
- *
- * @return measured text
+ * Returns the paragraph start offset of the text.
*/
- public static @NonNull MeasuredText buildForStaticLayout(
- @NonNull TextPaint paint,
- @NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir,
- @Nullable MeasuredText recycle) {
- final MeasuredText mt = recycle == null ? obtain() : recycle;
- mt.resetAndAnalyzeBidi(text, start, end, textDir);
- if (mt.mTextLength == 0) {
- // Need to build empty native measured text for StaticLayout.
- // TODO: Stop creating empty measured text for empty lines.
- long nativeBuilderPtr = nInitBuilder();
- try {
- mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
- }
- return mt;
- }
-
- long nativeBuilderPtr = nInitBuilder();
- try {
- if (mt.mSpanned == null) {
- // No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
- mt.mSpanEndCache.append(end);
- } else {
- // There may be a MetricsAffectingSpan. Split into span transitions and apply
- // styles.
- int spanEnd;
- for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
- spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
- MetricAffectingSpan.class);
- MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
- MetricAffectingSpan.class);
- spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
- MetricAffectingSpan.class);
- mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
- nativeBuilderPtr);
- mt.mSpanEndCache.append(spanEnd);
- }
- }
- mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
- }
-
- return mt;
+ public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
}
/**
- * Reset internal state and analyzes text for bidirectional runs.
- *
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
+ * Returns the paragraph end offset of the text.
*/
- private void resetAndAnalyzeBidi(@NonNull CharSequence text,
- @IntRange(from = 0) int start, // inclusive
- @IntRange(from = 0) int end, // exclusive
- @NonNull TextDirectionHeuristic textDir) {
- reset();
- mSpanned = text instanceof Spanned ? (Spanned) text : null;
- mTextStart = start;
- mTextLength = end - start;
-
- if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
- mCopiedBuffer = new char[mTextLength];
- }
- TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
-
- // Replace characters associated with ReplacementSpan to U+FFFC.
- if (mSpanned != null) {
- ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
-
- for (int i = 0; i < spans.length; i++) {
- int startInPara = mSpanned.getSpanStart(spans[i]) - start;
- int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
- // The span interval may be larger and must be restricted to [start, end)
- if (startInPara < 0) startInPara = 0;
- if (endInPara > mTextLength) endInPara = mTextLength;
- Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
- }
- }
-
- if ((textDir == TextDirectionHeuristics.LTR ||
- textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR ||
- textDir == TextDirectionHeuristics.ANYRTL_LTR) &&
- TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
- mLevels.clear();
- mParaDir = Layout.DIR_LEFT_TO_RIGHT;
- mLtrWithoutBidi = true;
- } else {
- final int bidiRequest;
- if (textDir == TextDirectionHeuristics.LTR) {
- bidiRequest = Layout.DIR_REQUEST_LTR;
- } else if (textDir == TextDirectionHeuristics.RTL) {
- bidiRequest = Layout.DIR_REQUEST_RTL;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
- bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
- bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
- } else {
- final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
- bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
- }
- mLevels.resize(mTextLength);
- mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
- mLtrWithoutBidi = false;
- }
+ public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return mParagraphBreakPoints[paraIndex];
}
- private void applyReplacementRun(@NonNull ReplacementSpan replacement,
- @IntRange(from = 0) int start, // inclusive, in copied buffer
- @IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
- // Use original text. Shouldn't matter.
- // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
- // backward compatibility? or Should we initialize them for getFontMetricsInt?
- final float width = replacement.getSize(
- mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
- if (nativeBuilderPtr == 0) {
- // Assigns all width to the first character. This is the same behavior as minikin.
- mWidths.set(start, width);
- if (end > start + 1) {
- Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
- }
- mWholeWidth += width;
- } else {
- nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- width);
- }
+ /** @hide */
+ public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
+ return mMeasuredParagraphs[paraIndex];
}
- private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
- @IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
- if (nativeBuilderPtr != 0) {
- mCachedPaint.getFontMetricsInt(mCachedFm);
- }
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spanned overrides
+ //
+ // Just proxy for underlying mText if appropriate.
- if (mLtrWithoutBidi) {
- // If the whole text is LTR direction, just apply whole region.
- if (nativeBuilderPtr == 0) {
- mWholeWidth += mCachedPaint.getTextRunAdvances(
- mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
- mWidths.getRawArray(), start);
- } else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- false /* isRtl */);
- }
+ @Override
+ public <T> T[] getSpans(int start, int end, Class<T> type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpans(start, end, type);
} else {
- // If there is multiple bidi levels, split into individual bidi level and apply style.
- byte level = mLevels.get(start);
- // Note that the empty text or empty range won't reach this method.
- // Safe to search from start + 1.
- for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
- if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point
- final boolean isRtl = (level & 0x1) != 0;
- if (nativeBuilderPtr == 0) {
- final int levelLength = levelEnd - levelStart;
- mWholeWidth += mCachedPaint.getTextRunAdvances(
- mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
- isRtl, mWidths.getRawArray(), levelStart);
- } else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
- levelEnd, isRtl);
- }
- if (levelEnd == end) {
- break;
- }
- levelStart = levelEnd;
- level = mLevels.get(levelEnd);
- }
- }
+ return ArrayUtils.emptyArray(type);
}
}
- private void applyMetricsAffectingSpan(
- @NonNull TextPaint paint,
- @Nullable MetricAffectingSpan[] spans,
- @IntRange(from = 0) int start, // inclusive, in original text buffer
- @IntRange(from = 0) int end, // exclusive, in original text buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
- mCachedPaint.set(paint);
- // XXX paint should not have a baseline shift, but...
- mCachedPaint.baselineShift = 0;
-
- final boolean needFontMetrics = nativeBuilderPtr != 0;
-
- if (needFontMetrics && mCachedFm == null) {
- mCachedFm = new Paint.FontMetricsInt();
- }
-
- ReplacementSpan replacement = null;
- if (spans != null) {
- for (int i = 0; i < spans.length; i++) {
- MetricAffectingSpan span = spans[i];
- if (span instanceof ReplacementSpan) {
- // The last ReplacementSpan is effective for backward compatibility reasons.
- replacement = (ReplacementSpan) span;
- } else {
- // TODO: No need to call updateMeasureState for ReplacementSpan as well?
- span.updateMeasureState(mCachedPaint);
- }
- }
- }
-
- final int startInCopiedBuffer = start - mTextStart;
- final int endInCopiedBuffer = end - mTextStart;
-
- if (replacement != null) {
- applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
- nativeBuilderPtr);
+ @Override
+ public int getSpanStart(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanStart(tag);
} else {
- applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+ return -1;
}
+ }
- if (needFontMetrics) {
- if (mCachedPaint.baselineShift < 0) {
- mCachedFm.ascent += mCachedPaint.baselineShift;
- mCachedFm.top += mCachedPaint.baselineShift;
- } else {
- mCachedFm.descent += mCachedPaint.baselineShift;
- mCachedFm.bottom += mCachedPaint.baselineShift;
- }
-
- mFontMetrics.append(mCachedFm.top);
- mFontMetrics.append(mCachedFm.bottom);
- mFontMetrics.append(mCachedFm.ascent);
- mFontMetrics.append(mCachedFm.descent);
+ @Override
+ public int getSpanEnd(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanEnd(tag);
+ } else {
+ return -1;
}
}
- /**
- * Returns the maximum index that the accumulated width not exceeds the width.
- *
- * If forward=false is passed, returns the minimum index from the end instead.
- *
- * This only works if the MeasuredText is computed with computeForMeasurement.
- * Undefined behavior in other case.
- */
- @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
- float[] w = mWidths.getRawArray();
- if (forwards) {
- int i = 0;
- while (i < limit) {
- width -= w[i];
- if (width < 0.0f) break;
- i++;
- }
- while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
- return i;
+ @Override
+ public int getSpanFlags(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanFlags(tag);
} else {
- int i = limit - 1;
- while (i >= 0) {
- width -= w[i];
- if (width < 0.0f) break;
- i--;
- }
- while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
- i++;
- }
- return limit - i - 1;
+ return 0;
}
}
- /**
- * Returns the length of the substring.
- *
- * This only works if the MeasuredText is computed with computeForMeasurement.
- * Undefined behavior in other case.
- */
- @FloatRange(from = 0.0f) float measure(int start, int limit) {
- float width = 0;
- float[] w = mWidths.getRawArray();
- for (int i = start; i < limit; ++i) {
- width += w[i];
+ @Override
+ public int nextSpanTransition(int start, int limit, Class type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).nextSpanTransition(start, limit, type);
+ } else {
+ return mText.length();
}
- return width;
}
- private static native /* Non Zero */ long nInitBuilder();
-
- /**
- * Apply style to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredText builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param isRtl True if the text is RTL.
- */
- private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- boolean isRtl);
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // CharSequence overrides.
+ //
+ // Just proxy for underlying mText.
- /**
- * Apply ReplacementRun to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredText builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param width The width of the replacement.
- */
- private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @FloatRange(from = 0) float width);
+ @Override
+ public int length() {
+ return mText.length();
+ }
- private static native long nBuildNativeMeasuredText(/* Non Zero */ long nativeBuilderPtr,
- @NonNull char[] text);
+ @Override
+ public char charAt(int index) {
+ // TODO: Should this be index + mStart ?
+ return mText.charAt(index);
+ }
- private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ // TODO: return MeasuredText.
+ // TODO: Should this be index + mStart, end + mStart ?
+ return mText.subSequence(start, end);
+ }
- @CriticalNative
- private static native /* Non Zero */ long nGetReleaseFunc();
+ @Override
+ public String toString() {
+ return mText.toString();
+ }
}
diff --git a/core/java/android/text/PremeasuredText.java b/core/java/android/text/PremeasuredText.java
deleted file mode 100644
index 465314dd21ac..000000000000
--- a/core/java/android/text/PremeasuredText.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.util.IntArray;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-
-/**
- * A text which has already been measured.
- *
- * TODO: Rename to better name? e.g. MeasuredText, FrozenText etc.
- */
-public class PremeasuredText implements Spanned {
- private static final char LINE_FEED = '\n';
-
- // The original text.
- private final @NonNull CharSequence mText;
-
- // The inclusive start offset of the measuring target.
- private final @IntRange(from = 0) int mStart;
-
- // The exclusive end offset of the measuring target.
- private final @IntRange(from = 0) int mEnd;
-
- // The TextPaint used for measurement.
- private final @NonNull TextPaint mPaint;
-
- // The requested text direction.
- private final @NonNull TextDirectionHeuristic mTextDir;
-
- // The measured paragraph texts.
- private final @NonNull MeasuredText[] mMeasuredTexts;
-
- // The sorted paragraph end offsets.
- private final @NonNull int[] mParagraphBreakPoints;
-
- /**
- * Build PremeasuredText from the text.
- *
- * @param text The text to be measured.
- * @param paint The paint to be used for drawing.
- * @param textDir The text direction.
- * @return The measured text.
- */
- public static @NonNull PremeasuredText build(@NonNull CharSequence text,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir) {
- return PremeasuredText.build(text, paint, textDir, 0, text.length());
- }
-
- /**
- * Build PremeasuredText from the specific range of the text..
- *
- * @param text The text to be measured.
- * @param paint The paint to be used for drawing.
- * @param textDir The text direction.
- * @param start The inclusive start offset of the text.
- * @param end The exclusive start offset of the text.
- * @return The measured text.
- */
- public static @NonNull PremeasuredText build(@NonNull CharSequence text,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end) {
- Preconditions.checkNotNull(text);
- Preconditions.checkNotNull(paint);
- Preconditions.checkNotNull(textDir);
- Preconditions.checkArgumentInRange(start, 0, text.length(), "start");
- Preconditions.checkArgumentInRange(end, 0, text.length(), "end");
-
- final IntArray paragraphEnds = new IntArray();
- final ArrayList<MeasuredText> measuredTexts = new ArrayList<>();
-
- int paraEnd = 0;
- for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
- paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
- if (paraEnd < 0) {
- // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end.
- paraEnd = end;
- } else {
- paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
- }
-
- paragraphEnds.add(paraEnd);
- measuredTexts.add(MeasuredText.buildForStaticLayout(
- paint, text, paraStart, paraEnd, textDir, null /* no recycle */));
- }
-
- return new PremeasuredText(text, start, end, paint, textDir,
- measuredTexts.toArray(new MeasuredText[measuredTexts.size()]),
- paragraphEnds.toArray());
- }
-
- // Use PremeasuredText.build instead.
- private PremeasuredText(@NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir,
- @NonNull MeasuredText[] measuredTexts,
- @NonNull int[] paragraphBreakPoints) {
- mText = text;
- mStart = start;
- mEnd = end;
- mPaint = paint;
- mMeasuredTexts = measuredTexts;
- mParagraphBreakPoints = paragraphBreakPoints;
- mTextDir = textDir;
- }
-
- /**
- * Return the underlying text.
- */
- public @NonNull CharSequence getText() {
- return mText;
- }
-
- /**
- * Returns the inclusive start offset of measured region.
- */
- public @IntRange(from = 0) int getStart() {
- return mStart;
- }
-
- /**
- * Returns the exclusive end offset of measured region.
- */
- public @IntRange(from = 0) int getEnd() {
- return mEnd;
- }
-
- /**
- * Returns the text direction associated with char sequence.
- */
- public @NonNull TextDirectionHeuristic getTextDir() {
- return mTextDir;
- }
-
- /**
- * Returns the paint used to measure this text.
- */
- public @NonNull TextPaint getPaint() {
- return mPaint;
- }
-
- /**
- * Returns the length of the paragraph of this text.
- */
- public @IntRange(from = 0) int getParagraphCount() {
- return mParagraphBreakPoints.length;
- }
-
- /**
- * Returns the paragraph start offset of the text.
- */
- public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
- }
-
- /**
- * Returns the paragraph end offset of the text.
- */
- public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return mParagraphBreakPoints[paraIndex];
- }
-
- /** @hide */
- public @NonNull MeasuredText getMeasuredText(@IntRange(from = 0) int paraIndex) {
- return mMeasuredTexts[paraIndex];
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Spanned overrides
- //
- // Just proxy for underlying mText if appropriate.
-
- @Override
- public <T> T[] getSpans(int start, int end, Class<T> type) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpans(start, end, type);
- } else {
- return ArrayUtils.emptyArray(type);
- }
- }
-
- @Override
- public int getSpanStart(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanStart(tag);
- } else {
- return -1;
- }
- }
-
- @Override
- public int getSpanEnd(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanEnd(tag);
- } else {
- return -1;
- }
- }
-
- @Override
- public int getSpanFlags(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanFlags(tag);
- } else {
- return 0;
- }
- }
-
- @Override
- public int nextSpanTransition(int start, int limit, Class type) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).nextSpanTransition(start, limit, type);
- } else {
- return mText.length();
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // CharSequence overrides.
- //
- // Just proxy for underlying mText.
-
- @Override
- public int length() {
- return mText.length();
- }
-
- @Override
- public char charAt(int index) {
- // TODO: Should this be index + mStart ?
- return mText.charAt(index);
- }
-
- @Override
- public CharSequence subSequence(int start, int end) {
- // TODO: return PremeasuredText.
- // TODO: Should this be index + mStart, end + mStart ?
- return mText.subSequence(start, end);
- }
-
- @Override
- public String toString() {
- return mText.toString();
- }
-}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index d69b1190140f..36bec863e7ac 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -55,7 +55,8 @@ public class StaticLayout extends Layout {
* First, call nInit to setup native line breaker object. Then, for each paragraph, do the
* following:
*
- * - Create MeasuredText by MeasuredText.buildForStaticLayout which measures in native.
+ * - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
+ * native.
* - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
@@ -650,34 +651,34 @@ public class StaticLayout extends Layout {
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
indents, mLeftPaddings, mRightPaddings);
- PremeasuredText premeasured = null;
+ MeasuredText measured = null;
final Spanned spanned;
- if (source instanceof PremeasuredText) {
- premeasured = (PremeasuredText) source;
+ if (source instanceof MeasuredText) {
+ measured = (MeasuredText) source;
- final CharSequence original = premeasured.getText();
+ final CharSequence original = measured.getText();
spanned = (original instanceof Spanned) ? (Spanned) original : null;
- if (bufStart != premeasured.getStart() || bufEnd != premeasured.getEnd()) {
+ if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
// The buffer position has changed. Re-measure here.
- premeasured = PremeasuredText.build(original, paint, textDir, bufStart, bufEnd);
+ measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd);
} else {
- // We can use premeasured information.
+ // We can use measured information.
- // Overwrite with the one when premeasured.
+ // Overwrite with the one when emeasured.
// TODO: Give an option for developer not to overwrite and measure again here?
- textDir = premeasured.getTextDir();
- paint = premeasured.getPaint();
+ textDir = measured.getTextDir();
+ paint = measured.getPaint();
}
} else {
- premeasured = PremeasuredText.build(source, paint, textDir, bufStart, bufEnd);
+ measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd);
spanned = (source instanceof Spanned) ? (Spanned) source : null;
}
try {
- for (int paraIndex = 0; paraIndex < premeasured.getParagraphCount(); paraIndex++) {
- final int paraStart = premeasured.getParagraphStart(paraIndex);
- final int paraEnd = premeasured.getParagraphEnd(paraIndex);
+ for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
+ final int paraStart = measured.getParagraphStart(paraIndex);
+ final int paraEnd = measured.getParagraphEnd(paraIndex);
int firstWidthLineCount = 1;
int firstWidth = outerWidth;
@@ -743,10 +744,10 @@ public class StaticLayout extends Layout {
}
}
- final MeasuredText measured = premeasured.getMeasuredText(paraIndex);
- final char[] chs = measured.getChars();
- final int[] spanEndCache = measured.getSpanEndCache().getRawArray();
- final int[] fmCache = measured.getFontMetrics().getRawArray();
+ final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex);
+ final char[] chs = measuredPara.getChars();
+ final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
+ final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
// TODO: Stop keeping duplicated width copy in native and Java.
widths.resize(chs.length);
@@ -759,7 +760,7 @@ public class StaticLayout extends Layout {
// Inputs
chs,
- measured.getNativePtr(),
+ measuredPara.getNativePtr(),
paraEnd - paraStart,
firstWidth,
firstWidthLineCount,
@@ -863,7 +864,7 @@ public class StaticLayout extends Layout {
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
- flags[breakIndex], needMultiply, measured, bufEnd,
+ flags[breakIndex], needMultiply, measuredPara, bufEnd,
includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
paint, moreChars);
@@ -894,8 +895,8 @@ public class StaticLayout extends Layout {
if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
&& mLineCount < mMaximumVisibleLineCount) {
- final MeasuredText measured =
- MeasuredText.buildForBidi(source, bufEnd, bufEnd, textDir, null);
+ final MeasuredParagraph measuredPara =
+ MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
paint.getFontMetricsInt(fm);
v = out(source,
bufEnd, bufEnd, fm.ascent, fm.descent,
@@ -903,7 +904,7 @@ public class StaticLayout extends Layout {
v,
spacingmult, spacingadd, null,
null, fm, 0,
- needMultiply, measured, bufEnd,
+ needMultiply, measuredPara, bufEnd,
includepad, trackpad, addLastLineSpacing, null,
null, bufStart, ellipsize,
ellipsizedWidth, 0, paint, false);
@@ -918,7 +919,7 @@ public class StaticLayout extends Layout {
private int out(final CharSequence text, final int start, final int end, int above, int below,
int top, int bottom, int v, final float spacingmult, final float spacingadd,
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
- final int flags, final boolean needMultiply, @NonNull final MeasuredText measured,
+ final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured,
final int bufEnd, final boolean includePad, final boolean trackPad,
final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 9c9fbf23832f..409e51438d20 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1250,10 +1250,10 @@ public class TextUtils {
@NonNull String ellipsis) {
final int len = text.length();
- MeasuredText mt = null;
- MeasuredText resultMt = null;
+ MeasuredParagraph mt = null;
+ MeasuredParagraph resultMt = null;
try {
- mt = MeasuredText.buildForMeasurement(paint, text, 0, text.length(), textDir, mt);
+ mt = MeasuredParagraph.buildForMeasurement(paint, text, 0, text.length(), textDir, mt);
float width = mt.getWholeWidth();
if (width <= avail) {
@@ -1332,7 +1332,7 @@ public class TextUtils {
if (remaining == 0) { // All text is gone.
textFits = true;
} else {
- resultMt = MeasuredText.buildForMeasurement(
+ resultMt = MeasuredParagraph.buildForMeasurement(
paint, result, 0, result.length(), textDir, resultMt);
width = resultMt.getWholeWidth();
if (width <= avail) {
@@ -1479,11 +1479,11 @@ public class TextUtils {
public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
float avail, String oneMore, String more, TextDirectionHeuristic textDir) {
- MeasuredText mt = null;
- MeasuredText tempMt = null;
+ MeasuredParagraph mt = null;
+ MeasuredParagraph tempMt = null;
try {
int len = text.length();
- mt = MeasuredText.buildForMeasurement(p, text, 0, len, textDir, mt);
+ mt = MeasuredParagraph.buildForMeasurement(p, text, 0, len, textDir, mt);
final float width = mt.getWholeWidth();
if (width <= avail) {
return text;
@@ -1523,7 +1523,7 @@ public class TextUtils {
}
// XXX this is probably ok, but need to look at it more
- tempMt = MeasuredText.buildForMeasurement(
+ tempMt = MeasuredParagraph.buildForMeasurement(
p, format, 0, format.length(), textDir, tempMt);
float moreWid = tempMt.getWholeWidth();
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 62f971724673..e94f91a12905 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -38,13 +38,13 @@ public class FeatureFlagUtils {
static {
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("device_info_v2", "true");
- DEFAULT_FLAGS.put("settings_search_v2", "true");
DEFAULT_FLAGS.put("settings_app_info_v2", "true");
DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "false");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
DEFAULT_FLAGS.put("settings_zone_picker_v2", "false");
+ DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
}
/**
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index c25b272c11b9..e0d085cfaa66 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -42,6 +42,16 @@ public final class StatsManager {
}
/**
+ * Temporary to prevent build failures. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
+ // To prevent breakages of dependencies on old API.
+
+ return false;
+ }
+
+ /**
* Clients can send a configuration and simultaneously registers the name of a broadcast
* receiver that listens for when it should request data.
*
@@ -70,6 +80,15 @@ public final class StatsManager {
}
/**
+ * Temporary to prevent build failures. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean removeConfiguration(String configKey) {
+ // To prevent breakages of old dependencies.
+ return false;
+ }
+
+ /**
* Remove a configuration from logging.
*
* @param configKey Configuration key to remove.
@@ -93,6 +112,16 @@ public final class StatsManager {
}
/**
+ * Temporary to prevent build failures. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public byte[] getData(String configKey) {
+ // TODO: remove this and all other methods with String-based config keys.
+ // To prevent build breakages of dependencies.
+ return null;
+ }
+
+ /**
* Clients can request data with a binder call. This getter is destructive and also clears
* the retrieved metrics from statsd memory.
*
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index cc4a0b60dd0a..84ae20b92f3c 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -18,30 +18,18 @@ package android.util;
import android.os.SystemClock;
+import libcore.util.TimeZoneFinder;
+import libcore.util.ZoneInfoDB;
+
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
import java.util.Calendar;
-import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
-import java.util.List;
-import libcore.util.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
-
/**
* A class containing utility methods related to time zones.
*/
public class TimeUtils {
/** @hide */ public TimeUtils() {}
- private static final boolean DBG = false;
- private static final String TAG = "TimeUtils";
-
- /** Cached results of getTimeZonesWithUniqueOffsets */
- private static final Object sLastUniqueLockObj = new Object();
- private static List<String> sLastUniqueZoneOffsets = null;
- private static String sLastUniqueCountry = null;
-
/** {@hide} */
private static SimpleDateFormat sLoggingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -76,86 +64,6 @@ public class TimeUtils {
}
/**
- * Returns an immutable list of unique time zone IDs for the country.
- *
- * @param country to find
- * @return unmodifiable list of unique time zones, maybe empty but never null.
- * @hide
- */
- public static List<String> getTimeZoneIdsWithUniqueOffsets(String country) {
- synchronized(sLastUniqueLockObj) {
- if ((country != null) && country.equals(sLastUniqueCountry)) {
- if (DBG) {
- Log.d(TAG, "getTimeZonesWithUniqueOffsets(" +
- country + "): return cached version");
- }
- return sLastUniqueZoneOffsets;
- }
- }
-
- Collection<android.icu.util.TimeZone> zones = getIcuTimeZones(country);
- ArrayList<android.icu.util.TimeZone> uniqueTimeZones = new ArrayList<>();
- for (android.icu.util.TimeZone zone : zones) {
- // See if we already have this offset,
- // Using slow but space efficient and these are small.
- boolean found = false;
- for (int i = 0; i < uniqueTimeZones.size(); i++) {
- if (uniqueTimeZones.get(i).getRawOffset() == zone.getRawOffset()) {
- found = true;
- break;
- }
- }
- if (!found) {
- if (DBG) {
- Log.d(TAG, "getTimeZonesWithUniqueOffsets: add unique offset=" +
- zone.getRawOffset() + " zone.getID=" + zone.getID());
- }
- uniqueTimeZones.add(zone);
- }
- }
-
- synchronized(sLastUniqueLockObj) {
- // Cache the last result
- sLastUniqueZoneOffsets = extractZoneIds(uniqueTimeZones);
- sLastUniqueCountry = country;
-
- return sLastUniqueZoneOffsets;
- }
- }
-
- private static List<String> extractZoneIds(List<android.icu.util.TimeZone> timeZones) {
- List<String> ids = new ArrayList<>(timeZones.size());
- for (android.icu.util.TimeZone timeZone : timeZones) {
- ids.add(timeZone.getID());
- }
- return Collections.unmodifiableList(ids);
- }
-
- /**
- * Returns an immutable list of frozen ICU time zones for the country.
- *
- * @param countryIso is a two character country code.
- * @return TimeZone list, maybe empty but never null.
- * @hide
- */
- private static List<android.icu.util.TimeZone> getIcuTimeZones(String countryIso) {
- if (countryIso == null) {
- if (DBG) Log.d(TAG, "getIcuTimeZones(null): return empty list");
- return Collections.emptyList();
- }
- List<android.icu.util.TimeZone> timeZones =
- TimeZoneFinder.getInstance().lookupTimeZonesByCountry(countryIso);
- if (timeZones == null) {
- if (DBG) {
- Log.d(TAG, "getIcuTimeZones(" + countryIso
- + "): returned null, converting to empty list");
- }
- return Collections.emptyList();
- }
- return timeZones;
- }
-
- /**
* Returns a String indicating the version of the time zone database currently
* in use. The format of the string is dependent on the underlying time zone
* database implementation, but will typically contain the year in which the database
diff --git a/core/java/android/view/IRemoteAnimationFinishedCallback.aidl b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl
new file mode 100644
index 000000000000..ae58b226ec03
--- /dev/null
+++ b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+/**
+ * Interface to be invoked by the controlling process when a remote animation has finished.
+ *
+ * @see IRemoteAnimationRunner
+ * {@hide}
+ */
+interface IRemoteAnimationFinishedCallback {
+ void onAnimationFinished();
+}
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
new file mode 100644
index 000000000000..1350ebf10a4f
--- /dev/null
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.RemoteAnimationTarget;
+import android.view.IRemoteAnimationFinishedCallback;
+
+/**
+ * Interface that is used to callback from window manager to the process that runs a remote
+ * animation to start or cancel it.
+ *
+ * {@hide}
+ */
+oneway interface IRemoteAnimationRunner {
+
+ /**
+ * Called when the process needs to start the remote animation.
+ *
+ * @param apps The list of apps to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ void onAnimationStart(in RemoteAnimationTarget[] apps,
+ in IRemoteAnimationFinishedCallback finishedCallback);
+
+ /**
+ * Called when the animation was cancelled. From this point on, any updates onto the leashes
+ * won't have any effect anymore.
+ */
+ void onAnimationCancelled();
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8c7032207c34..4adcb8f15be7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -38,6 +38,7 @@ import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedStackListener;
+import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowSession;
@@ -124,6 +125,7 @@ interface IWindowManager
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
boolean scaleUp);
+ void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
void executeAppTransition();
/** Used by system ui to report that recents has shown itself. */
@@ -294,6 +296,11 @@ interface IWindowManager
boolean hasNavigationBar();
/**
+ * Get the position of the nav bar
+ */
+ int getNavBarPosition();
+
+ /**
* Lock the device immediately with the specified options (can be null).
*/
void lockNow(in Bundle options);
diff --git a/core/java/android/view/RemoteAnimationAdapter.aidl b/core/java/android/view/RemoteAnimationAdapter.aidl
new file mode 100644
index 000000000000..855bc740e75c
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationAdapter.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+parcelable RemoteAnimationAdapter;
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
new file mode 100644
index 000000000000..d597e597b119
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.app.ActivityOptions;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Object that describes how to run a remote animation.
+ * <p>
+ * A remote animation lets another app control the entire app transition. It does so by
+ * <ul>
+ * <li>using {@link ActivityOptions#makeRemoteAnimation}</li>
+ * <li>using {@link IWindowManager#overridePendingAppTransitionRemote}</li>
+ * </ul>
+ * to register a {@link RemoteAnimationAdapter} that describes how the animation should be run:
+ * Along some meta-data, this object contains a callback that gets invoked from window manager when
+ * the transition is ready to be started.
+ * <p>
+ * Window manager supplies a list of {@link RemoteAnimationTarget}s into the callback. Each target
+ * contains information about the activity that is animating as well as
+ * {@link RemoteAnimationTarget#leash}. The controlling app can modify the leash like any other
+ * {@link SurfaceControl}, including the possibility to synchronize updating the leash's surface
+ * properties with a frame to be drawn using
+ * {@link SurfaceControl.Transaction#deferTransactionUntil}.
+ * <p>
+ * When the animation is done, the controlling app can invoke
+ * {@link IRemoteAnimationFinishedCallback} that gets supplied into
+ * {@link IRemoteAnimationRunner#onStartAnimation}
+ *
+ * @hide
+ */
+public class RemoteAnimationAdapter implements Parcelable {
+
+ private final IRemoteAnimationRunner mRunner;
+ private final long mDuration;
+ private final long mStatusBarTransitionDelay;
+
+ /**
+ * @param runner The interface that gets notified when we actually need to start the animation.
+ * @param duration The duration of the animation.
+ * @param statusBarTransitionDelay The desired delay for all visual animations in the
+ * status bar caused by this app animation in millis.
+ */
+ public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+ long statusBarTransitionDelay) {
+ mRunner = runner;
+ mDuration = duration;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ }
+
+ public RemoteAnimationAdapter(Parcel in) {
+ mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
+ mDuration = in.readLong();
+ mStatusBarTransitionDelay = in.readLong();
+ }
+
+ public IRemoteAnimationRunner getRunner() {
+ return mRunner;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ public long getStatusBarTransitionDelay() {
+ return mStatusBarTransitionDelay;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongInterface(mRunner);
+ dest.writeLong(mDuration);
+ dest.writeLong(mStatusBarTransitionDelay);
+ }
+
+ public static final Creator<RemoteAnimationAdapter> CREATOR
+ = new Creator<RemoteAnimationAdapter>() {
+ public RemoteAnimationAdapter createFromParcel(Parcel in) {
+ return new RemoteAnimationAdapter(in);
+ }
+
+ public RemoteAnimationAdapter[] newArray(int size) {
+ return new RemoteAnimationAdapter[size];
+ }
+ };
+}
diff --git a/core/java/android/view/RemoteAnimationDefinition.aidl b/core/java/android/view/RemoteAnimationDefinition.aidl
new file mode 100644
index 000000000000..32ecd01ebf25
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationDefinition.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+parcelable RemoteAnimationDefinition;
diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java
new file mode 100644
index 000000000000..381f6926a1e8
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationDefinition.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.view.WindowManager.TransitionType;
+
+/**
+ * Defines which animation types should be overridden by which remote animation.
+ *
+ * @hide
+ */
+public class RemoteAnimationDefinition implements Parcelable {
+
+ private final SparseArray<RemoteAnimationAdapter> mTransitionAnimationMap;
+
+ public RemoteAnimationDefinition() {
+ mTransitionAnimationMap = new SparseArray<>();
+ }
+
+ /**
+ * Registers a remote animation for a specific transition.
+ *
+ * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @param adapter The adapter that described how to run the remote animation.
+ */
+ public void addRemoteAnimation(@TransitionType int transition, RemoteAnimationAdapter adapter) {
+ mTransitionAnimationMap.put(transition, adapter);
+ }
+
+ /**
+ * Checks whether a remote animation for specific transition is defined.
+ *
+ * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @return Whether this definition has defined a remote animation for the specified transition.
+ */
+ public boolean hasTransition(@TransitionType int transition) {
+ return mTransitionAnimationMap.get(transition) != null;
+ }
+
+ /**
+ * Retrieves the remote animation for a specific transition.
+ *
+ * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @return The remote animation adapter for the specified transition.
+ */
+ public @Nullable RemoteAnimationAdapter getAdapter(@TransitionType int transition) {
+ return mTransitionAnimationMap.get(transition);
+ }
+
+ public RemoteAnimationDefinition(Parcel in) {
+ mTransitionAnimationMap = in.readSparseArray(null /* loader */);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeSparseArray((SparseArray) mTransitionAnimationMap);
+ }
+
+ public static final Creator<RemoteAnimationDefinition> CREATOR =
+ new Creator<RemoteAnimationDefinition>() {
+ public RemoteAnimationDefinition createFromParcel(Parcel in) {
+ return new RemoteAnimationDefinition(in);
+ }
+
+ public RemoteAnimationDefinition[] newArray(int size) {
+ return new RemoteAnimationDefinition[size];
+ }
+ };
+}
diff --git a/core/java/android/view/RemoteAnimationTarget.aidl b/core/java/android/view/RemoteAnimationTarget.aidl
new file mode 100644
index 000000000000..769bf5e16673
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationTarget.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+parcelable RemoteAnimationTarget;
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
new file mode 100644
index 000000000000..f39e618e169d
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes an activity to be animated as part of a remote animation.
+ *
+ * @hide
+ */
+public class RemoteAnimationTarget implements Parcelable {
+
+ /**
+ * The app is in the set of opening apps of this transition.
+ */
+ public static final int MODE_OPENING = 0;
+
+ /**
+ * The app is in the set of closing apps of this transition.
+ */
+ public static final int MODE_CLOSING = 1;
+
+ @IntDef(prefix = { "MODE_" }, value = {
+ MODE_OPENING,
+ MODE_CLOSING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Mode {}
+
+ /**
+ * The {@link Mode} to describe whether this app is opening or closing.
+ */
+ public final @Mode int mode;
+
+ /**
+ * The id of the task this app belongs to.
+ */
+ public final int taskId;
+
+ /**
+ * The {@link SurfaceControl} object to actually control the transform of the app.
+ */
+ public final SurfaceControl leash;
+
+ /**
+ * Whether the app is translucent and may reveal apps behind.
+ */
+ public final boolean isTranslucent;
+
+ /**
+ * The clip rect window manager applies when clipping the app's main surface in screen space
+ * coordinates. This is just a hint to the animation runner: If running a clip-rect animation,
+ * anything that extends beyond these bounds will not have any effect. This implies that any
+ * clip-rect animation should likely stop at these bounds.
+ */
+ public final Rect clipRect;
+
+ /**
+ * The index of the element in the tree in prefix order. This should be used for z-layering
+ * to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
+ * happen.
+ */
+ public final int prefixOrderIndex;
+
+ /**
+ * The source position of the app, in screen spaces coordinates. If the position of the leash
+ * is modified from the controlling app, any animation transform needs to be offset by this
+ * amount.
+ */
+ public final Point position;
+
+ /**
+ * The bounds of the source container the app lives in, in screen space coordinates. If the crop
+ * of the leash is modified from the controlling app, it needs to take the source container
+ * bounds into account when calculating the crop.
+ */
+ public final Rect sourceContainerBounds;
+
+ public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
+ Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) {
+ this.mode = mode;
+ this.taskId = taskId;
+ this.leash = leash;
+ this.isTranslucent = isTranslucent;
+ this.clipRect = new Rect(clipRect);
+ this.prefixOrderIndex = prefixOrderIndex;
+ this.position = new Point(position);
+ this.sourceContainerBounds = new Rect(sourceContainerBounds);
+ }
+
+ public RemoteAnimationTarget(Parcel in) {
+ taskId = in.readInt();
+ mode = in.readInt();
+ leash = in.readParcelable(null);
+ isTranslucent = in.readBoolean();
+ clipRect = in.readParcelable(null);
+ prefixOrderIndex = in.readInt();
+ position = in.readParcelable(null);
+ sourceContainerBounds = in.readParcelable(null);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(taskId);
+ dest.writeInt(mode);
+ dest.writeParcelable(leash, 0 /* flags */);
+ dest.writeBoolean(isTranslucent);
+ dest.writeParcelable(clipRect, 0 /* flags */);
+ dest.writeInt(prefixOrderIndex);
+ dest.writeParcelable(position, 0 /* flags */);
+ dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
+ }
+
+ public static final Creator<RemoteAnimationTarget> CREATOR
+ = new Creator<RemoteAnimationTarget>() {
+ public RemoteAnimationTarget createFromParcel(Parcel in) {
+ return new RemoteAnimationTarget(in);
+ }
+
+ public RemoteAnimationTarget[] newArray(int size) {
+ return new RemoteAnimationTarget[size];
+ }
+ };
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ad71b58514e3..f461e45372d3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3226,6 +3226,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static final int PFLAG3_SCREEN_READER_FOCUSABLE = 0x10000000;
+ /**
+ * The last aggregated visibility. Used to detect when it truly changes.
+ */
+ private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000;
+
/* End of masks for mPrivateFlags3 */
/**
@@ -3387,6 +3392,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* decorations when they are shown. You can perform layout of your inner
* UI elements to account for non-fullscreen system UI through the
* {@link #fitSystemWindows(Rect)} method.
+ *
+ * <p>Note: on displays that have a {@link DisplayCutout}, the window may still be placed
+ * differently than if {@link #SYSTEM_UI_FLAG_FULLSCREEN} was set, if the
+ * window's {@link WindowManager.LayoutParams#layoutInDisplayCutoutMode
+ * layoutInDisplayCutoutMode} is
+ * {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ * LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}. To avoid this, use either of the other modes.
+ *
+ * @see WindowManager.LayoutParams#layoutInDisplayCutoutMode
+ * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
@@ -4193,6 +4210,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static boolean sCanFocusZeroSized;
+ /**
+ * Always assign focus if a focusable View is available.
+ */
+ private static boolean sAlwaysAssignFocus;
+
private String mTransitionName;
static class TintInfo {
@@ -4810,6 +4832,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P;
+ sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -7000,8 +7024,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* Called when this view wants to give up focus. If focus is cleared
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
* <p>
- * <strong>Note:</strong> When a View clears focus the framework is trying
- * to give focus to the first focusable View from the top. Hence, if this
+ * <strong>Note:</strong> When not in touch-mode, the framework will try to give focus
+ * to the first focusable View from the top after focus is cleared. Hence, if this
* View is the first from the top that can take focus, then all callbacks
* related to clearing focus will be invoked after which the framework will
* give focus to this view.
@@ -7012,7 +7036,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
System.out.println(this + " clearFocus()");
}
- clearFocusInternal(null, true, true);
+ final boolean refocus = sAlwaysAssignFocus || !isInTouchMode();
+ clearFocusInternal(null, true, refocus);
}
/**
@@ -7207,20 +7232,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
}
- private void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
- if (isAutofillable() && isAttachedToWindow()) {
+ /** @hide */
+ public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
+ if (canNotifyAutofillEnterExitEvent()) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
- if (enter && hasWindowFocus() && isFocused()) {
+ if (enter && isFocused()) {
// We have not been laid out yet, hence cannot evaluate
// whether this view is visible to the user, we will do
// the evaluation once layout is complete.
if (!isLaidOut()) {
mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
} else if (isVisibleToUser()) {
+ // TODO This is a potential problem that View gets focus before it's visible
+ // to User. Ideally View should handle the event when isVisibleToUser()
+ // becomes true where it should issue notifyViewEntered().
afm.notifyViewEntered(this);
}
- } else if (!hasWindowFocus() || !isFocused()) {
+ } else if (!isFocused()) {
afm.notifyViewExited(this);
}
}
@@ -7354,7 +7383,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
- if (!isShown()) {
+ // Panes disappearing are relevant even if though the view is no longer visible.
+ boolean isWindowStateChanged =
+ (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0);
+ if (!isShown() && !isWindowDisappearedEvent) {
return;
}
onInitializeAccessibilityEvent(event);
@@ -7462,6 +7496,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
+ if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
+ && !TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ event.getText().add(getAccessibilityPaneTitle());
+ }
}
/**
@@ -8283,6 +8321,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
&& getAutofillViewId() > LAST_APP_AUTOFILL_ID;
}
+ /** @hide */
+ public boolean canNotifyAutofillEnterExitEvent() {
+ return isAutofillable() && isAttachedToWindow();
+ }
+
private void populateVirtualStructure(ViewStructure structure,
AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
@@ -11587,6 +11630,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
return;
}
+ // Changes to views with a pane title count as window state changes, as the pane title
+ // marks them as significant parts of the UI.
+ if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ event.setContentChangeTypes(changeType);
+ onPopulateAccessibilityEvent(event);
+ if (mParent != null) {
+ try {
+ mParent.requestSendAccessibilityEvent(this, event);
+ } catch (AbstractMethodError e) {
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
+ + " does not fully implement ViewParent", e);
+ }
+ }
+ }
+
if (mParent != null) {
try {
mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
@@ -12400,8 +12460,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
imm.focusIn(this);
}
- notifyEnterOrExitForAutoFillIfNeeded(hasWindowFocus);
-
refreshDrawableState();
}
@@ -12520,6 +12578,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@CallSuper
public void onVisibilityAggregated(boolean isVisible) {
+ // Update our internal visibility tracking so we can detect changes
+ boolean oldVisible = (mPrivateFlags3 & PFLAG3_AGGREGATED_VISIBLE) != 0;
+ mPrivateFlags3 = isVisible ? (mPrivateFlags3 | PFLAG3_AGGREGATED_VISIBLE)
+ : (mPrivateFlags3 & ~PFLAG3_AGGREGATED_VISIBLE);
if (isVisible && mAttachInfo != null) {
initialAwakenScrollBars();
}
@@ -12560,6 +12622,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
}
+ if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ if (isVisible != oldVisible) {
+ notifyAccessibilityStateChanged(isVisible
+ ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
+ : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
+ }
+ }
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f81a4c33271a..30f584c570ca 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -20,7 +20,7 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
-import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
@@ -530,7 +530,7 @@ public final class ViewRootImpl implements ViewParent,
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
- sAlwaysAssignFocus = true;
+ sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
sCompatibilityDone = true;
}
@@ -1596,9 +1596,9 @@ public final class ViewRootImpl implements ViewParent,
void dispatchApplyInsets(View host) {
WindowInsets insets = getWindowInsets(true /* forceConstruct */);
- final boolean layoutInCutout =
- (mWindowAttributes.flags2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0;
- if (!layoutInCutout) {
+ final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
+ == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
+ if (!dispatchCutout) {
// Window is either not laid out in cutout or the status bar inset takes care of
// clearing the cutout, so we don't need to dispatch the cutout to the hierarchy.
insets = insets.consumeDisplayCutout();
@@ -2337,7 +2337,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mFirst) {
- if (sAlwaysAssignFocus) {
+ if (sAlwaysAssignFocus || !isInTouchMode()) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus());
@@ -3608,7 +3608,7 @@ public final class ViewRootImpl implements ViewParent,
checkThread();
if (mView != null) {
if (!mView.hasFocus()) {
- if (sAlwaysAssignFocus) {
+ if (sAlwaysAssignFocus || !isInTouchMode()) {
v.requestFocus();
}
} else {
@@ -4211,10 +4211,7 @@ public final class ViewRootImpl implements ViewParent,
// find the best view to give focus to in this brave new non-touch-mode
// world
- final View focused = focusSearch(null, View.FOCUS_DOWN);
- if (focused != null) {
- return focused.requestFocus(View.FOCUS_DOWN);
- }
+ return mView.restoreDefaultFocus();
}
return false;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a65aba152c83..3bb3a4c17b8f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -21,7 +21,6 @@ import static android.view.WindowLayoutParamsProto.ALPHA;
import static android.view.WindowLayoutParamsProto.BUTTON_BRIGHTNESS;
import static android.view.WindowLayoutParamsProto.COLOR_MODE;
import static android.view.WindowLayoutParamsProto.FLAGS;
-import static android.view.WindowLayoutParamsProto.FLAGS_EXTRA;
import static android.view.WindowLayoutParamsProto.FORMAT;
import static android.view.WindowLayoutParamsProto.GRAVITY;
import static android.view.WindowLayoutParamsProto.HAS_SYSTEM_UI_LISTENERS;
@@ -46,7 +45,6 @@ import static android.view.WindowLayoutParamsProto.Y;
import android.Manifest.permission;
import android.annotation.IntDef;
-import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -107,6 +105,191 @@ public interface WindowManager extends ViewManager {
final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
/**
+ * Not set up for a transition.
+ * @hide
+ */
+ int TRANSIT_UNSET = -1;
+
+ /**
+ * No animation for transition.
+ * @hide
+ */
+ int TRANSIT_NONE = 0;
+
+ /**
+ * A window in a new activity is being opened on top of an existing one in the same task.
+ * @hide
+ */
+ int TRANSIT_ACTIVITY_OPEN = 6;
+
+ /**
+ * The window in the top-most activity is being closed to reveal the previous activity in the
+ * same task.
+ * @hide
+ */
+ int TRANSIT_ACTIVITY_CLOSE = 7;
+
+ /**
+ * A window in a new task is being opened on top of an existing one in another activity's task.
+ * @hide
+ */
+ int TRANSIT_TASK_OPEN = 8;
+
+ /**
+ * A window in the top-most activity is being closed to reveal the previous activity in a
+ * different task.
+ * @hide
+ */
+ int TRANSIT_TASK_CLOSE = 9;
+
+ /**
+ * A window in an existing task is being displayed on top of an existing one in another
+ * activity's task.
+ * @hide
+ */
+ int TRANSIT_TASK_TO_FRONT = 10;
+
+ /**
+ * A window in an existing task is being put below all other tasks.
+ * @hide
+ */
+ int TRANSIT_TASK_TO_BACK = 11;
+
+ /**
+ * A window in a new activity that doesn't have a wallpaper is being opened on top of one that
+ * does, effectively closing the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_CLOSE = 12;
+
+ /**
+ * A window in a new activity that does have a wallpaper is being opened on one that didn't,
+ * effectively opening the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_OPEN = 13;
+
+ /**
+ * A window in a new activity is being opened on top of an existing one, and both are on top
+ * of the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
+
+ /**
+ * The window in the top-most activity is being closed to reveal the previous activity, and
+ * both are on top of the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+
+ /**
+ * A window in a new task is being opened behind an existing one in another activity's task.
+ * The new window will show briefly and then be gone.
+ * @hide
+ */
+ int TRANSIT_TASK_OPEN_BEHIND = 16;
+
+ /**
+ * A window in a task is being animated in-place.
+ * @hide
+ */
+ int TRANSIT_TASK_IN_PLACE = 17;
+
+ /**
+ * An activity is being relaunched (e.g. due to configuration change).
+ * @hide
+ */
+ int TRANSIT_ACTIVITY_RELAUNCH = 18;
+
+ /**
+ * A task is being docked from recents.
+ * @hide
+ */
+ int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
+
+ /**
+ * Keyguard is going away.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_GOING_AWAY = 20;
+
+ /**
+ * Keyguard is going away with showing an activity behind that requests wallpaper.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
+
+ /**
+ * Keyguard is being occluded.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_OCCLUDE = 22;
+
+ /**
+ * Keyguard is being unoccluded.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "TRANSIT_" }, value = {
+ TRANSIT_UNSET,
+ TRANSIT_NONE,
+ TRANSIT_ACTIVITY_OPEN,
+ TRANSIT_ACTIVITY_CLOSE,
+ TRANSIT_TASK_OPEN,
+ TRANSIT_TASK_CLOSE,
+ TRANSIT_TASK_TO_FRONT,
+ TRANSIT_TASK_TO_BACK,
+ TRANSIT_WALLPAPER_CLOSE,
+ TRANSIT_WALLPAPER_OPEN,
+ TRANSIT_WALLPAPER_INTRA_OPEN,
+ TRANSIT_WALLPAPER_INTRA_CLOSE,
+ TRANSIT_TASK_OPEN_BEHIND,
+ TRANSIT_TASK_IN_PLACE,
+ TRANSIT_ACTIVITY_RELAUNCH,
+ TRANSIT_DOCK_TASK_FROM_RECENTS,
+ TRANSIT_KEYGUARD_GOING_AWAY,
+ TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ TRANSIT_KEYGUARD_OCCLUDE,
+ TRANSIT_KEYGUARD_UNOCCLUDE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface TransitionType {}
+
+ /**
+ * Transition flag: Keyguard is going away, but keeping the notification shade open
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE = 0x1;
+
+ /**
+ * Transition flag: Keyguard is going away, but doesn't want an animation for it
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION = 0x2;
+
+ /**
+ * Transition flag: Keyguard is going away while it was showing the system wallpaper.
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER = 0x4;
+
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface TransitionFlags {}
+
+ /**
* Exception that is thrown when trying to add view whose
* {@link LayoutParams} {@link LayoutParams#token}
* is invalid.
@@ -889,7 +1072,12 @@ public interface WindowManager extends ViewManager {
* decorations around the border (such as the status bar). The
* window must correctly position its contents to take the screen
* decoration into account. This flag is normally set for you
- * by Window as described in {@link Window#setFlags}. */
+ * by Window as described in {@link Window#setFlags}.
+ *
+ * <p>Note: on displays that have a {@link DisplayCutout}, the window may be placed
+ * such that it avoids the {@link DisplayCutout} area if necessary according to the
+ * {@link #layoutInDisplayCutoutMode}.
+ */
public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;
/** Window flag: allow window to extend outside of the screen. */
@@ -1295,33 +1483,6 @@ public interface WindowManager extends ViewManager {
}, formatToHexString = true)
public int flags;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @LongDef(
- flag = true,
- value = {
- LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA,
- })
- @interface Flags2 {}
-
- /**
- * Window flag: allow placing the window within the area that overlaps with the
- * display cutout.
- *
- * <p>
- * The window must correctly position its contents to take the display cutout into account.
- *
- * @see DisplayCutout
- */
- public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 0x00000001;
-
- /**
- * Various behavioral options/flags. Default is none.
- *
- * @see #FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA
- */
- @Flags2 public long flags2;
-
/**
* If the window has requested hardware acceleration, but this is not
* allowed in the process it is in, then still render it as if it is
@@ -2050,6 +2211,77 @@ public interface WindowManager extends ViewManager {
*/
public boolean hasSystemUiListeners;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ flag = true,
+ value = {LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT,
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS,
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER})
+ @interface LayoutInDisplayCutoutMode {}
+
+ /**
+ * Controls how the window is laid out if there is a {@link DisplayCutout}.
+ *
+ * <p>
+ * Defaults to {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}.
+ *
+ * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
+ * @see DisplayCutout
+ */
+ @LayoutInDisplayCutoutMode
+ public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+
+ /**
+ * The window is allowed to extend into the {@link DisplayCutout} area, only if the
+ * {@link DisplayCutout} is fully contained within the status bar. Otherwise, the window is
+ * laid out such that it does not overlap with the {@link DisplayCutout} area.
+ *
+ * <p>
+ * In practice, this means that if the window did not set FLAG_FULLSCREEN or
+ * SYSTEM_UI_FLAG_FULLSCREEN, it can extend into the cutout area in portrait.
+ * Otherwise (i.e. fullscreen or landscape) it is laid out such that it does overlap the
+ * cutout area.
+ *
+ * <p>
+ * The usual precautions for not overlapping with the status bar are sufficient for ensuring
+ * that no important content overlaps with the DisplayCutout.
+ *
+ * @see DisplayCutout
+ * @see WindowInsets
+ */
+ public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;
+
+ /**
+ * The window is always allowed to extend into the {@link DisplayCutout} area,
+ * even if fullscreen or in landscape.
+ *
+ * <p>
+ * The window must make sure that no important content overlaps with the
+ * {@link DisplayCutout}.
+ *
+ * @see DisplayCutout
+ * @see WindowInsets#getDisplayCutout()
+ */
+ public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1;
+
+ /**
+ * The window is never allowed to overlap with the DisplayCutout area.
+ *
+ * <p>
+ * This should be used with windows that transiently set SYSTEM_UI_FLAG_FULLSCREEN to
+ * avoid a relayout of the window when the flag is set or cleared.
+ *
+ * @see DisplayCutout
+ * @see View#SYSTEM_UI_FLAG_FULLSCREEN SYSTEM_UI_FLAG_FULLSCREEN
+ * @see View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ */
+ public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;
+
+
/**
* When this window has focus, disable touch pad pointer gesture processing.
* The window will receive raw position updates from the touch pad instead
@@ -2273,9 +2505,9 @@ public interface WindowManager extends ViewManager {
out.writeInt(y);
out.writeInt(type);
out.writeInt(flags);
- out.writeLong(flags2);
out.writeInt(privateFlags);
out.writeInt(softInputMode);
+ out.writeInt(layoutInDisplayCutoutMode);
out.writeInt(gravity);
out.writeFloat(horizontalMargin);
out.writeFloat(verticalMargin);
@@ -2329,9 +2561,9 @@ public interface WindowManager extends ViewManager {
y = in.readInt();
type = in.readInt();
flags = in.readInt();
- flags2 = in.readLong();
privateFlags = in.readInt();
softInputMode = in.readInt();
+ layoutInDisplayCutoutMode = in.readInt();
gravity = in.readInt();
horizontalMargin = in.readFloat();
verticalMargin = in.readFloat();
@@ -2462,10 +2694,6 @@ public interface WindowManager extends ViewManager {
flags = o.flags;
changes |= FLAGS_CHANGED;
}
- if (flags2 != o.flags2) {
- flags2 = o.flags2;
- changes |= FLAGS_CHANGED;
- }
if (privateFlags != o.privateFlags) {
privateFlags = o.privateFlags;
changes |= PRIVATE_FLAGS_CHANGED;
@@ -2474,6 +2702,10 @@ public interface WindowManager extends ViewManager {
softInputMode = o.softInputMode;
changes |= SOFT_INPUT_MODE_CHANGED;
}
+ if (layoutInDisplayCutoutMode != o.layoutInDisplayCutoutMode) {
+ layoutInDisplayCutoutMode = o.layoutInDisplayCutoutMode;
+ changes |= LAYOUT_CHANGED;
+ }
if (gravity != o.gravity) {
gravity = o.gravity;
changes |= LAYOUT_CHANGED;
@@ -2651,6 +2883,10 @@ public interface WindowManager extends ViewManager {
sb.append(softInputModeToString(softInputMode));
sb.append('}');
}
+ if (layoutInDisplayCutoutMode != 0) {
+ sb.append(" layoutInDisplayCutoutMode=");
+ sb.append(layoutInDisplayCutoutModeToString(layoutInDisplayCutoutMode));
+ }
sb.append(" ty=");
sb.append(ViewDebug.intToString(LayoutParams.class, "type", type));
if (format != PixelFormat.OPAQUE) {
@@ -2719,11 +2955,6 @@ public interface WindowManager extends ViewManager {
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
ViewDebug.flagsToString(LayoutParams.class, "flags", flags));
- if (flags2 != 0) {
- sb.append(System.lineSeparator());
- // TODO(roosa): add a long overload for ViewDebug.flagsToString.
- sb.append(prefix).append(" fl2=0x").append(Long.toHexString(flags2));
- }
if (privateFlags != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" pfl=").append(ViewDebug.flagsToString(
@@ -2771,7 +3002,6 @@ public interface WindowManager extends ViewManager {
proto.write(NEEDS_MENU_KEY, needsMenuKey);
proto.write(COLOR_MODE, mColorMode);
proto.write(FLAGS, flags);
- proto.write(FLAGS_EXTRA, flags2);
proto.write(PRIVATE_FLAGS, privateFlags);
proto.write(SYSTEM_UI_VISIBILITY_FLAGS, systemUiVisibility);
proto.write(SUBTREE_SYSTEM_UI_VISIBILITY_FLAGS, subtreeSystemUiVisibility);
@@ -2849,6 +3079,20 @@ public interface WindowManager extends ViewManager {
&& height == WindowManager.LayoutParams.MATCH_PARENT;
}
+ private static String layoutInDisplayCutoutModeToString(
+ @LayoutInDisplayCutoutMode int mode) {
+ switch (mode) {
+ case LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
+ return "default";
+ case LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:
+ return "always";
+ case LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
+ return "never";
+ default:
+ return "unknown(" + mode + ")";
+ }
+ }
+
private static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
final StringBuilder result = new StringBuilder();
final int state = softInputMode & SOFT_INPUT_MASK_STATE;
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 9c1c9e34cfb4..a6f36bbf4ef4 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -45,6 +45,11 @@ public interface WindowManagerPolicyConstants {
int PRESENCE_INTERNAL = 1 << 0;
int PRESENCE_EXTERNAL = 1 << 1;
+ // Navigation bar position values
+ int NAV_BAR_LEFT = 1 << 0;
+ int NAV_BAR_RIGHT = 1 << 1;
+ int NAV_BAR_BOTTOM = 1 << 2;
+
/**
* Sticky broadcast of the current HDMI plugged state.
*/
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index aa61926dcedc..e0f74a7d6dd6 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -192,9 +192,11 @@ import java.util.List;
* <b>TRANSITION TYPES</b></br>
* </p>
* <p>
- * <b>Window state changed</b> - represents the event of opening a
- * {@link android.widget.PopupWindow}, {@link android.view.Menu},
- * {@link android.app.Dialog}, etc.</br>
+ * <b>Window state changed</b> - represents the event of a change to a section of
+ * the user interface that is visually distinct. Should be sent from either the
+ * root view of a window or from a view that is marked as a pane
+ * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Not that changes
+ * to true windows are represented by {@link #TYPE_WINDOWS_CHANGED}.</br>
* <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br>
* <em>Properties:</em></br>
* <ul>
@@ -203,7 +205,7 @@ import java.util.List;
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
- * <li>{@link #getText()} - The text of the source's sub-tree.</li>
+ * <li>{@link #getText()} - The text of the source's sub-tree, including the pane titles.</li>
* </ul>
* </p>
* <p>
@@ -436,8 +438,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
/**
- * Represents the event of opening a {@link android.widget.PopupWindow},
- * {@link android.view.Menu}, {@link android.app.Dialog}, etc.
+ * Represents the event of a change to a visually distinct section of the user interface.
+ * These events should only be dispatched from {@link android.view.View}s that have
+ * accessibility pane titles, and replaces {@link #TYPE_WINDOW_CONTENT_CHANGED} for those
+ * sources. Details about the change are available from {@link #getContentChangeTypes()}.
*/
public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
@@ -565,12 +569,30 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
/**
- * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event:
* The node's pane title changed.
*/
public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 0x00000008;
/**
+ * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event:
+ * The node has a pane title, and either just appeared or just was assigned a title when it
+ * had none before.
+ */
+ public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 0x00000010;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event:
+ * Can mean one of two slightly different things. The primary meaning is that the node has
+ * a pane title, and was removed from the node hierarchy. It will also be sent if the pane
+ * title is set to {@code null} after it contained a title.
+ * No source will be returned if the node is no longer on the screen. To make the change more
+ * clear for the user, the first entry in {@link #getText()} will return the value that would
+ * have been returned by {@code getSource().getPaneTitle()}.
+ */
+ public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0x00000020;
+
+ /**
* Change type for {@link #TYPE_WINDOWS_CHANGED} event:
* The window was added.
*/
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 78b41c6f4c7b..de9b0d7dfa4a 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -341,6 +341,10 @@ public final class AutofillManager {
@GuardedBy("mLock")
@Nullable private AutofillId mSaveTriggerId;
+ /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
+ @GuardedBy("mLock")
+ private boolean mOnInvisibleCalled;
+
/** If set, session is commited when the activity is finished; otherwise session is canceled. */
@GuardedBy("mLock")
private boolean mSaveOnFinish;
@@ -397,6 +401,11 @@ public final class AutofillManager {
boolean isVisibleForAutofill();
/**
+ * Client might disable enter/exit event e.g. when activity is paused.
+ */
+ boolean isDisablingEnterExitEventForAutofill();
+
+ /**
* Finds views by traversing the hierarchies of the client.
*
* @param viewIds The autofill ids of the views to find
@@ -499,6 +508,19 @@ public final class AutofillManager {
}
/**
+ * Called once the client becomes invisible.
+ *
+ * @see AutofillClient#isVisibleForAutofill()
+ *
+ * {@hide}
+ */
+ public void onInvisibleForAutofill() {
+ synchronized (mLock) {
+ mOnInvisibleCalled = true;
+ }
+ }
+
+ /**
* Save state before activity lifecycle
*
* @param outState Place to store the state
@@ -623,21 +645,45 @@ public final class AutofillManager {
return false;
}
+ private boolean isClientVisibleForAutofillLocked() {
+ final AutofillClient client = getClient();
+ return client != null && client.isVisibleForAutofill();
+ }
+
+ private boolean isClientDisablingEnterExitEvent() {
+ final AutofillClient client = getClient();
+ return client != null && client.isDisablingEnterExitEventForAutofill();
+ }
+
private void notifyViewEntered(@NonNull View view, int flags) {
if (!hasAutofillFeature()) {
return;
}
- AutofillCallback callback = null;
+ AutofillCallback callback;
synchronized (mLock) {
- if (shouldIgnoreViewEnteredLocked(view, flags)) return;
+ callback = notifyViewEnteredLocked(view, flags);
+ }
- ensureServiceClientAddedIfNeededLocked();
+ if (callback != null) {
+ mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+ }
+ }
- if (!mEnabled) {
- if (mCallback != null) {
- callback = mCallback;
- }
- } else {
+ /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
+ if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
+
+ AutofillCallback callback = null;
+
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (!mEnabled) {
+ if (mCallback != null) {
+ callback = mCallback;
+ }
+ } else {
+ // don't notify entered when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view);
final AutofillValue value = view.getAutofillValue();
@@ -650,10 +696,7 @@ public final class AutofillManager {
}
}
}
-
- if (callback != null) {
- mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
- }
+ return callback;
}
/**
@@ -666,9 +709,16 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- ensureServiceClientAddedIfNeededLocked();
+ notifyViewExitedLocked(view);
+ }
+ }
- if (mEnabled && isActiveLocked()) {
+ void notifyViewExitedLocked(@NonNull View view) {
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (mEnabled && isActiveLocked()) {
+ // dont notify exited when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view);
// Update focus on existing session.
@@ -719,7 +769,7 @@ public final class AutofillManager {
}
}
if (mTrackedViews != null) {
- mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
+ mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
}
}
}
@@ -752,17 +802,32 @@ public final class AutofillManager {
if (!hasAutofillFeature()) {
return;
}
- AutofillCallback callback = null;
+ AutofillCallback callback;
synchronized (mLock) {
- if (shouldIgnoreViewEnteredLocked(view, flags)) return;
+ callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
+ }
- ensureServiceClientAddedIfNeededLocked();
+ if (callback != null) {
+ callback.onAutofillEvent(view, virtualId,
+ AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+ }
+ }
- if (!mEnabled) {
- if (mCallback != null) {
- callback = mCallback;
- }
- } else {
+ /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
+ int flags) {
+ AutofillCallback callback = null;
+ if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
+
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (!mEnabled) {
+ if (mCallback != null) {
+ callback = mCallback;
+ }
+ } else {
+ // don't notify entered when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
if (!isActiveLocked()) {
@@ -774,11 +839,7 @@ public final class AutofillManager {
}
}
}
-
- if (callback != null) {
- callback.onAutofillEvent(view, virtualId,
- AutofillCallback.EVENT_INPUT_UNAVAILABLE);
- }
+ return callback;
}
/**
@@ -792,9 +853,16 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- ensureServiceClientAddedIfNeededLocked();
+ notifyViewExitedLocked(view, virtualId);
+ }
+ }
- if (mEnabled && isActiveLocked()) {
+ private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (mEnabled && isActiveLocked()) {
+ // don't notify exited when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
// Update focus on existing session.
@@ -1155,7 +1223,7 @@ public final class AutofillManager {
}
/** @hide */
- public void onAuthenticationResult(int authenticationId, Intent data) {
+ public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
if (!hasAutofillFeature()) {
return;
}
@@ -1167,9 +1235,24 @@ public final class AutofillManager {
if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
synchronized (mLock) {
- if (!isActiveLocked() || data == null) {
+ if (!isActiveLocked()) {
return;
}
+ // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
+ // of app activity. We enforce enter event to re-show fill ui in such case.
+ // CTS example:
+ // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
+ // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
+ if (!mOnInvisibleCalled && focusView != null
+ && focusView.canNotifyAutofillEnterExitEvent()) {
+ notifyViewExitedLocked(focusView);
+ notifyViewEnteredLocked(focusView, 0);
+ }
+ if (data == null) {
+ // data is set to null when result is not RESULT_OK
+ return;
+ }
+
final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
final Bundle responseData = new Bundle();
responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
@@ -1402,6 +1485,9 @@ public final class AutofillManager {
if (sessionId == mSessionId) {
final AutofillClient client = getClient();
if (client != null) {
+ // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
+ // before onAuthenticationResult()
+ mOnInvisibleCalled = false;
client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
}
}
@@ -1767,6 +1853,7 @@ public final class AutofillManager {
pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
+ pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
pw.print(pfx); pw.print("tracked views: ");
if (mTrackedViews == null) {
@@ -1937,15 +2024,13 @@ public final class AutofillManager {
* @param id the id of the view/virtual view whose visibility changed.
* @param isVisible visible if the view is visible in the view hierarchy.
*/
- void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
- AutofillClient client = getClient();
-
+ void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
if (sDebug) {
Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
+ isVisible);
}
- if (client != null && client.isVisibleForAutofill()) {
+ if (isClientVisibleForAutofillLocked()) {
if (isVisible) {
if (isInSet(mInvisibleTrackedIds, id)) {
mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index 5cba21e3cc07..e80fdd93542c 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -78,8 +78,10 @@ public class AutofillPopupWindow extends PopupWindow {
public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
mWindowPresenter = new WindowPresenter(presenter);
+ setTouchModal(false);
setOutsideTouchable(true);
- setInputMethodMode(INPUT_METHOD_NEEDED);
+ setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
+ setFocusable(true);
}
@Override
diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java
index 003f221d08b2..1eb300eafb66 100644
--- a/core/java/android/view/inputmethod/ExtractedText.java
+++ b/core/java/android/view/inputmethod/ExtractedText.java
@@ -29,6 +29,8 @@ import android.text.TextUtils;
public class ExtractedText implements Parcelable {
/**
* The text that has been extracted.
+ *
+ * @see android.widget.TextView#getText()
*/
public CharSequence text;
@@ -88,6 +90,8 @@ public class ExtractedText implements Parcelable {
/**
* The hint that has been extracted.
+ *
+ * @see android.widget.TextView#getHint()
*/
public CharSequence hint;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 57f9895f45fa..e5545405728d 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1,17 +1,17 @@
/*
- * Copyright (C) 2007-2008 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, 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.
+ * 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.view.inputmethod;
@@ -131,13 +131,13 @@ public interface InputConnection {
* spans. <strong>Editor authors</strong>: you should strive to
* send text with styles if possible, but it is not required.
*/
- static final int GET_TEXT_WITH_STYLES = 0x0001;
+ int GET_TEXT_WITH_STYLES = 0x0001;
/**
* Flag for use with {@link #getExtractedText} to indicate you
* would like to receive updates when the extracted text changes.
*/
- public static final int GET_EXTRACTED_TEXT_MONITOR = 0x0001;
+ int GET_EXTRACTED_TEXT_MONITOR = 0x0001;
/**
* Get <var>n</var> characters of text before the current cursor
@@ -176,7 +176,7 @@ public interface InputConnection {
* @return the text before the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextBeforeCursor(int n, int flags);
+ CharSequence getTextBeforeCursor(int n, int flags);
/**
* Get <var>n</var> characters of text after the current cursor
@@ -215,7 +215,7 @@ public interface InputConnection {
* @return the text after the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextAfterCursor(int n, int flags);
+ CharSequence getTextAfterCursor(int n, int flags);
/**
* Gets the selected text, if any.
@@ -249,7 +249,7 @@ public interface InputConnection {
* later, returns false when the target application does not implement
* this method.
*/
- public CharSequence getSelectedText(int flags);
+ CharSequence getSelectedText(int flags);
/**
* Retrieve the current capitalization mode in effect at the
@@ -279,7 +279,7 @@ public interface InputConnection {
* @return the caps mode flags that are in effect at the current
* cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
*/
- public int getCursorCapsMode(int reqModes);
+ int getCursorCapsMode(int reqModes);
/**
* Retrieve the current text in the input connection's editor, and
@@ -314,8 +314,7 @@ public interface InputConnection {
* longer valid of the editor can't comply with the request for
* some reason.
*/
- public ExtractedText getExtractedText(ExtractedTextRequest request,
- int flags);
+ ExtractedText getExtractedText(ExtractedTextRequest request, int flags);
/**
* Delete <var>beforeLength</var> characters of text before the
@@ -342,8 +341,8 @@ public interface InputConnection {
* delete more characters than are in the editor, as that may have
* ill effects on the application. Calling this method will cause
* the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on your service after the batch input is over.</p>
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on your service after the batch input is over.</p>
*
* <p><strong>Editor authors:</strong> please be careful of race
* conditions in implementing this call. An IME can make a change
@@ -369,7 +368,7 @@ public interface InputConnection {
* that range.
* @return true on success, false if the input connection is no longer valid.
*/
- public boolean deleteSurroundingText(int beforeLength, int afterLength);
+ boolean deleteSurroundingText(int beforeLength, int afterLength);
/**
* A variant of {@link #deleteSurroundingText(int, int)}. Major differences are:
@@ -397,7 +396,7 @@ public interface InputConnection {
* @return true on success, false if the input connection is no longer valid. Returns
* {@code false} when the target application does not implement this method.
*/
- public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
+ boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
/**
* Replace the currently composing text with the given text, and
@@ -416,8 +415,8 @@ public interface InputConnection {
* <p>This is usually called by IMEs to add or remove or change
* characters in the composing span. Calling this method will
* cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.</p>
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.</p>
*
* <p><strong>Editor authors:</strong> please keep in mind the
* text may be very similar or completely different than what was
@@ -455,7 +454,7 @@ public interface InputConnection {
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean setComposingText(CharSequence text, int newCursorPosition);
+ boolean setComposingText(CharSequence text, int newCursorPosition);
/**
* Mark a certain region of text as composing text. If there was a
@@ -474,8 +473,8 @@ public interface InputConnection {
* <p>Since this does not change the contents of the text, editors should not call
* {@link InputMethodManager#updateSelection(View, int, int, int, int)} and
* IMEs should not receive
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}.
- * </p>
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)}.</p>
*
* <p>This has no impact on the cursor/selection position. It may
* result in the cursor being anywhere inside or outside the
@@ -488,7 +487,7 @@ public interface InputConnection {
* valid. In {@link android.os.Build.VERSION_CODES#N} and later, false is returned when the
* target application does not implement this method.
*/
- public boolean setComposingRegion(int start, int end);
+ boolean setComposingRegion(int start, int end);
/**
* Have the text editor finish whatever composing text is
@@ -507,7 +506,7 @@ public interface InputConnection {
* @return true on success, false if the input connection
* is no longer valid.
*/
- public boolean finishComposingText();
+ boolean finishComposingText();
/**
* Commit text to the text box and set the new cursor position.
@@ -522,8 +521,8 @@ public interface InputConnection {
* then {@link #finishComposingText()}.</p>
*
* <p>Calling this method will cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -543,7 +542,7 @@ public interface InputConnection {
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean commitText(CharSequence text, int newCursorPosition);
+ boolean commitText(CharSequence text, int newCursorPosition);
/**
* Commit a completion the user has selected from the possible ones
@@ -569,8 +568,8 @@ public interface InputConnection {
*
* <p>Calling this method (with a valid {@link CompletionInfo} object)
* will cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -581,15 +580,15 @@ public interface InputConnection {
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean commitCompletion(CompletionInfo text);
+ boolean commitCompletion(CompletionInfo text);
/**
* Commit a correction automatically performed on the raw user's input. A
* typical example would be to correct typos using a dictionary.
*
* <p>Calling this method will cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -601,7 +600,7 @@ public interface InputConnection {
* In {@link android.os.Build.VERSION_CODES#N} and later, returns false
* when the target application does not implement this method.
*/
- public boolean commitCorrection(CorrectionInfo correctionInfo);
+ boolean commitCorrection(CorrectionInfo correctionInfo);
/**
* Set the selection of the text editor. To set the cursor
@@ -609,8 +608,8 @@ public interface InputConnection {
*
* <p>Since this moves the cursor, calling this method will cause
* the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -628,7 +627,7 @@ public interface InputConnection {
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean setSelection(int start, int end);
+ boolean setSelection(int start, int end);
/**
* Have the editor perform an action it has said it can do.
@@ -642,7 +641,7 @@ public interface InputConnection {
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean performEditorAction(int editorAction);
+ boolean performEditorAction(int editorAction);
/**
* Perform a context menu action on the field. The given id may be one of:
@@ -652,7 +651,7 @@ public interface InputConnection {
* {@link android.R.id#paste}, {@link android.R.id#copyUrl},
* or {@link android.R.id#switchInputMethod}
*/
- public boolean performContextMenuAction(int id);
+ boolean performContextMenuAction(int id);
/**
* Tell the editor that you are starting a batch of editor
@@ -662,8 +661,8 @@ public interface InputConnection {
*
* <p><strong>IME authors:</strong> use this to avoid getting
* calls to
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * corresponding to intermediate state. Also, use this to avoid
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} corresponding to intermediate state. Also, use this to avoid
* flickers that may arise from displaying intermediate state. Be
* sure to call {@link #endBatchEdit} for each call to this, or
* you may block updates in the editor.</p>
@@ -678,7 +677,7 @@ public interface InputConnection {
* this method starts a batch edit, that means it will always return true
* unless the input connection is no longer valid.
*/
- public boolean beginBatchEdit();
+ boolean beginBatchEdit();
/**
* Tell the editor that you are done with a batch edit previously
@@ -696,7 +695,7 @@ public interface InputConnection {
* the latest one (in other words, if the nesting count is > 0), false
* otherwise or if the input connection is no longer valid.
*/
- public boolean endBatchEdit();
+ boolean endBatchEdit();
/**
* Send a key event to the process that is currently attached
@@ -734,7 +733,7 @@ public interface InputConnection {
* @see KeyCharacterMap#PREDICTIVE
* @see KeyCharacterMap#ALPHA
*/
- public boolean sendKeyEvent(KeyEvent event);
+ boolean sendKeyEvent(KeyEvent event);
/**
* Clear the given meta key pressed states in the given input
@@ -749,7 +748,7 @@ public interface InputConnection {
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean clearMetaKeyStates(int states);
+ boolean clearMetaKeyStates(int states);
/**
* Called back when the connected IME switches between fullscreen and normal modes.
@@ -766,7 +765,7 @@ public interface InputConnection {
* devices.
* @see InputMethodManager#isFullscreenMode()
*/
- public boolean reportFullscreenMode(boolean enabled);
+ boolean reportFullscreenMode(boolean enabled);
/**
* API to send private commands from an input method to its
@@ -786,7 +785,7 @@ public interface InputConnection {
* associated editor understood it), false if the input connection is no longer
* valid.
*/
- public boolean performPrivateCommand(String action, Bundle data);
+ boolean performPrivateCommand(String action, Bundle data);
/**
* The editor is requested to call
@@ -794,7 +793,7 @@ public interface InputConnection {
* once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
* used together with {@link #CURSOR_UPDATE_MONITOR}.
*/
- public static final int CURSOR_UPDATE_IMMEDIATE = 1 << 0;
+ int CURSOR_UPDATE_IMMEDIATE = 1 << 0;
/**
* The editor is requested to call
@@ -805,7 +804,7 @@ public interface InputConnection {
* This flag can be used together with {@link #CURSOR_UPDATE_IMMEDIATE}.
* </p>
*/
- public static final int CURSOR_UPDATE_MONITOR = 1 << 1;
+ int CURSOR_UPDATE_MONITOR = 1 << 1;
/**
* Called by the input method to ask the editor for calling back
@@ -821,7 +820,7 @@ public interface InputConnection {
* In {@link android.os.Build.VERSION_CODES#N} and later, returns {@code false} also when the
* target application does not implement this method.
*/
- public boolean requestCursorUpdates(int cursorUpdateMode);
+ boolean requestCursorUpdates(int cursorUpdateMode);
/**
* Called by the {@link InputMethodManager} to enable application developers to specify a
@@ -832,7 +831,7 @@ public interface InputConnection {
*
* @return {@code null} to use the default {@link Handler}.
*/
- public Handler getHandler();
+ Handler getHandler();
/**
* Called by the system up to only once to notify that the system is about to invalidate
@@ -846,7 +845,7 @@ public interface InputConnection {
*
* <p>Note: This does nothing when called from input methods.</p>
*/
- public void closeConnection();
+ void closeConnection();
/**
* When this flag is used, the editor will be able to request read access to the content URI
@@ -863,7 +862,7 @@ public interface InputConnection {
* client is able to request a temporary read-only access even after the current IME is switched
* to any other IME as long as the client keeps {@link InputContentInfo} object.</p>
**/
- public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION =
+ int INPUT_CONTENT_GRANT_READ_URI_PERMISSION =
android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; // 0x00000001
/**
@@ -897,6 +896,6 @@ public interface InputConnection {
* @return {@code true} if this request is accepted by the application, whether the request
* is already handled or still being handled in background, {@code false} otherwise.
*/
- public boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
+ boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
@Nullable Bundle opts);
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 317730ca092c..f671e22b4922 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -1,17 +1,17 @@
/*
- * Copyright (C) 2007-2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * 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.view.inputmethod;
@@ -74,6 +74,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public CharSequence getTextBeforeCursor(int n, int flags) {
return mTarget.getTextBeforeCursor(n, flags);
}
@@ -82,6 +83,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public CharSequence getTextAfterCursor(int n, int flags) {
return mTarget.getTextAfterCursor(n, flags);
}
@@ -90,6 +92,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public CharSequence getSelectedText(int flags) {
return mTarget.getSelectedText(flags);
}
@@ -98,6 +101,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public int getCursorCapsMode(int reqModes) {
return mTarget.getCursorCapsMode(reqModes);
}
@@ -106,6 +110,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
return mTarget.getExtractedText(request, flags);
}
@@ -114,6 +119,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
return mTarget.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
}
@@ -122,6 +128,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
return mTarget.deleteSurroundingText(beforeLength, afterLength);
}
@@ -130,6 +137,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
return mTarget.setComposingText(text, newCursorPosition);
}
@@ -138,6 +146,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean setComposingRegion(int start, int end) {
return mTarget.setComposingRegion(start, end);
}
@@ -146,6 +155,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean finishComposingText() {
return mTarget.finishComposingText();
}
@@ -154,6 +164,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitText(CharSequence text, int newCursorPosition) {
return mTarget.commitText(text, newCursorPosition);
}
@@ -162,6 +173,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitCompletion(CompletionInfo text) {
return mTarget.commitCompletion(text);
}
@@ -170,6 +182,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitCorrection(CorrectionInfo correctionInfo) {
return mTarget.commitCorrection(correctionInfo);
}
@@ -178,6 +191,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean setSelection(int start, int end) {
return mTarget.setSelection(start, end);
}
@@ -186,6 +200,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean performEditorAction(int editorAction) {
return mTarget.performEditorAction(editorAction);
}
@@ -194,6 +209,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean performContextMenuAction(int id) {
return mTarget.performContextMenuAction(id);
}
@@ -202,6 +218,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean beginBatchEdit() {
return mTarget.beginBatchEdit();
}
@@ -210,6 +227,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean endBatchEdit() {
return mTarget.endBatchEdit();
}
@@ -218,6 +236,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean sendKeyEvent(KeyEvent event) {
return mTarget.sendKeyEvent(event);
}
@@ -226,6 +245,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean clearMetaKeyStates(int states) {
return mTarget.clearMetaKeyStates(states);
}
@@ -234,6 +254,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean reportFullscreenMode(boolean enabled) {
return mTarget.reportFullscreenMode(enabled);
}
@@ -242,6 +263,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean performPrivateCommand(String action, Bundle data) {
return mTarget.performPrivateCommand(action, data);
}
@@ -250,6 +272,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return mTarget.requestCursorUpdates(cursorUpdateMode);
}
@@ -258,6 +281,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public Handler getHandler() {
return mTarget.getHandler();
}
@@ -266,6 +290,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public void closeConnection() {
mTarget.closeConnection();
}
@@ -274,6 +299,7 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
return mTarget.commitContent(inputContentInfo, flags, opts);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1e291209a373..7db5c3207296 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1082,15 +1082,15 @@ public final class InputMethodManager {
}
/**
- * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
- * input window should only be hidden if it was not explicitly shown
+ * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestHideSelf(int)}
+ * to indicate that the soft input window should only be hidden if it was not explicitly shown
* by the user.
*/
public static final int HIDE_IMPLICIT_ONLY = 0x0001;
/**
- * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
- * input window should normally be hidden, unless it was originally
+ * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestShowSelf(int)}
+ * to indicate that the soft input window should normally be hidden, unless it was originally
* shown with {@link #SHOW_FORCED}.
*/
public static final int HIDE_NOT_ALWAYS = 0x0002;
@@ -1869,9 +1869,9 @@ public final class InputMethodManager {
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY},
* {@link #HIDE_NOT_ALWAYS} bit set.
- * @deprecated Use {@link InputMethodService#hideSoftInputFromInputMethod(int)}
- * instead. This method was intended for IME developers who should be accessing APIs through
- * the service. APIs in this class are intended for app developers interacting with the IME.
+ * @deprecated Use {@link InputMethodService#requestHideSelf(int)} instead. This method was
+ * intended for IME developers who should be accessing APIs through the service. APIs in this
+ * class are intended for app developers interacting with the IME.
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
@@ -1901,9 +1901,9 @@ public final class InputMethodManager {
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #SHOW_IMPLICIT} or
* {@link #SHOW_FORCED} bit set.
- * @deprecated Use {@link InputMethodService#showSoftInputFromInputMethod(int)}
- * instead. This method was intended for IME developers who should be accessing APIs through
- * the service. APIs in this class are intended for app developers interacting with the IME.
+ * @deprecated Use {@link InputMethodService#requestShowSelf(int)} instead. This method was
+ * intended for IME developers who should be accessing APIs through the service. APIs in this
+ * class are intended for app developers interacting with the IME.
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index b3522ec94c0c..e9fe481112a2 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -27,7 +27,6 @@ import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.StrictMode;
import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
@@ -251,7 +250,6 @@ public final class WebViewFactory {
"WebView.disableWebView() was called: WebView is disabled");
}
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
Class<WebViewFactoryProvider> providerClass = getProviderClass();
@@ -279,7 +277,6 @@ public final class WebViewFactory {
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
- StrictMode.setThreadPolicy(oldPolicy);
}
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b5ac33070f1f..247c806e928e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4627,7 +4627,7 @@ public class Editor {
return 0;
}
- protected final void showMagnifier() {
+ protected final void showMagnifier(@NonNull final MotionEvent event) {
if (mMagnifier == null) {
return;
}
@@ -4653,9 +4653,10 @@ public class Editor {
final Layout layout = mTextView.getLayout();
final int lineNumber = layout.getLineForOffset(offset);
- // Horizontally snap to character offset.
- final float xPosInView = getHorizontal(mTextView.getLayout(), offset)
- + mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
+ // Horizontally move the magnifier smoothly.
+ final int[] textViewLocationOnScreen = new int[2];
+ mTextView.getLocationOnScreen(textViewLocationOnScreen);
+ final float xPosInView = event.getRawX() - textViewLocationOnScreen[0];
// Vertically snap to middle of current line.
final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
+ mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
@@ -4850,11 +4851,11 @@ public class Editor {
case MotionEvent.ACTION_DOWN:
mDownPositionX = ev.getRawX();
mDownPositionY = ev.getRawY();
- showMagnifier();
+ showMagnifier(ev);
break;
case MotionEvent.ACTION_MOVE:
- showMagnifier();
+ showMagnifier(ev);
break;
case MotionEvent.ACTION_UP:
@@ -5208,11 +5209,11 @@ public class Editor {
// re-engages the handle.
mTouchWordDelta = 0.0f;
mPrevX = UNSET_X_VALUE;
- showMagnifier();
+ showMagnifier(event);
break;
case MotionEvent.ACTION_MOVE:
- showMagnifier();
+ showMagnifier(event);
break;
case MotionEvent.ACTION_UP:
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 26dfcc2d668a..310b1708cb13 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -32,6 +32,7 @@ import android.view.PixelCopy;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
+import android.view.ViewParent;
import com.android.internal.util.Preconditions;
@@ -44,6 +45,8 @@ public final class Magnifier {
private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
// The view to which this magnifier is attached.
private final View mView;
+ // The coordinates of the view in the surface.
+ private final int[] mViewCoordinatesInSurface;
// The window containing the magnifier.
private final PopupWindow mWindow;
// The center coordinates of the window containing the magnifier.
@@ -87,6 +90,8 @@ public final class Magnifier {
com.android.internal.R.dimen.magnifier_height);
mZoomScale = context.getResources().getFloat(
com.android.internal.R.dimen.magnifier_zoom_scale);
+ // The view's surface coordinates will not be updated until the magnifier is first shown.
+ mViewCoordinatesInSurface = new int[2];
mWindow = new PopupWindow(context);
mWindow.setContentView(content);
@@ -120,9 +125,34 @@ public final class Magnifier {
configureCoordinates(xPosInView, yPosInView);
// Clamp startX value to avoid distorting the rendering of the magnifier content.
- final int startX = Math.max(0, Math.min(
+ // For this, we compute:
+ // - zeroScrollXInSurface: this is the start x of mView, where this is not masked by a
+ // potential scrolling container. For example, if mView is a
+ // TextView contained in a HorizontalScrollView,
+ // mViewCoordinatesInSurface will reflect the surface position of
+ // the first text character, rather than the position of the first
+ // visible one. Therefore, we need to add back the amount of
+ // scrolling from the parent containers.
+ // - actualWidth: similarly, the width of a View will be larger than its actually visible
+ // width when it is contained in a scrolling container. We need to use
+ // the minimum width of a scrolling container which contains this view.
+ int zeroScrollXInSurface = mViewCoordinatesInSurface[0];
+ int actualWidth = mView.getWidth();
+ ViewParent viewParent = mView.getParent();
+ while (viewParent instanceof View) {
+ final View container = (View) viewParent;
+ if (container.canScrollHorizontally(-1 /* left scroll */)
+ || container.canScrollHorizontally(1 /* right scroll */)) {
+ zeroScrollXInSurface += container.getScrollX();
+ actualWidth = Math.min(actualWidth, container.getWidth()
+ - container.getPaddingLeft() - container.getPaddingRight());
+ }
+ viewParent = viewParent.getParent();
+ }
+
+ final int startX = Math.max(zeroScrollXInSurface, Math.min(
mCenterZoomCoords.x - mBitmap.getWidth() / 2,
- mView.getWidth() - mBitmap.getWidth()));
+ zeroScrollXInSurface + actualWidth - mBitmap.getWidth()));
final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
if (xPosInView != mPrevPosInView.x || yPosInView != mPrevPosInView.y) {
@@ -169,10 +199,9 @@ public final class Magnifier {
posX = xPosInView;
posY = yPosInView;
} else {
- final int[] coordinatesInSurface = new int[2];
- mView.getLocationInSurface(coordinatesInSurface);
- posX = xPosInView + coordinatesInSurface[0];
- posY = yPosInView + coordinatesInSurface[1];
+ mView.getLocationInSurface(mViewCoordinatesInSurface);
+ posX = xPosInView + mViewCoordinatesInSurface[0];
+ posY = yPosInView + mViewCoordinatesInSurface[1];
}
mCenterZoomCoords.x = Math.round(posX);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4170bd112450..dac6c0269539 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,8 +80,8 @@ import android.text.GraphicsOperations;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
+import android.text.MeasuredText;
import android.text.ParcelableSpan;
-import android.text.PremeasuredText;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
@@ -3887,6 +3887,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* @param elegant set the paint's elegant metrics flag.
*
+ * @see #isElegantTextHeight()
* @see Paint#isElegantTextHeight()
*
* @attr ref android.R.styleable#TextView_elegantTextHeight
@@ -5546,7 +5547,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (imm != null) imm.restartInput(this);
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
- } else if (!(text instanceof PremeasuredText || text instanceof CharWrapper)) {
+ } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
@@ -9278,8 +9279,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
*
- * Checks whether the transformation method applied to this TextView is set to ALL CAPS. This
- * settings is internally ignored if this field is editable or selectable.
+ * Checks whether the transformation method applied to this TextView is set to ALL CAPS.
* @return Whether the current transformation method is for ALL CAPS.
*
* @see #setAllCaps(boolean)
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
new file mode 100644
index 000000000000..310a7bbdcabf
--- /dev/null
+++ b/core/java/android/widget/VideoView2.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.media.AudioAttributes;
+import android.media.MediaPlayer;
+import android.media.update.ApiLoader;
+import android.media.update.VideoView2Provider;
+import android.media.update.ViewProvider;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+
+/**
+ * TODO PUBLIC API
+ * @hide
+ */
+public class VideoView2 extends FrameLayout {
+ @IntDef({
+ VIEW_TYPE_TEXTUREVIEW,
+ VIEW_TYPE_SURFACEVIEW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ViewType {}
+ public static final int VIEW_TYPE_SURFACEVIEW = 1;
+ public static final int VIEW_TYPE_TEXTUREVIEW = 2;
+
+ private final VideoView2Provider mProvider;
+
+ /**
+ * @hide
+ */
+ public VideoView2(@NonNull Context context) {
+ this(context, null);
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2(
+ @NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mProvider = ApiLoader.getProvider(context).createVideoView2(this, new SuperProvider());
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2Provider getProvider() {
+ return mProvider;
+ }
+
+ /**
+ * @hide
+ */
+ public void start() {
+ mProvider.start_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void pause() {
+ mProvider.pause_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getDuration() {
+ return mProvider.getDuration_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getCurrentPosition() {
+ return mProvider.getCurrentPosition_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void seekTo(int msec) {
+ mProvider.seekTo_impl(msec);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isPlaying() {
+ return mProvider.isPlaying_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getBufferPercentage() {
+ return mProvider.getBufferPercentage_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getAudioSessionId() {
+ return mProvider.getAudioSessionId_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void showSubtitle() {
+ mProvider.showSubtitle_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void hideSubtitle() {
+ mProvider.hideSubtitle_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void setAudioFocusRequest(int focusGain) {
+ mProvider.setAudioFocusRequest_impl(focusGain);
+ }
+
+ /**
+ * @hide
+ */
+ public void setAudioAttributes(@NonNull AudioAttributes attributes) {
+ mProvider.setAudioAttributes_impl(attributes);
+ }
+
+ /**
+ * @hide
+ */
+ public void setVideoPath(String path) {
+ mProvider.setVideoPath_impl(path);
+ }
+
+ /**
+ * @hide
+ */
+ public void setVideoURI(Uri uri) {
+ mProvider.setVideoURI_impl(uri);
+ }
+
+ /**
+ * @hide
+ */
+ public void setVideoURI(Uri uri, Map<String, String> headers) {
+ mProvider.setVideoURI_impl(uri, headers);
+ }
+
+ /**
+ * @hide
+ */
+ public void setMediaController2(MediaController2 controllerView) {
+ mProvider.setMediaController2_impl(controllerView);
+ }
+
+ /**
+ * @hide
+ */
+ public void setViewType(@ViewType int viewType) {
+ mProvider.setViewType_impl(viewType);
+ }
+
+ /**
+ * @hide
+ */
+ @ViewType
+ public int getViewType() {
+ return mProvider.getViewType_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void stopPlayback() {
+ mProvider.stopPlayback_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) {
+ mProvider.setOnPreparedListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnCompletionListener(MediaPlayer.OnCompletionListener l) {
+ mProvider.setOnCompletionListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnErrorListener(MediaPlayer.OnErrorListener l) {
+ mProvider.setOnErrorListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnInfoListener(MediaPlayer.OnInfoListener l) {
+ mProvider.setOnInfoListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnViewTypeChangedListener(OnViewTypeChangedListener l) {
+ mProvider.setOnViewTypeChangedListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public interface OnViewTypeChangedListener {
+ /**
+ * @hide
+ */
+ void onViewTypeChanged(@ViewType int viewType);
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return mProvider.getAccessibilityClassName_impl();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mProvider.onTouchEvent_impl(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ return mProvider.onTrackballEvent_impl(ev);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return mProvider.onKeyDown_impl(keyCode, event);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ mProvider.onFinishInflate_impl();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return mProvider.dispatchKeyEvent_impl(event);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mProvider.setEnabled_impl(enabled);
+ }
+
+ private class SuperProvider implements ViewProvider {
+ @Override
+ public void onAttachedToWindow_impl() {
+ VideoView2.super.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ VideoView2.super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ VideoView2.super.onLayout(changed, left, top, right, bottom);
+ }
+
+ @Override
+ public void draw_impl(Canvas canvas) {
+ VideoView2.super.draw(canvas);
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return VideoView2.super.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return VideoView2.super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return VideoView2.super.onTrackballEvent(ev);
+ }
+
+ @Override
+ public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
+ return VideoView2.super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ VideoView2.super.onFinishInflate();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent_impl(KeyEvent event) {
+ return VideoView2.super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ VideoView2.super.setEnabled(enabled);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 439e5df7d352..ac5dbc4e175a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4062,8 +4062,6 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag) {
- final int[] uids = new int[1];
- final String[] tags = new String[1];
if (workSource != null) {
for (int i = 0; i < workSource.size(); ++i) {
uid = workSource.get(i);
@@ -4074,9 +4072,8 @@ public class BatteryStatsImpl extends BatteryStats {
workSourceName != null ? workSourceName : packageName);
pkg.noteWakeupAlarmLocked(tag);
}
- uids[0] = workSource.get(i);
- tags[0] = workSource.getName(i);
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uids, tags, tag);
+ StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, workSource.get(i),
+ workSource.getName(i), tag);
}
ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4097,9 +4094,7 @@ public class BatteryStatsImpl extends BatteryStats {
BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName);
pkg.noteWakeupAlarmLocked(tag);
}
- uids[0] = uid;
- tags[0] = null;
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uids, tags, tag);
+ StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, uid, null, tag);
}
}
@@ -4224,9 +4219,8 @@ public class BatteryStatsImpl extends BatteryStats {
StatsLog.write(
StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1);
} else {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, type, name, 1);
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
+ 1);
}
}
}
@@ -4268,9 +4262,8 @@ public class BatteryStatsImpl extends BatteryStats {
StatsLog.write(
StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0);
} else {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, type, name, 0);
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
+ 0);
}
}
}
@@ -4364,10 +4357,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uids, tags, name, historyName, 1);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ uid, null, name, historyName, 1);
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4376,15 +4367,11 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
WorkSource workSource) {
final int N = workSource.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
- uids[0] = workSource.get(i);
- tags[0] = workSource.getName(i);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uids, tags, name,
- historyName, 1);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource.get(i), workSource.getName(i), name, historyName, 1);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4415,10 +4402,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
- int[] uids = new int[] { uid };
- String[] tags = new String[] { null };
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uids, tags, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ uid, null, name, historyName, 0);
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4427,15 +4412,11 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
WorkSource workSource) {
final int N = workSource.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
- uids[0] = workSource.get(i);
- tags[0] = workSource.getName(i);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uids, tags, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource.get(i), workSource.getName(i), name, historyName, 0);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -5420,11 +5401,10 @@ public class BatteryStatsImpl extends BatteryStats {
workChain.getUids(), workChain.getTags(), 1);
}
} else {
- final int[] uids = new int[] {uid};
- final String[] tags = new String[] {null};
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
+ 1);
}
}
@@ -5470,11 +5450,10 @@ public class BatteryStatsImpl extends BatteryStats {
workChain.getUids(), workChain.getTags(), 0);
}
} else {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] {null};
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
+ 0);
}
}
@@ -5547,14 +5526,11 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults) {
final int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i = 0; i < N; i++) {
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, uids, tags, numNewResults);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i),
+ numNewResults);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5879,14 +5855,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5903,14 +5875,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5927,14 +5895,11 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiScanStartedFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
+ 1);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5951,14 +5916,11 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
+ 0);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6934,18 +6896,14 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
}
}
}
@@ -6953,9 +6911,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -6969,18 +6925,15 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
+ null, 0);
}
}
}
@@ -6988,9 +6941,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetVideoLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+ 0);
}
}
@@ -7004,18 +6956,15 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ 0);
}
}
}
@@ -7023,9 +6972,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -7039,18 +6986,14 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
}
}
}
@@ -7058,9 +7001,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -9622,9 +9563,7 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SYNC_STATE_CHANGED, uids, tags, name, 1);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
}
}
@@ -9633,9 +9572,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SYNC_STATE_CHANGED, uids, tags, name, 0);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
}
}
}
@@ -9644,9 +9581,8 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = mJobStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uids, tags, name, 1);
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
+ name, 1);
}
}
@@ -9655,9 +9591,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uids, tags, name, 0);
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
+ name, 0);
}
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9768,12 +9703,11 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
t.startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
if (sensor == Sensor.GPS) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null, 1);
} else {
- StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, uids, tags, sensor, 1);
+ StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
+ 1);
}
}
@@ -9783,13 +9717,12 @@ public class BatteryStatsImpl extends BatteryStats {
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
if (sensor == Sensor.GPS) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null,
+ 0);
} else {
- StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, uids, tags, sensor, 0);
+ StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
+ sensor, 0);
}
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c5fe4cb0177b..f814ba9e484d 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -572,10 +572,12 @@ public class ZygoteInit {
final String seInfo = null;
final String classLoaderContext =
getSystemServerClassLoaderContext(classPathForElement);
+ final int targetSdkVersion = 0; // SystemServer targets the system's SDK version
try {
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
- uuid, classLoaderContext, seInfo, false /* downgrade */);
+ uuid, classLoaderContext, seInfo, false /* downgrade */,
+ targetSdkVersion);
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/policy/KeyguardDismissCallback.java b/core/java/com/android/internal/policy/KeyguardDismissCallback.java
new file mode 100644
index 000000000000..38337ec6f274
--- /dev/null
+++ b/core/java/com/android/internal/policy/KeyguardDismissCallback.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.os.RemoteException;
+import com.android.internal.policy.IKeyguardDismissCallback;
+
+/**
+ * @hide
+ */
+public class KeyguardDismissCallback extends IKeyguardDismissCallback.Stub {
+
+ @Override
+ public void onDismissError() throws RemoteException {
+ // To be overidden
+ }
+
+ @Override
+ public void onDismissSucceeded() throws RemoteException {
+ // To be overidden
+ }
+
+ @Override
+ public void onDismissCancelled() throws RemoteException {
+ // To be overidden
+ }
+}
diff --git a/core/java/com/android/internal/print/DualDumpOutputStream.java b/core/java/com/android/internal/print/DualDumpOutputStream.java
new file mode 100644
index 000000000000..4b10ef2facbb
--- /dev/null
+++ b/core/java/com/android/internal/print/DualDumpOutputStream.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.print;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+
+/**
+ * Dump either to a proto or a print writer using the same interface.
+ *
+ * <p>This mirrors the interface of {@link ProtoOutputStream}.
+ */
+public class DualDumpOutputStream {
+ private static final String LOG_TAG = DualDumpOutputStream.class.getSimpleName();
+
+ // When writing to a proto, the proto
+ private final @Nullable ProtoOutputStream mProtoStream;
+
+ // When printing in clear text, the writer
+ private final @Nullable IndentingPrintWriter mIpw;
+ // Temporary storage of data when printing to mIpw
+ private final LinkedList<DumpObject> mDumpObjects = new LinkedList<>();
+
+ private static abstract class Dumpable {
+ final String name;
+
+ private Dumpable(String name) {
+ this.name = name;
+ }
+
+ abstract void print(IndentingPrintWriter ipw, boolean printName);
+ }
+
+ private static class DumpObject extends Dumpable {
+ private final LinkedHashMap<String, ArrayList<Dumpable>> mSubObjects = new LinkedHashMap<>();
+
+ private DumpObject(String name) {
+ super(name);
+ }
+
+ @Override
+ void print(IndentingPrintWriter ipw, boolean printName) {
+ if (printName) {
+ ipw.println(name + "={");
+ } else {
+ ipw.println("{");
+ }
+ ipw.increaseIndent();
+
+ for (ArrayList<Dumpable> subObject: mSubObjects.values()) {
+ int numDumpables = subObject.size();
+
+ if (numDumpables == 1) {
+ subObject.get(0).print(ipw, true);
+ } else {
+ ipw.println(subObject.get(0).name + "=[");
+ ipw.increaseIndent();
+
+ for (int i = 0; i < numDumpables; i++) {
+ subObject.get(i).print(ipw, false);
+ }
+
+ ipw.decreaseIndent();
+ ipw.println("]");
+ }
+ }
+
+ ipw.decreaseIndent();
+ ipw.println("}");
+ }
+
+ /**
+ * Add new field / subobject to this object.
+ *
+ * <p>If a name is added twice, they will be printed as a array
+ *
+ * @param fieldName name of the field added
+ * @param d The dumpable to add
+ */
+ public void add(String fieldName, Dumpable d) {
+ ArrayList<Dumpable> l = mSubObjects.get(fieldName);
+
+ if (l == null) {
+ l = new ArrayList<>(1);
+ mSubObjects.put(fieldName, l);
+ }
+
+ l.add(d);
+ }
+ }
+
+ private static class DumpField extends Dumpable {
+ private final String mValue;
+
+ private DumpField(String name, String value) {
+ super(name);
+ this.mValue = value;
+ }
+
+ @Override
+ void print(IndentingPrintWriter ipw, boolean printName) {
+ if (printName) {
+ ipw.println(name + "=" + mValue);
+ } else {
+ ipw.println(mValue);
+ }
+ }
+ }
+
+
+ /**
+ * Create a new DualDumpOutputStream. Only one output should be set.
+ *
+ * @param proto If dumping to proto the {@link ProtoOutputStream}
+ * @param ipw If dumping to a print writer, the {@link IndentingPrintWriter}
+ */
+ public DualDumpOutputStream(@Nullable ProtoOutputStream proto,
+ @Nullable IndentingPrintWriter ipw) {
+ if ((proto == null) == (ipw == null)) {
+ Log.e(LOG_TAG, "Cannot dump to clear text and proto at once. Ignoring proto");
+ proto = null;
+ }
+
+ mProtoStream = proto;
+ mIpw = ipw;
+
+ if (!isProto()) {
+ // Add root object
+ mDumpObjects.add(new DumpObject(null));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, double val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, boolean val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, int val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, float val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, byte[] val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, Arrays.toString(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, long val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, @Nullable String val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public long start(@NonNull String fieldName, long fieldId) {
+ if (mProtoStream != null) {
+ return mProtoStream.start(fieldId);
+ } else {
+ DumpObject d = new DumpObject(fieldName);
+ mDumpObjects.getLast().add(fieldName, d);
+ mDumpObjects.addLast(d);
+ return System.identityHashCode(d);
+ }
+ }
+
+ public void end(long token) {
+ if (mProtoStream != null) {
+ mProtoStream.end(token);
+ } else {
+ if (System.identityHashCode(mDumpObjects.getLast()) != token) {
+ Log.w(LOG_TAG, "Unexpected token for ending " + mDumpObjects.getLast().name
+ + " at " + Arrays.toString(Thread.currentThread().getStackTrace()));
+ }
+ mDumpObjects.removeLast();
+ }
+ }
+
+ public void flush() {
+ if (mProtoStream != null) {
+ mProtoStream.flush();
+ } else {
+ if (mDumpObjects.size() == 1) {
+ mDumpObjects.getFirst().print(mIpw, false);
+
+ // Reset root object
+ mDumpObjects.clear();
+ mDumpObjects.add(new DumpObject(null));
+ }
+
+ mIpw.flush();
+ }
+ }
+
+ /**
+ * Add a dump from a different service into this dump.
+ *
+ * <p>Only for clear text dump. For proto dump use {@link #write(String, long, byte[])}.
+ *
+ * @param fieldName The name of the field
+ * @param nestedState The state of the dump
+ */
+ public void writeNested(@NonNull String fieldName, byte[] nestedState) {
+ if (mIpw == null) {
+ Log.w(LOG_TAG, "writeNested does not work for proto logging");
+ return;
+ }
+
+ mDumpObjects.getLast().add(fieldName,
+ new DumpField(fieldName, (new String(nestedState, StandardCharsets.UTF_8)).trim()));
+ }
+
+ /**
+ * @return {@code true} iff we are dumping to a proto
+ */
+ public boolean isProto() {
+ return mProtoStream != null;
+ }
+}
diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java
index 28c7fc2182b2..3192d5cbbd1d 100644
--- a/core/java/com/android/internal/print/DumpUtils.java
+++ b/core/java/com/android/internal/print/DumpUtils.java
@@ -39,7 +39,6 @@ import android.service.print.PrinterCapabilitiesProto;
import android.service.print.PrinterIdProto;
import android.service.print.PrinterInfoProto;
import android.service.print.ResolutionProto;
-import android.util.proto.ProtoOutputStream;
/**
* Utilities for dumping print related proto buffer
@@ -49,13 +48,14 @@ public class DumpUtils {
* Write a string to a proto if the string is not {@code null}.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the string
* @param string The string to write
*/
- public static void writeStringIfNotNull(@NonNull ProtoOutputStream proto, long id,
- @Nullable String string) {
+ public static void writeStringIfNotNull(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @Nullable String string) {
if (string != null) {
- proto.write(id, string);
+ proto.write(idName, id, string);
}
}
@@ -63,14 +63,15 @@ public class DumpUtils {
* Write a {@link ComponentName} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param component The component name to write
*/
- public static void writeComponentName(@NonNull ProtoOutputStream proto, long id,
- @NonNull ComponentName component) {
- long token = proto.start(id);
- proto.write(ComponentNameProto.PACKAGE_NAME, component.getPackageName());
- proto.write(ComponentNameProto.CLASS_NAME, component.getClassName());
+ public static void writeComponentName(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @NonNull ComponentName component) {
+ long token = proto.start(idName, id);
+ proto.write("package_name", ComponentNameProto.PACKAGE_NAME, component.getPackageName());
+ proto.write("class_name", ComponentNameProto.CLASS_NAME, component.getClassName());
proto.end(token);
}
@@ -78,14 +79,16 @@ public class DumpUtils {
* Write a {@link PrinterId} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param printerId The printer id to write
*/
- public static void writePrinterId(@NonNull ProtoOutputStream proto, long id,
+ public static void writePrinterId(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrinterId printerId) {
- long token = proto.start(id);
- writeComponentName(proto, PrinterIdProto.SERVICE_NAME, printerId.getServiceName());
- proto.write(PrinterIdProto.LOCAL_ID, printerId.getLocalId());
+ long token = proto.start(idName, id);
+ writeComponentName(proto, "service_name", PrinterIdProto.SERVICE_NAME,
+ printerId.getServiceName());
+ proto.write("local_id", PrinterIdProto.LOCAL_ID, printerId.getLocalId());
proto.end(token);
}
@@ -93,71 +96,76 @@ public class DumpUtils {
* Write a {@link PrinterCapabilitiesInfo} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param cap The capabilities to write
*/
public static void writePrinterCapabilities(@NonNull Context context,
- @NonNull ProtoOutputStream proto, long id, @NonNull PrinterCapabilitiesInfo cap) {
- long token = proto.start(id);
- writeMargins(proto, PrinterCapabilitiesProto.MIN_MARGINS, cap.getMinMargins());
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrinterCapabilitiesInfo cap) {
+ long token = proto.start(idName, id);
+ writeMargins(proto, "min_margins", PrinterCapabilitiesProto.MIN_MARGINS,
+ cap.getMinMargins());
int numMediaSizes = cap.getMediaSizes().size();
for (int i = 0; i < numMediaSizes; i++) {
- writeMediaSize(context, proto, PrinterCapabilitiesProto.MEDIA_SIZES,
+ writeMediaSize(context, proto, "media_sizes", PrinterCapabilitiesProto.MEDIA_SIZES,
cap.getMediaSizes().get(i));
}
int numResolutions = cap.getResolutions().size();
for (int i = 0; i < numResolutions; i++) {
- writeResolution(proto, PrinterCapabilitiesProto.RESOLUTIONS,
+ writeResolution(proto, "resolutions", PrinterCapabilitiesProto.RESOLUTIONS,
cap.getResolutions().get(i));
}
if ((cap.getColorModes() & PrintAttributes.COLOR_MODE_MONOCHROME) != 0) {
- proto.write(PrinterCapabilitiesProto.COLOR_MODES,
+ proto.write("color_modes", PrinterCapabilitiesProto.COLOR_MODES,
PrintAttributesProto.COLOR_MODE_MONOCHROME);
}
if ((cap.getColorModes() & PrintAttributes.COLOR_MODE_COLOR) != 0) {
- proto.write(PrinterCapabilitiesProto.COLOR_MODES,
+ proto.write("color_modes", PrinterCapabilitiesProto.COLOR_MODES,
PrintAttributesProto.COLOR_MODE_COLOR);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_NONE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_NONE);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_LONG_EDGE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_LONG_EDGE);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_SHORT_EDGE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_SHORT_EDGE);
}
proto.end(token);
}
-
/**
* Write a {@link PrinterInfo} to a proto.
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param info The printer info to write
*/
- public static void writePrinterInfo(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrinterInfo info) {
- long token = proto.start(id);
- writePrinterId(proto, PrinterInfoProto.ID, info.getId());
- proto.write(PrinterInfoProto.NAME, info.getName());
- proto.write(PrinterInfoProto.STATUS, info.getStatus());
- proto.write(PrinterInfoProto.DESCRIPTION, info.getDescription());
+ public static void writePrinterInfo(@NonNull Context context,
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrinterInfo info) {
+ long token = proto.start(idName, id);
+ writePrinterId(proto, "id", PrinterInfoProto.ID, info.getId());
+ proto.write("name", PrinterInfoProto.NAME, info.getName());
+ proto.write("status", PrinterInfoProto.STATUS, info.getStatus());
+ proto.write("description", PrinterInfoProto.DESCRIPTION, info.getDescription());
PrinterCapabilitiesInfo cap = info.getCapabilities();
if (cap != null) {
- writePrinterCapabilities(context, proto, PrinterInfoProto.CAPABILITIES, cap);
+ writePrinterCapabilities(context, proto, "capabilities", PrinterInfoProto.CAPABILITIES,
+ cap);
}
proto.end(token);
@@ -168,16 +176,17 @@ public class DumpUtils {
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param mediaSize The media size to write
*/
- public static void writeMediaSize(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrintAttributes.MediaSize mediaSize) {
- long token = proto.start(id);
- proto.write(MediaSizeProto.ID, mediaSize.getId());
- proto.write(MediaSizeProto.LABEL, mediaSize.getLabel(context.getPackageManager()));
- proto.write(MediaSizeProto.HEIGHT_MILS, mediaSize.getHeightMils());
- proto.write(MediaSizeProto.WIDTH_MILS, mediaSize.getWidthMils());
+ public static void writeMediaSize(@NonNull Context context, @NonNull DualDumpOutputStream proto,
+ String idName, long id, @NonNull PrintAttributes.MediaSize mediaSize) {
+ long token = proto.start(idName, id);
+ proto.write("id", MediaSizeProto.ID, mediaSize.getId());
+ proto.write("label", MediaSizeProto.LABEL, mediaSize.getLabel(context.getPackageManager()));
+ proto.write("height_mils", MediaSizeProto.HEIGHT_MILS, mediaSize.getHeightMils());
+ proto.write("width_mils", MediaSizeProto.WIDTH_MILS, mediaSize.getWidthMils());
proto.end(token);
}
@@ -185,16 +194,17 @@ public class DumpUtils {
* Write a {@link PrintAttributes.Resolution} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param res The resolution to write
*/
- public static void writeResolution(@NonNull ProtoOutputStream proto, long id,
+ public static void writeResolution(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrintAttributes.Resolution res) {
- long token = proto.start(id);
- proto.write(ResolutionProto.ID, res.getId());
- proto.write(ResolutionProto.LABEL, res.getLabel());
- proto.write(ResolutionProto.HORIZONTAL_DPI, res.getHorizontalDpi());
- proto.write(ResolutionProto.VERTICAL_DPI, res.getVerticalDpi());
+ long token = proto.start(idName, id);
+ proto.write("id", ResolutionProto.ID, res.getId());
+ proto.write("label", ResolutionProto.LABEL, res.getLabel());
+ proto.write("horizontal_DPI", ResolutionProto.HORIZONTAL_DPI, res.getHorizontalDpi());
+ proto.write("veritical_DPI", ResolutionProto.VERTICAL_DPI, res.getVerticalDpi());
proto.end(token);
}
@@ -202,16 +212,17 @@ public class DumpUtils {
* Write a {@link PrintAttributes.Margins} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param margins The margins to write
*/
- public static void writeMargins(@NonNull ProtoOutputStream proto, long id,
+ public static void writeMargins(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrintAttributes.Margins margins) {
- long token = proto.start(id);
- proto.write(MarginsProto.TOP_MILS, margins.getTopMils());
- proto.write(MarginsProto.LEFT_MILS, margins.getLeftMils());
- proto.write(MarginsProto.RIGHT_MILS, margins.getRightMils());
- proto.write(MarginsProto.BOTTOM_MILS, margins.getBottomMils());
+ long token = proto.start(idName, id);
+ proto.write("top_mils", MarginsProto.TOP_MILS, margins.getTopMils());
+ proto.write("left_mils", MarginsProto.LEFT_MILS, margins.getLeftMils());
+ proto.write("right_mils", MarginsProto.RIGHT_MILS, margins.getRightMils());
+ proto.write("bottom_mils", MarginsProto.BOTTOM_MILS, margins.getBottomMils());
proto.end(token);
}
@@ -220,32 +231,34 @@ public class DumpUtils {
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param attributes The attributes to write
*/
public static void writePrintAttributes(@NonNull Context context,
- @NonNull ProtoOutputStream proto, long id, @NonNull PrintAttributes attributes) {
- long token = proto.start(id);
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrintAttributes attributes) {
+ long token = proto.start(idName, id);
PrintAttributes.MediaSize mediaSize = attributes.getMediaSize();
if (mediaSize != null) {
- writeMediaSize(context, proto, PrintAttributesProto.MEDIA_SIZE, mediaSize);
+ writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize);
}
- proto.write(PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
+ proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
PrintAttributes.Resolution res = attributes.getResolution();
if (res != null) {
- writeResolution(proto, PrintAttributesProto.RESOLUTION, res);
+ writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res);
}
PrintAttributes.Margins minMargins = attributes.getMinMargins();
if (minMargins != null) {
- writeMargins(proto, PrintAttributesProto.MIN_MARGINS, minMargins);
+ writeMargins(proto, "min_margings", PrintAttributesProto.MIN_MARGINS, minMargins);
}
- proto.write(PrintAttributesProto.COLOR_MODE, attributes.getColorMode());
- proto.write(PrintAttributesProto.DUPLEX_MODE, attributes.getDuplexMode());
+ proto.write("color_mode", PrintAttributesProto.COLOR_MODE, attributes.getColorMode());
+ proto.write("duplex_mode", PrintAttributesProto.DUPLEX_MODE, attributes.getDuplexMode());
proto.end(token);
}
@@ -253,21 +266,22 @@ public class DumpUtils {
* Write a {@link PrintDocumentInfo} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param info The info to write
*/
- public static void writePrintDocumentInfo(@NonNull ProtoOutputStream proto, long id,
- @NonNull PrintDocumentInfo info) {
- long token = proto.start(id);
- proto.write(PrintDocumentInfoProto.NAME, info.getName());
+ public static void writePrintDocumentInfo(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @NonNull PrintDocumentInfo info) {
+ long token = proto.start(idName, id);
+ proto.write("name", PrintDocumentInfoProto.NAME, info.getName());
int pageCount = info.getPageCount();
if (pageCount != PrintDocumentInfo.PAGE_COUNT_UNKNOWN) {
- proto.write(PrintDocumentInfoProto.PAGE_COUNT, pageCount);
+ proto.write("page_count", PrintDocumentInfoProto.PAGE_COUNT, pageCount);
}
- proto.write(PrintDocumentInfoProto.CONTENT_TYPE, info.getContentType());
- proto.write(PrintDocumentInfoProto.DATA_SIZE, info.getDataSize());
+ proto.write("content_type", PrintDocumentInfoProto.CONTENT_TYPE, info.getContentType());
+ proto.write("data_size", PrintDocumentInfoProto.DATA_SIZE, info.getDataSize());
proto.end(token);
}
@@ -275,14 +289,15 @@ public class DumpUtils {
* Write a {@link PageRange} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param range The range to write
*/
- public static void writePageRange(@NonNull ProtoOutputStream proto, long id,
+ public static void writePageRange(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PageRange range) {
- long token = proto.start(id);
- proto.write(PageRangeProto.START, range.getStart());
- proto.write(PageRangeProto.END, range.getEnd());
+ long token = proto.start(idName, id);
+ proto.write("start", PageRangeProto.START, range.getStart());
+ proto.write("end", PageRangeProto.END, range.getEnd());
proto.end(token);
}
@@ -291,64 +306,70 @@ public class DumpUtils {
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param printJobInfo The print job info to write
*/
- public static void writePrintJobInfo(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrintJobInfo printJobInfo) {
- long token = proto.start(id);
- proto.write(PrintJobInfoProto.LABEL, printJobInfo.getLabel());
+ public static void writePrintJobInfo(@NonNull Context context,
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrintJobInfo printJobInfo) {
+ long token = proto.start(idName, id);
+ proto.write("label", PrintJobInfoProto.LABEL, printJobInfo.getLabel());
PrintJobId printJobId = printJobInfo.getId();
if (printJobId != null) {
- proto.write(PrintJobInfoProto.PRINT_JOB_ID, printJobId.flattenToString());
+ proto.write("print_job_id", PrintJobInfoProto.PRINT_JOB_ID,
+ printJobId.flattenToString());
}
int state = printJobInfo.getState();
if (state >= PrintJobInfoProto.STATE_CREATED && state <= PrintJobInfoProto.STATE_CANCELED) {
- proto.write(PrintJobInfoProto.STATE, state);
+ proto.write("state", PrintJobInfoProto.STATE, state);
} else {
- proto.write(PrintJobInfoProto.STATE, PrintJobInfoProto.STATE_UNKNOWN);
+ proto.write("state", PrintJobInfoProto.STATE, PrintJobInfoProto.STATE_UNKNOWN);
}
PrinterId printer = printJobInfo.getPrinterId();
if (printer != null) {
- writePrinterId(proto, PrintJobInfoProto.PRINTER, printer);
+ writePrinterId(proto, "printer", PrintJobInfoProto.PRINTER, printer);
}
String tag = printJobInfo.getTag();
if (tag != null) {
- proto.write(PrintJobInfoProto.TAG, tag);
+ proto.write("tag", PrintJobInfoProto.TAG, tag);
}
- proto.write(PrintJobInfoProto.CREATION_TIME, printJobInfo.getCreationTime());
+ proto.write("creation_time", PrintJobInfoProto.CREATION_TIME,
+ printJobInfo.getCreationTime());
PrintAttributes attributes = printJobInfo.getAttributes();
if (attributes != null) {
- writePrintAttributes(context, proto, PrintJobInfoProto.ATTRIBUTES, attributes);
+ writePrintAttributes(context, proto, "attributes", PrintJobInfoProto.ATTRIBUTES,
+ attributes);
}
PrintDocumentInfo docInfo = printJobInfo.getDocumentInfo();
if (docInfo != null) {
- writePrintDocumentInfo(proto, PrintJobInfoProto.DOCUMENT_INFO, docInfo);
+ writePrintDocumentInfo(proto, "document_info", PrintJobInfoProto.DOCUMENT_INFO,
+ docInfo);
}
- proto.write(PrintJobInfoProto.IS_CANCELING, printJobInfo.isCancelling());
+ proto.write("is_canceling", PrintJobInfoProto.IS_CANCELING, printJobInfo.isCancelling());
PageRange[] pages = printJobInfo.getPages();
if (pages != null) {
for (int i = 0; i < pages.length; i++) {
- writePageRange(proto, PrintJobInfoProto.PAGES, pages[i]);
+ writePageRange(proto, "pages", PrintJobInfoProto.PAGES, pages[i]);
}
}
- proto.write(PrintJobInfoProto.HAS_ADVANCED_OPTIONS,
+ proto.write("has_advanced_options", PrintJobInfoProto.HAS_ADVANCED_OPTIONS,
printJobInfo.getAdvancedOptions() != null);
- proto.write(PrintJobInfoProto.PROGRESS, printJobInfo.getProgress());
+ proto.write("progress", PrintJobInfoProto.PROGRESS, printJobInfo.getProgress());
CharSequence status = printJobInfo.getStatus(context.getPackageManager());
if (status != null) {
- proto.write(PrintJobInfoProto.STATUS, status.toString());
+ proto.write("status", PrintJobInfoProto.STATUS, status.toString());
}
proto.end(token);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 31d22e0f92fd..b2bab6f7d58f 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -19,9 +19,9 @@ package com.android.internal.widget;
import android.app.PendingIntent;
import android.app.trust.IStrongAuthTracker;
import android.os.Bundle;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
+import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.KeychainProtectionParameter;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -64,7 +64,7 @@ interface ILockSettings {
// {@code ServiceSpecificException} may be thrown to signal an error, which caller can
// convert to {@code RecoveryManagerException}.
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
- RecoveryData getRecoveryData(in byte[] account);
+ KeychainSnapshot getRecoveryData(in byte[] account);
byte[] generateAndStoreKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
@@ -75,10 +75,10 @@ interface ILockSettings {
void setRecoverySecretTypes(in int[] secretTypes);
int[] getRecoverySecretTypes();
int[] getPendingRecoverySecretTypes();
- void recoverySecretAvailable(in RecoveryMetadata recoverySecret);
+ void recoverySecretAvailable(in KeychainProtectionParameter recoverySecret);
byte[] startRecoverySession(in String sessionId,
in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
- in List<RecoveryMetadata> secrets);
+ in List<KeychainProtectionParameter> secrets);
Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
- in List<EntryRecoveryData> applicationKeys);
+ in List<WrappedApplicationKey> applicationKeys);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b3f66e9652f6..96f3308ec178 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -84,7 +84,7 @@ cc_library_shared {
"android_view_VelocityTracker.cpp",
"android_text_AndroidCharacter.cpp",
"android_text_Hyphenator.cpp",
- "android_text_MeasuredText.cpp",
+ "android_text_MeasuredParagraph.cpp",
"android_text_StaticLayout.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6d7fe056acdf..6569b4783e67 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -178,7 +178,7 @@ extern int register_android_net_LocalSocketImpl(JNIEnv* env);
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
-extern int register_android_text_MeasuredText(JNIEnv* env);
+extern int register_android_text_MeasuredParagraph(JNIEnv* env);
extern int register_android_text_StaticLayout(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
@@ -1342,7 +1342,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_content_XmlBlock),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
- REG_JNI(register_android_text_MeasuredText),
+ REG_JNI(register_android_text_MeasuredParagraph),
REG_JNI(register_android_text_StaticLayout),
REG_JNI(register_android_view_InputDevice),
REG_JNI(register_android_view_KeyCharacterMap),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 79aa5acac4ee..685fcaf15211 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -237,10 +237,22 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
// Create the codec.
NinePatchPeeker peeker;
- std::unique_ptr<SkAndroidCodec> codec = SkAndroidCodec::MakeFromStream(
- std::move(stream), &peeker);
- if (!codec.get()) {
- return nullObjectReturn("SkAndroidCodec::MakeFromStream returned null");
+ std::unique_ptr<SkAndroidCodec> codec;
+ {
+ SkCodec::Result result;
+ std::unique_ptr<SkCodec> c = SkCodec::MakeFromStream(std::move(stream), &result,
+ &peeker);
+ if (!c) {
+ SkString msg;
+ msg.printf("Failed to create image decoder with message '%s'",
+ SkCodec::ResultToString(result));
+ return nullObjectReturn(msg.c_str());
+ }
+
+ codec = SkAndroidCodec::MakeFromCodec(std::move(c));
+ if (!codec) {
+ return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null");
+ }
}
// Do not allow ninepatch decodes to 565. In the past, decodes to 565
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index a0a4be4590be..249202a117e1 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -77,11 +77,27 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
return nullptr;
}
std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
- decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker);
+ SkCodec::Result result;
+ auto codec = SkCodec::MakeFromStream(std::move(stream), &result, &decoder->mPeeker);
+ if (!codec) {
+ switch (result) {
+ case SkCodec::kIncompleteInput:
+ env->ThrowNew(gIncomplete_class, "Incomplete input");
+ break;
+ default:
+ SkString msg;
+ msg.printf("Failed to create image decoder with message '%s'",
+ SkCodec::ResultToString(result));
+ doThrowIOE(env, msg.c_str());
+ break;
+ }
+
+ return nullptr;
+ }
+
+ decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
if (!decoder->mCodec.get()) {
- // FIXME: (b/71578461) Use the error message from
- // SkCodec::MakeFromStream to report a more informative error message.
- doThrowIOE(env, "Failed to create an SkCodec");
+ doThrowIOE(env, "Could not create AndroidCodec");
return nullptr;
}
@@ -160,11 +176,6 @@ static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jby
return native_create(env, std::move(stream));
}
-static bool supports_any_down_scale(const SkAndroidCodec* codec) {
- return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP;
-}
-
-// This method should never return null. Instead, it should throw an exception.
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jobject jcallback, jobject jpostProcess,
jint desiredWidth, jint desiredHeight, jobject jsubset,
@@ -173,33 +184,14 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
jboolean asAlphaMask) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
SkAndroidCodec* codec = decoder->mCodec.get();
- SkImageInfo decodeInfo = codec->getInfo();
- bool scale = false;
- int sampleSize = 1;
- if (desiredWidth != decodeInfo.width() || desiredHeight != decodeInfo.height()) {
- bool match = false;
- if (desiredWidth < decodeInfo.width() && desiredHeight < decodeInfo.height()) {
- if (supports_any_down_scale(codec)) {
- match = true;
- decodeInfo = decodeInfo.makeWH(desiredWidth, desiredHeight);
- } else {
- int sampleX = decodeInfo.width() / desiredWidth;
- int sampleY = decodeInfo.height() / desiredHeight;
- sampleSize = std::min(sampleX, sampleY);
- SkISize sampledSize = codec->getSampledDimensions(sampleSize);
- decodeInfo = decodeInfo.makeWH(sampledSize.width(), sampledSize.height());
- if (decodeInfo.width() == desiredWidth && decodeInfo.height() == desiredHeight) {
- match = true;
- }
- }
- }
- if (!match) {
- scale = true;
- if (requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) {
- doThrowISE(env, "Cannot scale unpremultiplied pixels!");
- return nullptr;
- }
- }
+ const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
+ SkISize decodeSize = desiredSize;
+ const int sampleSize = codec->computeSampleSize(&decodeSize);
+ const bool scale = desiredSize != decodeSize;
+ SkImageInfo decodeInfo = codec->getInfo().makeWH(decodeSize.width(), decodeSize.height());
+ if (scale && requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) {
+ doThrowISE(env, "Cannot scale unpremultiplied pixels!");
+ return nullptr;
}
switch (decodeInfo.alphaType()) {
diff --git a/core/jni/android_text_MeasuredText.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index af9d13122201..bdae0b22987f 100644
--- a/core/jni/android_text_MeasuredText.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "MeasuredText"
+#define LOG_TAG "MeasuredParagraph"
#include "ScopedIcuLocale.h"
#include "unicode/locid.h"
@@ -49,7 +49,7 @@ static inline Paint* toPaint(jlong ptr) {
return reinterpret_cast<Paint*>(ptr);
}
-static inline minikin::MeasuredText* toMeasuredText(jlong ptr) {
+static inline minikin::MeasuredText* toMeasuredParagraph(jlong ptr) {
return reinterpret_cast<minikin::MeasuredText*>(ptr);
}
@@ -57,8 +57,8 @@ template<typename Ptr> static inline jlong toJLong(Ptr ptr) {
return reinterpret_cast<jlong>(ptr);
}
-static void releaseMeasuredText(jlong measuredTextPtr) {
- delete toMeasuredText(measuredTextPtr);
+static void releaseMeasuredParagraph(jlong measuredTextPtr) {
+ delete toMeasuredParagraph(measuredTextPtr);
}
// Regular JNI
@@ -84,7 +84,7 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong
}
// Regular JNI
-static jlong nBuildNativeMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
+static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr,
jcharArray javaText) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
@@ -100,23 +100,23 @@ static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) {
// CriticalNative
static jlong nGetReleaseFunc() {
- return toJLong(&releaseMeasuredText);
+ return toJLong(&releaseMeasuredParagraph);
}
static const JNINativeMethod gMethods[] = {
- // MeasuredTextBuilder native functions.
+ // MeasuredParagraphBuilder native functions.
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildNativeMeasuredText", "(J[C)J", (void*) nBuildNativeMeasuredText},
+ {"nBuildNativeMeasuredParagraph", "(J[C)J", (void*) nBuildNativeMeasuredParagraph},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
- // MeasuredText native functions.
+ // MeasuredParagraph native functions.
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
};
-int register_android_text_MeasuredText(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/text/MeasuredText", gMethods, NELEM(gMethods));
+int register_android_text_MeasuredParagraph(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/text/MeasuredParagraph", gMethods, NELEM(gMethods));
}
}
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index b5c23dfa873d..682dc8739869 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -174,7 +174,7 @@ static const JNINativeMethod gMethods[] = {
// Inputs
"[C" // text
- "J" // MeasuredText ptr.
+ "J" // MeasuredParagraph ptr.
"I" // length
"F" // firstWidth
"I" // firstWidthLineCount
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index d4bdb9b577dc..3ffb254b2333 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -389,8 +389,9 @@ message GlobalSettingsProto {
optional SettingProto notification_snooze_options = 341;
optional SettingProto enable_gnss_raw_meas_full_tracking = 346;
optional SettingProto zram_enabled = 347;
+ optional SettingProto enable_smart_replies_in_notifications = 348;
- // Next tag = 348;
+ // Next tag = 349;
}
message SecureSettingsProto {
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 8753bf768321..c9f7d52ae83f 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -41,4 +41,13 @@ message ForceAppStandbyTrackerProto {
// Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
+
+ // Whether device is a small battery device
+ optional bool is_small_battery_device = 6;
+
+ // Whether force app standby for small battery device setting is enabled
+ optional bool force_all_apps_standby_for_small_battery = 7;
+
+ // Whether device is charging
+ optional bool is_charging = 8;
}
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index b81cd1f50f91..f079e1e1b5e0 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -58,7 +58,6 @@ message WindowLayoutParamsProto {
optional NeedsMenuState needs_menu_key = 22;
optional .android.view.DisplayProto.ColorMode color_mode = 23;
optional uint32 flags = 24;
- optional uint64 flags_extra = 25;
optional uint32 private_flags = 26;
optional uint32 system_ui_visibility_flags = 27;
optional uint32 subtree_system_ui_visibility_flags = 28;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a3e8f1ebd3ff..d2a22d0794b6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3692,6 +3692,12 @@
<permission android:name="android.permission.MODIFY_QUIET_MODE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to control remote animations. See
+ {@link ActivityOptions#makeRemoteAnimation}
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
index 528efca49fc3..a88836eff5a0 100644
--- a/core/res/res/layout/autofill_dataset_picker.xml
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -24,6 +24,8 @@
android:id="@+id/autofill_dataset_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
+ android:drawSelectorOnTop="true"
+ android:clickable="true"
android:divider="@null"
android:visibility="gone">
</ListView>
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 353a1a57fda5..445b19b5d01c 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -39,6 +39,10 @@
android:layout_height="@dimen/notification_progress_bar_height"
android:layout_marginTop="@dimen/notification_progress_margin_top"
layout="@layout/notification_template_progress" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_template_right_icon" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 6b1049a0cb82..d47bff6403ec 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -56,6 +56,12 @@
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index e94e646fa581..76c0a676b6f3 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -64,6 +64,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 3c87f92639bb..ac4c052d6141 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -67,6 +67,12 @@
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
<include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index e4c91a458220..718cf16a1fe9 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -119,6 +119,12 @@
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
<include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index a72ad538a85c..34f5ae8133be 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -47,6 +47,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spacing="@dimen/notification_messaging_spacing" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin_bottom" />
</LinearLayout>
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
diff --git a/core/res/res/layout/notification_template_smart_reply_container.xml b/core/res/res/layout/notification_template_smart_reply_container.xml
new file mode 100644
index 000000000000..637241eba982
--- /dev/null
+++ b/core/res/res/layout/notification_template_smart_reply_container.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/smart_reply_container"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <!-- SmartReplyView will be added here. -->
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 04131009b141..f8a77f8f1c32 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -77,9 +77,9 @@
<item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
<item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item>
- <item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item>
- <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
- <item name="highlight_alpha_material_colored" format="float" type="dimen">0.26</item>
+ <item name="highlight_alpha_material_light" format="float" type="dimen">0.16</item>
+ <item name="highlight_alpha_material_dark" format="float" type="dimen">0.32</item>
+ <item name="highlight_alpha_material_colored" format="float" type="dimen">0.48</item>
<!-- Primary & accent colors -->
<eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e3a910f5cc76..3b02a967a4b2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3249,4 +3249,7 @@
<string name="config_fontFamilyButton">@string/font_family_button_material</string>
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
+
+ <!-- Package name that should be granted Notification Assistant access -->
+ <string name="config_defaultAssistantAccessPackage" translatable="false">android.ext.services</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f4ced5821e53..638f1b25b4a5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2550,6 +2550,7 @@
<java-symbol type="bool" name="config_mainBuiltInDisplayIsRound" />
<java-symbol type="id" name="actions_container" />
+ <java-symbol type="id" name="smart_reply_container" />
<java-symbol type="id" name="remote_input_tag" />
<java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
@@ -3215,4 +3216,6 @@
<java-symbol type="string" name="harmful_app_warning_uninstall" />
<java-symbol type="string" name="harmful_app_warning_launch_anyway" />
<java-symbol type="string" name="harmful_app_warning_title" />
+
+ <java-symbol type="string" name="config_defaultAssistantAccessPackage" />
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 410bee025df4..d5689c7539a1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -204,6 +204,7 @@ public class SettingsBackupTest {
Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
Settings.Global.ENABLE_DISKSTATS_LOGGING,
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
+ Settings.Global.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
@@ -212,6 +213,7 @@ public class SettingsBackupTest {
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
Settings.Global.GLOBAL_HTTP_PROXY_HOST,
diff --git a/core/tests/coretests/src/android/text/MeasuredTextTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index ddef0c651b31..5d33397e13f2 100644
--- a/core/tests/coretests/src/android/text/MeasuredTextTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class MeasuredTextTest {
+public class MeasuredParagraphTest {
private static final TextDirectionHeuristic LTR = TextDirectionHeuristics.LTR;
private static final TextDirectionHeuristic RTL = TextDirectionHeuristics.RTL;
@@ -60,9 +60,9 @@ public class MeasuredTextTest {
@Test
public void buildForBidi() {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
- mt = MeasuredText.buildForBidi("XXX", 0, 3, LTR, null);
+ mt = MeasuredParagraph.buildForBidi("XXX", 0, 3, LTR, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -75,7 +75,7 @@ public class MeasuredTextTest {
assertEquals(0, mt.getNativePtr());
// Recycle it
- MeasuredText mt2 = MeasuredText.buildForBidi("_VVV_", 1, 4, RTL, mt);
+ MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
@@ -91,9 +91,9 @@ public class MeasuredTextTest {
@Test
public void buildForMeasurement() {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
- mt = MeasuredText.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null);
+ mt = MeasuredParagraph.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -109,7 +109,8 @@ public class MeasuredTextTest {
assertEquals(0, mt.getNativePtr());
// Recycle it
- MeasuredText mt2 = MeasuredText.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt);
+ MeasuredParagraph mt2 =
+ MeasuredParagraph.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
@@ -129,9 +130,9 @@ public class MeasuredTextTest {
@Test
public void buildForStaticLayout() {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
- mt = MeasuredText.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null);
+ mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -145,7 +146,8 @@ public class MeasuredTextTest {
assertNotEquals(0, mt.getNativePtr());
// Recycle it
- MeasuredText mt2 = MeasuredText.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt);
+ MeasuredParagraph mt2 =
+ MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
@@ -163,6 +165,6 @@ public class MeasuredTextTest {
@Test
public void testFor70146381() {
- MeasuredText.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null);
+ MeasuredParagraph.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null);
}
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4732beca6394..c0958cd6cdd7 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -75,6 +75,7 @@ applications that come with the platform
<privapp-permissions package="com.android.launcher3">
<permission name="android.permission.BIND_APPWIDGET"/>
+ <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 419e2b7e4818..d13e05c22509 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -239,11 +239,12 @@ public final class ImageDecoder implements AutoCloseable {
};
/**
- * Supplied to onPartialImage if the provided data is incomplete.
+ * Used if the provided data is incomplete.
*
- * Will never be thrown by ImageDecoder.
+ * May be thrown if there is nothing to display.
*
- * There may be a partial image to display.
+ * If supplied to onPartialImage, there may be a correct partial image to
+ * display.
*/
public static class IncompleteException extends IOException {};
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index dea194e4ffde..4571553d4d4e 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -16,17 +16,12 @@
package android.graphics.drawable;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
-import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.FloatProperty;
-import android.view.DisplayListCanvas;
-import android.view.RenderNodeAnimator;
import android.view.animation.LinearInterpolator;
/**
@@ -78,8 +73,8 @@ class RippleBackground extends RippleComponent {
private void onStateChanged(boolean animateChanged) {
float newOpacity = 0.0f;
- if (mHovered) newOpacity += 1.0f;
- if (mFocused) newOpacity += 1.0f;
+ if (mHovered) newOpacity += .25f;
+ if (mFocused) newOpacity += .75f;
if (mAnimator != null) {
mAnimator.cancel();
mAnimator = null;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 734cff542c51..b883656d784a 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -264,8 +264,8 @@ public class RippleDrawable extends LayerDrawable {
}
setRippleActive(enabled && pressed);
-
setBackgroundActive(hovered, focused);
+
return changed;
}
@@ -879,22 +879,18 @@ public class RippleDrawable extends LayerDrawable {
// Grab the color for the current state and cut the alpha channel in
// half so that the ripple and background together yield full alpha.
final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
- final int halfAlpha = (Color.alpha(color) / 2) << 24;
final Paint p = mRipplePaint;
if (mMaskColorFilter != null) {
// The ripple timing depends on the paint's alpha value, so we need
// to push just the alpha channel into the paint and let the filter
// handle the full-alpha color.
- final int fullAlphaColor = color | (0xFF << 24);
- mMaskColorFilter.setColor(fullAlphaColor);
-
- p.setColor(halfAlpha);
+ mMaskColorFilter.setColor(color | 0xFF000000);
+ p.setColor(color & 0xFF000000);
p.setColorFilter(mMaskColorFilter);
p.setShader(mMaskShader);
} else {
- final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
- p.setColor(halfAlphaColor);
+ p.setColor(color);
p.setColorFilter(null);
p.setShader(null);
}
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
index b57e02d559e0..07483f60c69e 100644
--- a/media/java/android/media/update/ApiLoader.java
+++ b/media/java/android/media/update/ApiLoader.java
@@ -49,8 +49,8 @@ public final class ApiLoader {
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
sMediaLibrary = libContext.getClassLoader()
.loadClass(UPDATE_CLASS)
- .getMethod(UPDATE_METHOD, Context.class)
- .invoke(null, appContext);
+ .getMethod(UPDATE_METHOD, Context.class, Context.class)
+ .invoke(null, appContext, libContext);
return sMediaLibrary;
}
}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 19f01c2bcc7f..a1e2404ca78a 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -18,6 +18,7 @@ package android.media.update;
import android.annotation.SystemApi;
import android.widget.MediaController2;
+import android.widget.VideoView2;
/**
* Interface for connecting the public API to an updatable implementation.
@@ -31,4 +32,5 @@ import android.widget.MediaController2;
public interface StaticProvider {
MediaController2Provider createMediaController2(
MediaController2 instance, ViewProvider superProvider);
+ VideoView2Provider createVideoView2(VideoView2 instance, ViewProvider superProvider);
}
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
new file mode 100644
index 000000000000..6fc9bdc64e02
--- /dev/null
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.AudioAttributes;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.widget.MediaController2;
+import android.widget.VideoView2;
+
+import java.util.Map;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.widget.VideoView2
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface VideoView2Provider extends ViewProvider {
+ void start_impl();
+ void pause_impl();
+ int getDuration_impl();
+ int getCurrentPosition_impl();
+ void seekTo_impl(int msec);
+ boolean isPlaying_impl();
+ int getBufferPercentage_impl();
+ int getAudioSessionId_impl();
+ void showSubtitle_impl();
+ void hideSubtitle_impl();
+ void setAudioFocusRequest_impl(int focusGain);
+ void setAudioAttributes_impl(AudioAttributes attributes);
+ void setVideoPath_impl(String path);
+ void setVideoURI_impl(Uri uri);
+ void setVideoURI_impl(Uri uri, Map<String, String> headers);
+ void setMediaController2_impl(MediaController2 controllerView);
+ void setViewType_impl(int viewType);
+ int getViewType_impl();
+ void stopPlayback_impl();
+ void setOnPreparedListener_impl(MediaPlayer.OnPreparedListener l);
+ void setOnCompletionListener_impl(MediaPlayer.OnCompletionListener l);
+ void setOnErrorListener_impl(MediaPlayer.OnErrorListener l);
+ void setOnInfoListener_impl(MediaPlayer.OnInfoListener l);
+ void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l);
+}
diff --git a/native/android/net.c b/native/android/net.c
index de4b90cc1956..60296a7bd00c 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -27,7 +27,7 @@ static int getnetidfromhandle(net_handle_t handle, unsigned *netid) {
static const uint32_t k32BitMask = 0xffffffff;
// This value MUST be kept in sync with the corresponding value in
// the android.net.Network#getNetworkHandle() implementation.
- static const uint32_t kHandleMagic = 0xfacade;
+ static const uint32_t kHandleMagic = 0xcafed00d;
// Check for minimum acceptable version of the API in the low bits.
if (handle != NETWORK_UNSPECIFIED &&
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index e0a3f6cb31ca..6c7441802d63 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -59,7 +59,9 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;
@@ -159,44 +161,11 @@ public final class PrintSpoolerService extends Service {
return new PrintSpooler();
}
- private void dumpLocked(PrintWriter pw, String[] args) {
- String prefix = (args.length > 0) ? args[0] : "";
- String tab = " ";
-
- pw.append(prefix).append("print jobs:").println();
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- pw.append(prefix).append(tab).append(printJob.toString());
- pw.println();
- }
-
- pw.append(prefix).append("print job files:").println();
- File[] files = getFilesDir().listFiles();
- if (files != null) {
- final int fileCount = files.length;
- for (int i = 0; i < fileCount; i++) {
- File file = files[i];
- if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- pw.append(prefix).append(tab).append(file.getName()).println();
- }
- }
- }
-
- pw.append(prefix).append("approved print services:").println();
- Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
- if (approvedPrintServices != null) {
- for (String approvedService : approvedPrintServices) {
- pw.append(prefix).append(tab).append(approvedService).println();
- }
- }
- }
-
- private void dumpLocked(@NonNull ProtoOutputStream proto) {
+ private void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
int numPrintJobs = mPrintJobs.size();
for (int i = 0; i < numPrintJobs; i++) {
- writePrintJobInfo(this, proto, PrintSpoolerInternalStateProto.PRINT_JOBS,
- mPrintJobs.get(i));
+ writePrintJobInfo(this, dumpStream, "print_jobs",
+ PrintSpoolerInternalStateProto.PRINT_JOBS, mPrintJobs.get(i));
}
File[] files = getFilesDir().listFiles();
@@ -204,7 +173,8 @@ public final class PrintSpoolerService extends Service {
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- proto.write(PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
+ dumpStream.write("print_job_files",
+ PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
}
}
}
@@ -214,13 +184,13 @@ public final class PrintSpoolerService extends Service {
for (String approvedService : approvedPrintServices) {
ComponentName componentName = ComponentName.unflattenFromString(approvedService);
if (componentName != null) {
- writeComponentName(proto, PrintSpoolerInternalStateProto.APPROVED_SERVICES,
- componentName);
+ writeComponentName(dumpStream, "approved_services",
+ PrintSpoolerInternalStateProto.APPROVED_SERVICES, componentName);
}
}
}
- proto.flush();
+ dumpStream.flush();
}
@Override
@@ -244,9 +214,15 @@ public final class PrintSpoolerService extends Service {
try {
synchronized (mLock) {
if (dumpAsProto) {
- dumpLocked(new ProtoOutputStream(fd));
+ dumpLocked(new DualDumpOutputStream(new ProtoOutputStream(fd), null));
} else {
- dumpLocked(pw, args);
+ try (FileOutputStream out = new FileOutputStream(fd)) {
+ try (PrintWriter w = new PrintWriter(out)) {
+ dumpLocked(new DualDumpOutputStream(null, new IndentingPrintWriter(w,
+ " ")));
+ }
+ } catch (IOException ignored) {
+ }
}
}
} finally {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index adb4832adcff..ae24c079e37b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -16,9 +16,9 @@
package com.android.settingslib.core.lifecycle;
import static android.arch.lifecycle.Lifecycle.Event.ON_START;
-
import static com.google.common.truth.Truth.assertThat;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.view.Menu;
import android.view.MenuInflater;
@@ -48,6 +48,7 @@ import org.robolectric.annotation.Config;
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class LifecycleTest {
+ private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
public static class TestDialogFragment extends ObservableDialogFragment {
@@ -146,7 +147,8 @@ public class LifecycleTest {
@Before
public void setUp() {
- mLifecycle = new Lifecycle(() -> mLifecycle);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
index 5d5733e463ea..050877d3ebfe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -17,11 +17,11 @@
package com.android.settingslib.development;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.arch.lifecycle.LifecycleOwner;
import android.os.SystemProperties;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
@@ -44,6 +44,7 @@ import org.robolectric.annotation.Config;
shadows = SystemPropertiesTestImpl.class)
public class LogpersistPreferenceControllerTest {
+ private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Mock
@@ -57,7 +58,8 @@ public class LogpersistPreferenceControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
SystemProperties.set("ro.debuggable", "1");
- mLifecycle = new Lifecycle(() -> mLifecycle);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AbstractLogpersistPreferenceController(RuntimeEnvironment.application,
mLifecycle) {
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 75b6c5fcefc1..88c57b50f87e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -17,13 +17,13 @@
package com.android.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.arch.lifecycle.LifecycleOwner;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
@@ -49,13 +49,15 @@ public class FooterPreferenceMixinTest {
@Mock
private PreferenceScreen mScreen;
+ private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private FooterPreferenceMixin mMixin;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mLifecycle = new Lifecycle(() -> mLifecycle);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
when(mFragment.getPreferenceManager().getContext())
.thenReturn(ShadowApplication.getInstance().getApplicationContext());
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 287a888a23b2..fd4d2967bf61 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -8,6 +8,7 @@
android:process="system"
android:backupAgent="SettingsBackupAgent"
android:killAfterRestore="false"
+ android:restoreAnyVersion="true"
android:icon="@mipmap/ic_launcher_settings"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1be064564574..48a3a3084ca0 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -28,7 +28,7 @@
<string name="def_bluetooth_disabled_profiles" translatable="false">0</string>
<bool name="def_auto_time">true</bool>
<bool name="def_auto_time_zone">true</bool>
- <bool name="def_accelerometer_rotation">true</bool>
+ <bool name="def_accelerometer_rotation">false</bool>
<!-- Default screen brightness, from 0 to 255. 102 is 40%. -->
<integer name="def_screen_brightness">102</integer>
<bool name="def_screen_brightness_automatic_mode">false</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index ae882275de7c..c7ba4d6ec750 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -31,10 +31,12 @@ import android.net.NetworkPolicyManager;
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.BackupUtils;
import android.util.Log;
@@ -50,6 +52,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -147,6 +150,13 @@ public class SettingsBackupAgent extends BackupAgentHelper {
// stored in the full-backup tarfile as well, so should not be changed.
private static final String STAGE_FILE = "flattened-data";
+ // List of keys that support restore to lower version of the SDK, introduced in Android P
+ private static final ArraySet<String> RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS =
+ new ArraySet<String>(Arrays.asList(new String[] {
+ KEY_NETWORK_POLICIES,
+ KEY_WIFI_NEW_CONFIG,
+ }));
+
private SettingsHelper mSettingsHelper;
private WifiManager mWifiManager;
@@ -209,6 +219,10 @@ public class SettingsBackupAgent extends BackupAgentHelper {
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
+ if (DEBUG) {
+ Log.d(TAG, "onRestore(): appVersionCode: " + appVersionCode
+ + "; Build.VERSION.SDK_INT: " + Build.VERSION.SDK_INT);
+ }
// versionCode of com.android.providers.settings corresponds to SDK_INT
mRestoredFromSdkInt = appVersionCode;
@@ -221,6 +235,15 @@ public class SettingsBackupAgent extends BackupAgentHelper {
while (data.readNextHeader()) {
final String key = data.getKey();
final int size = data.getDataSize();
+
+ // bail out of restoring from higher SDK_INT version for unsupported keys
+ if (appVersionCode > Build.VERSION.SDK_INT
+ && !RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS.contains(key)) {
+ Log.w(TAG, "Not restoring unrecognized key '"
+ + key + "' from future version " + appVersionCode);
+ continue;
+ }
+
switch (key) {
case KEY_SYSTEM :
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 36981320ba5f..d33e0841a906 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1122,6 +1122,9 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.ZRAM_ENABLED,
GlobalSettingsProto.ZRAM_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS,
+ GlobalSettingsProto.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS);
}
/** Dump a single {@link SettingsState.Setting} to a proto buf */
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1167d69a1577..175cff6b61b2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1584,6 +1584,11 @@ public class SettingsProvider extends ContentProvider {
restriction = UserManager.DISALLOW_SAFE_BOOT;
break;
+ case Settings.Global.AIRPLANE_MODE_ON:
+ if ("0".equals(value)) return false;
+ restriction = UserManager.DISALLOW_AIRPLANE_MODE;
+ break;
+
default:
if (setting != null && setting.startsWith(Settings.Global.DATA_ROAMING)) {
if ("0".equals(value)) return false;
@@ -2940,7 +2945,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 150;
+ private static final int SETTINGS_VERSION = 151;
private final int mUserId;
@@ -3533,6 +3538,18 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 150;
}
+ if (currentVersion == 150) {
+ // Version 151: Reset rotate locked setting for upgrading users
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ systemSettings.insertSettingLocked(
+ Settings.System.ACCELEROMETER_ROTATION,
+ getContext().getResources().getBoolean(
+ R.bool.def_accelerometer_rotation) ? "1" : "0",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+
+ currentVersion = 151;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index c52c0aae3556..61f7fe8dc019 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -108,6 +108,7 @@ public interface QSTile {
public Supplier<Icon> iconSupplier;
public int state = Tile.STATE_ACTIVE;
public CharSequence label;
+ public CharSequence secondaryLabel;
public CharSequence contentDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
@@ -122,6 +123,7 @@ public interface QSTile {
final boolean changed = !Objects.equals(other.icon, icon)
|| !Objects.equals(other.iconSupplier, iconSupplier)
|| !Objects.equals(other.label, label)
+ || !Objects.equals(other.secondaryLabel, secondaryLabel)
|| !Objects.equals(other.contentDescription, contentDescription)
|| !Objects.equals(other.dualLabelContentDescription,
dualLabelContentDescription)
@@ -135,6 +137,7 @@ public interface QSTile {
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
+ other.secondaryLabel = secondaryLabel;
other.contentDescription = contentDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.expandedAccessibilityClassName = expandedAccessibilityClassName;
@@ -156,6 +159,7 @@ public interface QSTile {
sb.append(",icon=").append(icon);
sb.append(",iconSupplier=").append(iconSupplier);
sb.append(",label=").append(label);
+ sb.append(",secondaryLabel=").append(secondaryLabel);
sb.append(",contentDescription=").append(contentDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
index 56a3ee3a28f7..e25930c18947 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
@@ -50,5 +50,7 @@ public interface NavBarButtonProvider extends Plugin {
}
void setDarkIntensity(float intensity);
+
+ void setDelayTouchFeedback(boolean shouldDelay);
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
index 674ed5a3a480..6131acc91ca5 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -14,6 +14,7 @@
package com.android.systemui.plugins.statusbar.phone;
+import android.graphics.Canvas;
import android.view.MotionEvent;
import com.android.systemui.plugins.Plugin;
@@ -35,6 +36,12 @@ public interface NavGesture extends Plugin {
public void setBarState(boolean vertical, boolean isRtl);
+ public void onDraw(Canvas canvas);
+
+ public void onDarkIntensityChange(float intensity);
+
+ public void onLayout(boolean changed, int left, int top, int right, int bottom);
+
public default void destroy() { }
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index c97cfc4bb835..9adb5501345d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -34,7 +34,6 @@
android:orientation="vertical">
<RelativeLayout
android:id="@+id/keyguard_clock_container"
- android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top">
@@ -51,16 +50,6 @@
android:format12Hour="@string/keyguard_widget_12_hours_format"
android:format24Hour="@string/keyguard_widget_24_hours_format"
android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
- <com.android.systemui.ChargingView
- android:id="@+id/battery_doze"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignTop="@id/clock_view"
- android:layout_alignBottom="@id/clock_view"
- android:layout_toEndOf="@id/clock_view"
- android:visibility="invisible"
- android:src="@drawable/ic_aod_charging_24dp"
- android:contentDescription="@string/accessibility_ambient_display_charging" />
<View
android:id="@+id/clock_separator"
android:layout_width="16dp"
diff --git a/packages/SystemUI/res/drawable/ic_face_unlock.xml b/packages/SystemUI/res/drawable/ic_face_unlock.xml
new file mode 100644
index 000000000000..29c22759be70
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_face_unlock.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="?attr/wallpaperTextColor"
+ android:strokeColor="?attr/wallpaperTextColor"
+ android:strokeWidth="1"
+ android:pathData="M9,11.75C8.31,11.75 7.75,12.31 7.75,13C7.75,13.69 8.31,14.25 9,14.25C9.69,14.25 10.25,13.69 10.25,13C10.25,12.31 9.69,11.75 9,11.75ZM15,11.75C14.31,11.75 13.75,12.31 13.75,13C13.75,13.69 14.31,14.25 15,14.25C15.69,14.25 16.25,13.69 16.25,13C16.25,12.31 15.69,11.75 15,11.75ZM12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM12,20C7.59,20 4,16.41 4,12C4,11.71 4.02,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z"
+ />
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_background_primary.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml
index 03bba53946da..dd74cadd0955 100644
--- a/packages/SystemUI/res/drawable/qs_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_primary.xml
@@ -16,5 +16,6 @@
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
<solid android:color="@color/qs_background_dark"/>
+ <corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
new file mode 100644
index 000000000000..1cd1451008b4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/notification_ripple_untinted_color">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/smart_reply_button_corner_radius"/>
+ <solid android:color="@color/smart_reply_button_background"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 2fd4df4d1cc7..d0d379c286bd 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -40,6 +40,7 @@
android:gravity="center_horizontal"
android:textStyle="italic"
android:textColor="?attr/wallpaperTextColorSecondary"
+ android:textSize="16sp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:visibility="gone" />
@@ -50,6 +51,7 @@
android:gravity="center_horizontal"
android:textStyle="italic"
android:textColor="?attr/wallpaperTextColorSecondary"
+ android:textSize="16sp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:accessibilityLiveRegion="polite" />
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
new file mode 100644
index 000000000000..4ac41d5cf6c3
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/smart_reply_button_spacing"
+ android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+ android:paddingHorizontal="@dimen/smart_reply_button_corner_radius"
+ android:background="@drawable/smart_reply_button_background"
+ android:gravity="center"
+ android:fontFamily="sans-serif"
+ android:textSize="@dimen/smart_reply_button_font_size"
+ android:textColor="@color/smart_reply_button_text"
+ android:textStyle="normal"
+ android:singleLine="true"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/smart_reply_view.xml b/packages/SystemUI/res/layout/smart_reply_view.xml
new file mode 100644
index 000000000000..6d5338697161
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_reply_view.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- LinearLayout -->
+<com.android.systemui.statusbar.policy.SmartReplyView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/smart_reply_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end">
+ <!-- smart_reply_button(s) will be added here. -->
+</com.android.systemui.statusbar.policy.SmartReplyView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index bef0830d5802..c5e5ee1f4af0 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,6 +43,8 @@
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:layout_marginStart="@dimen/notification_side_paddings"
+ android:layout_marginEnd="@dimen/notification_side_paddings"
android:clipToPadding="false"
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 7f3708788d95..4614999e3c4f 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -54,6 +54,18 @@
android:paddingStart="8dp"
/>
+ <ImageButton
+ android:id="@+id/helper"
+ android:layout_width="48dp"
+ android:layout_height="@*android:dimen/notification_header_height"
+ android:layout_gravity="top|end"
+ android:layout_marginEnd="6dp"
+ android:src="@drawable/ic_dnd"
+ android:tint="#FF0000"
+ android:background="@drawable/ripple_drawable"
+ android:visibility="visible"
+ />
+
<ViewStub
android:layout="@layout/notification_children_container"
android:id="@+id/child_container_stub"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f0d23469a49a..748c9a5ec7ed 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -17,130 +17,76 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@android:color/transparent"
android:theme="@style/qs_theme"
android:clipChildren="false" >
- <RelativeLayout
+ <LinearLayout
android:id="@+id/volume_dialog"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/volume_row_padding_bottom"
- android:background="@drawable/rounded_full_bg_bottom"
+ android:layout_gravity="center_vertical|end"
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:background="@android:color/transparent"
+ android:layout_margin="12dp"
android:translationZ="8dp"
+ android:orientation="vertical"
android:clipChildren="false" >
<LinearLayout
- android:id="@+id/volume_dialog_content"
- android:layout_width="match_parent"
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="wrap_content"
- android:layout_toStartOf="@id/expand"
android:clipChildren="false"
android:clipToPadding="false"
- android:orientation="vertical" >
-
- <LinearLayout
- android:id="@+id/volume_dialog_rows"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:background="@drawable/rounded_bg_full"
+ android:orientation="horizontal" >
<!-- volume rows added and removed here! :-) -->
- </LinearLayout>
-
-
</LinearLayout>
+
<LinearLayout
- android:id="@+id/expand"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_marginEnd="@dimen/volume_expander_margin_end" >
+ android:id="@+id/footer"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layout_height="@dimen/volume_dialog_panel_width"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp"
+ android:layout_below="@id/volume_dialog_rows"
+ android:background="@drawable/rounded_bg_full"
+ android:gravity="center"
+ android:orientation="vertical" >
+
<TextView
+ android:id="@+id/ringer_title"
+ android:text="@string/ring_toggle_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
- android:textAppearance="@style/TextAppearance.Volume.Header" />
- <com.android.keyguard.AlphaOptimizedImageButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/volume_expand_button"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:clickable="true"
- android:soundEffectsEnabled="false"
- android:src="@drawable/ic_volume_expand_animation"
- android:background="@drawable/ripple_drawable"
- tools:ignore="RtlHardcoded" />
- </LinearLayout>
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_below="@id/volume_dialog_content"
- android:layout_margin="10dp">
- <!-- special row for ringer mode -->
- <RelativeLayout
- android:id="@+id/ringer_mode"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/rounded_bg_full"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_toStartOf="@id/output_chooser"
- android:layout_margin="10dp">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/ringer_icon"
- style="@style/VolumeButtons"
- android:background="?android:selectableItemBackgroundBorderless"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:soundEffectsEnabled="false" />
-
- <TextView
- android:id="@+id/ringer_title"
- android:text="@string/ring_toggle_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_toEndOf="@+id/ringer_icon"
- android:layout_marginStart="64dp"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingStart="@dimen/volume_row_header_padding_start" />
-
- <TextView
- android:id="@+id/ringer_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:layout_marginEnd="14dp"
- android:maxLines="1"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:layout_centerVertical="true"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
- </RelativeLayout>
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/output_chooser"
+ android:id="@+id/ringer_icon"
style="@style/VolumeButtons"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_width="@dimen/volume_button_size"
android:layout_height="@dimen/volume_button_size"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:src="@drawable/ic_settings_bluetooth"
+ android:tint="?android:attr/colorAccent"
android:soundEffectsEnabled="false" />
- </RelativeLayout>
- </RelativeLayout>
+
+ <TextView
+ android:id="@+id/ringer_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </LinearLayout>
+ </LinearLayout>
</com.android.systemui.volume.VolumeUiLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index bf76e78c94a2..3590b768c91b 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -15,48 +15,70 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_height"
- android:clipChildren="false"
- android:clipToPadding="false"
+ android:tag="row"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:clipChildren="true"
+ android:clipToPadding="true"
android:theme="@style/qs_theme"
+ android:gravity="center"
android:orientation="vertical" >
- <TextView
- android:id="@+id/volume_row_header"
+ <LinearLayout
+ android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingStart="@dimen/volume_row_header_padding_start" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_slider_height"
- android:orientation="horizontal"
- android:paddingStart="@dimen/volume_row_padding_start" >
+ android:gravity="center"
+ android:padding="10dp">
+ <TextView
+ android:id="@+id/volume_row_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ <TextView
+ android:id="@+id/volume_row_connected_device"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/volume_row_icon"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:soundEffectsEnabled="false" />
-
- <SeekBar
- android:id="@+id/volume_row_slider"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignWithParentIfMissing="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:paddingStart="@dimen/volume_row_slider_padding_start"/>
+ android:id="@+id/output_chooser"
+ style="@style/VolumeButtons"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_volume_expand_animation"
+ android:soundEffectsEnabled="false" />
</LinearLayout>
+ <FrameLayout
+ android:id="@+id/volume_row_slider_frame"
+ android:padding="10dp"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layout_height="150dp">
+ <SeekBar
+ android:id="@+id/volume_row_slider"
+ android:padding="0dp"
+ android:layout_margin="0dp"
+ android:layout_width="150dp"
+ android:layout_height="@dimen/volume_dialog_panel_width"
+ android:layout_gravity="center"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:rotation="270" />
+ </FrameLayout>
- <Space
- android:id="@+id/spacer"
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_padding_bottom"/>
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_row_icon"
+ style="@style/VolumeButtons"
+ android:padding="10dp"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:soundEffectsEnabled="false" />
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f244d88b8573..0f4c3b8b9977 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -143,6 +143,9 @@
<color name="remote_input_accent">#eeeeee</color>
+ <color name="quick_step_track_background_dark">#61000000</color>
+ <color name="quick_step_track_background_light">#4DFFFFFF</color>
+
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
@@ -153,4 +156,6 @@
<color name="zen_introduction">#ffffffff</color>
+ <color name="smart_reply_button_text">#ff4285f4</color><!-- blue 500 -->
+ <color name="smart_reply_button_background">#fff7f7f7</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a510c4a6c503..7a670fd1ae5d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -264,7 +264,7 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_width">315dp</dimen>
+ <dimen name="volume_dialog_panel_width">120dp</dimen>
<!-- Gravity for the notification panel -->
<integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
@@ -579,6 +579,7 @@
<dimen name="keyguard_affordance_icon_width">24dp</dimen>
<dimen name="keyguard_indication_margin_bottom">65dp</dimen>
+ <dimen name="keyguard_indication_margin_bottom_ambient">30dp</dimen>
<!-- The text size for battery level -->
<dimen name="battery_level_text_size">12sp</dimen>
@@ -870,6 +871,8 @@
<dimen name="rounded_corner_radius">0dp</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
+ <dimen name="nav_quick_scrub_track_edge_padding">32dp</dimen>
+ <dimen name="nav_quick_scrub_track_thickness">2dp</dimen>
<!-- Intended corner radius when drawing the mobile signal -->
<dimen name="stat_sys_mobile_signal_corner_radius">0.75dp</dimen>
@@ -879,4 +882,9 @@
<!-- Home button padding for sizing -->
<dimen name="home_padding">15dp</dimen>
+ <!-- Smart reply button -->
+ <dimen name="smart_reply_button_corner_radius">24dip</dimen>
+ <dimen name="smart_reply_button_spacing">8dp</dimen>
+ <dimen name="smart_reply_button_padding_vertical">4dp</dimen>
+ <dimen name="smart_reply_button_font_size">14sp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6ff239ebd2ee..dde4dcfb23fe 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -61,11 +61,26 @@
<!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. [CHAR LIMIT=NONE]-->
<string name="battery_low_title">Battery is low</string>
+ <!-- When the battery is low and hybrid notifications are enabled, this is displayed to the user in a dialog.
+ The title of the low battery alert. [CHAR LIMIT=NONE]-->
+ <string name="battery_low_title_hybrid">Battery is low. Turn on Battery Saver</string>
+
<!-- A message that appears when the battery level is getting low in a dialog. This is
- appened to the subtitle of the low battery alert. "percentage" is the percentage of battery
+ appended to the subtitle of the low battery alert. "percentage" is the percentage of battery
remaining [CHAR LIMIT=none]-->
<string name="battery_low_percent_format"><xliff:g id="percentage">%s</xliff:g> remaining</string>
+ <!-- A message that appears when the battery remaining estimate is low in a dialog. This is
+ appended to the subtitle of the low battery alert. "percentage" is the percentage of battery
+ remaining. "time" is the amount of time remaining before the phone runs out of battery [CHAR LIMIT=none]-->
+ <string name="battery_low_percent_format_hybrid"><xliff:g id="percentage">%s</xliff:g> remaining, about <xliff:g id="time">%s</xliff:g> left based on your usage</string>
+
+ <!-- A message that appears when the battery remaining estimate is low in a dialog and insufficient
+ data was present to say it is customized to the user. This is appended to the subtitle of the
+ low battery alert. "percentage" is the percentage of battery remaining. "time" is the amount
+ of time remaining before the phone runs out of battery [CHAR LIMIT=none]-->
+ <string name="battery_low_percent_format_hybrid_short"><xliff:g id="percentage">%s</xliff:g> remaining, about <xliff:g id="time">%s</xliff:g> left</string>
+
<!-- Same as battery_low_percent_format, with a notice about battery saver if on. [CHAR LIMIT=none]-->
<string name="battery_low_percent_format_saver_started"><xliff:g id="percentage">%s</xliff:g> remaining. Battery Saver is on.</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 173a90a5baa7..64fa9c61280a 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
@@ -22,4 +22,8 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
oneway interface IOverviewProxy {
void onBind(in ISystemUiProxy sysUiProxy);
void onMotionEvent(in MotionEvent event);
+ void onQuickSwitch();
+ void onQuickScrubStart();
+ void onQuickScrubEnd();
+ void onQuickScrubProgress(float progress);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index a5d19639580e..13f30b2c27b9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -28,11 +28,14 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
import android.os.Trace;
import android.util.ArraySet;
import android.util.IntProperty;
import android.util.Property;
import android.util.TypedValue;
+import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
@@ -291,6 +294,20 @@ public class Utilities {
}
/**
+ * @return The next frame name for the specified surface.
+ */
+ public static long getNextFrameNumber(Surface s) {
+ return s.getNextFrameNumber();
+ }
+
+ /**
+ * @return The surface for the specified view.
+ */
+ public static Surface getSurface(View v) {
+ return v.getViewRootImpl().mSurface;
+ }
+
+ /**
* Returns a lightweight dump of a rect.
*/
public static String dumpRect(Rect r) {
@@ -299,4 +316,12 @@ public class Utilities {
}
return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
}
+
+ /**
+ * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
+ */
+ public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
+ Message msg = h.obtainMessage().setCallback(r);
+ h.sendMessageAtFrontOfQueue(msg);
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
new file mode 100644
index 000000000000..0d8ce58d55fd
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.Activity;
+
+public class ActivityCompat {
+ private final Activity mWrapped;
+
+ public ActivityCompat(Activity activity) {
+ mWrapped = activity;
+ }
+
+ /**
+ * @see Activity#registerRemoteAnimations
+ */
+ public void registerRemoteAnimations(RemoteAnimationDefinitionCompat definition) {
+ mWrapped.registerRemoteAnimations(definition.getWrapped());
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 705a21522b0a..712cca67c5d6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -38,4 +38,9 @@ public abstract class ActivityOptionsCompat {
: SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
return options;
}
+
+ public static ActivityOptions makeRemoteAnimation(
+ RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+ return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
new file mode 100644
index 000000000000..625b1de74290
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+
+/**
+ * @see RemoteAnimationAdapter
+ */
+public class RemoteAnimationAdapterCompat {
+
+ private final RemoteAnimationAdapter mWrapped;
+
+ public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration,
+ long statusBarTransitionDelay) {
+ mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration,
+ statusBarTransitionDelay);
+ }
+
+ RemoteAnimationAdapter getWrapped() {
+ return mWrapped;
+ }
+
+ private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
+ RemoteAnimationRunnerCompat remoteAnimationAdapter) {
+ return new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(RemoteAnimationTarget[] apps,
+ IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ final RemoteAnimationTargetCompat[] appsCompat =
+ RemoteAnimationTargetCompat.wrap(apps);
+ final Runnable animationFinishedCallback = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ + " finished callback", e);
+ }
+ }
+ };
+ remoteAnimationAdapter.onAnimationStart(appsCompat, animationFinishedCallback);
+ }
+
+ @Override
+ public void onAnimationCancelled() throws RemoteException {
+ remoteAnimationAdapter.onAnimationCancelled();
+ }
+ };
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java
new file mode 100644
index 000000000000..5fff5febec85
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.view.RemoteAnimationDefinition;
+
+/**
+ * @see RemoteAnimationDefinition
+ */
+public class RemoteAnimationDefinitionCompat {
+
+ private final RemoteAnimationDefinition mWrapped = new RemoteAnimationDefinition();
+
+ public void addRemoteAnimation(int transition, RemoteAnimationAdapterCompat adapter) {
+ mWrapped.addRemoteAnimation(transition, adapter.getWrapped());
+ }
+
+ RemoteAnimationDefinition getWrapped() {
+ return mWrapped;
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
new file mode 100644
index 000000000000..5a85df967197
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+public interface RemoteAnimationRunnerCompat {
+ void onAnimationStart(RemoteAnimationTargetCompat[] apps, Runnable finishedCallback);
+ void onAnimationCancelled();
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
new file mode 100644
index 000000000000..3871980a5b17
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.RemoteAnimationTarget;
+
+/**
+ * @see RemoteAnimationTarget
+ */
+public class RemoteAnimationTargetCompat {
+
+ public static final int MODE_OPENING = RemoteAnimationTarget.MODE_OPENING;
+ public static final int MODE_CLOSING = RemoteAnimationTarget.MODE_CLOSING;
+
+ public final int taskId;
+ public final int mode;
+ public final SurfaceControlCompat leash;
+ public final boolean isTranslucent;
+ public final Rect clipRect;
+ public final int prefixOrderIndex;
+ public final Point position;
+ public final Rect sourceContainerBounds;
+
+ public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
+ taskId = app.taskId;
+ mode = app.mode;
+ leash = new SurfaceControlCompat(app.leash);
+ isTranslucent = app.isTranslucent;
+ clipRect = app.clipRect;
+ position = app.position;
+ sourceContainerBounds = app.sourceContainerBounds;
+ prefixOrderIndex = app.prefixOrderIndex;
+ }
+
+ public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
+ final RemoteAnimationTargetCompat[] appsCompat =
+ new RemoteAnimationTargetCompat[apps.length];
+ for (int i = 0; i < apps.length; i++) {
+ appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]);
+ }
+ return appsCompat;
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
new file mode 100644
index 000000000000..cd12141c3268
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.view.SurfaceControl;
+
+public class SurfaceControlCompat {
+ SurfaceControl mSurfaceControl;
+
+ public SurfaceControlCompat(SurfaceControl surfaceControl) {
+ mSurfaceControl = surfaceControl;
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
new file mode 100644
index 000000000000..c82c5191b127
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+public class TransactionCompat {
+
+ private final Transaction mTransaction;
+
+ private final float[] mTmpValues = new float[9];
+
+ public TransactionCompat() {
+ mTransaction = new Transaction();
+ }
+
+ public void apply() {
+ mTransaction.apply();
+ }
+
+ public TransactionCompat show(SurfaceControlCompat surfaceControl) {
+ mTransaction.show(surfaceControl.mSurfaceControl);
+ return this;
+ }
+
+ public TransactionCompat hide(SurfaceControlCompat surfaceControl) {
+ mTransaction.hide(surfaceControl.mSurfaceControl);
+ return this;
+ }
+
+ public TransactionCompat setPosition(SurfaceControlCompat surfaceControl, float x, float y) {
+ mTransaction.setPosition(surfaceControl.mSurfaceControl, x, y);
+ return this;
+ }
+
+ public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
+ mTransaction.setSize(surfaceControl.mSurfaceControl, w, h);
+ return this;
+ }
+
+ public TransactionCompat setLayer(SurfaceControlCompat surfaceControl, int z) {
+ mTransaction.setLayer(surfaceControl.mSurfaceControl, z);
+ return this;
+ }
+
+ public TransactionCompat setAlpha(SurfaceControlCompat surfaceControl, float alpha) {
+ mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);
+ return this;
+ }
+
+ public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, float dsdx, float dtdx,
+ float dtdy, float dsdy) {
+ mTransaction.setMatrix(surfaceControl.mSurfaceControl, dsdx, dtdx, dtdy, dsdy);
+ return this;
+ }
+
+ public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, Matrix matrix) {
+ mTransaction.setMatrix(surfaceControl.mSurfaceControl, matrix, mTmpValues);
+ return this;
+ }
+
+ public TransactionCompat setWindowCrop(SurfaceControlCompat surfaceControl, Rect crop) {
+ mTransaction.setWindowCrop(surfaceControl.mSurfaceControl, crop);
+ return this;
+ }
+
+ public TransactionCompat setFinalCrop(SurfaceControlCompat surfaceControl, Rect crop) {
+ mTransaction.setFinalCrop(surfaceControl.mSurfaceControl, crop);
+ return this;
+ }
+
+ public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl,
+ IBinder handle, long frameNumber) {
+ mTransaction.deferTransactionUntil(surfaceControl.mSurfaceControl, handle, frameNumber);
+ return this;
+ }
+
+ public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl,
+ Surface barrier, long frameNumber) {
+ mTransaction.deferTransactionUntilSurface(surfaceControl.mSurfaceControl, barrier,
+ frameNumber);
+ return this;
+ }
+
+ public TransactionCompat setColor(SurfaceControlCompat surfaceControl, float[] color) {
+ mTransaction.setColor(surfaceControl.mSurfaceControl, color);
+ return this;
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 225dbb4aafbe..68400fc977df 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -20,9 +20,10 @@ import static android.view.Display.DEFAULT_DISPLAY;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
@@ -32,6 +33,31 @@ public class WindowManagerWrapper {
private static final String TAG = "WindowManagerWrapper";
+ public static final int TRANSIT_UNSET = WindowManager.TRANSIT_UNSET;
+ public static final int TRANSIT_NONE = WindowManager.TRANSIT_NONE;
+ public static final int TRANSIT_ACTIVITY_OPEN = WindowManager.TRANSIT_ACTIVITY_OPEN;
+ public static final int TRANSIT_ACTIVITY_CLOSE = WindowManager.TRANSIT_ACTIVITY_CLOSE;
+ public static final int TRANSIT_TASK_OPEN = WindowManager.TRANSIT_TASK_OPEN;
+ public static final int TRANSIT_TASK_CLOSE = WindowManager.TRANSIT_TASK_CLOSE;
+ public static final int TRANSIT_TASK_TO_FRONT = WindowManager.TRANSIT_TASK_TO_FRONT;
+ public static final int TRANSIT_TASK_TO_BACK = WindowManager.TRANSIT_TASK_TO_BACK;
+ public static final int TRANSIT_WALLPAPER_CLOSE = WindowManager.TRANSIT_WALLPAPER_CLOSE;
+ public static final int TRANSIT_WALLPAPER_OPEN = WindowManager.TRANSIT_WALLPAPER_OPEN;
+ public static final int TRANSIT_WALLPAPER_INTRA_OPEN =
+ WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+ public static final int TRANSIT_WALLPAPER_INTRA_CLOSE =
+ WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
+ public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+ public static final int TRANSIT_TASK_IN_PLACE = WindowManager.TRANSIT_TASK_IN_PLACE;
+ public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
+ public static final int TRANSIT_DOCK_TASK_FROM_RECENTS =
+ WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+ public static final int TRANSIT_KEYGUARD_GOING_AWAY = WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+ public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
+ WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+ public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+ public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+
private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
public static WindowManagerWrapper getInstance() {
@@ -65,4 +91,14 @@ public class WindowManagerWrapper {
Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
}
}
+
+ public void overridePendingAppTransitionRemote(
+ RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote(
+ remoteAnimationAdapter.getWrapped());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override pending app transition (remote): ", e);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 8135c616d87e..d80a33632f3e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -151,6 +151,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
mClickActions.clear();
final int subItemsCount = subItems.size();
+ final int blendedColor = getTextColor();
for (int i = 0; i < subItemsCount; i++) {
SliceItem item = subItems.get(i);
@@ -159,7 +160,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
KeyguardSliceButton button = mRow.findViewWithTag(itemTag);
if (button == null) {
button = new KeyguardSliceButton(mContext);
- button.setTextColor(mTextColor);
+ button.setTextColor(blendedColor);
button.setTag(itemTag);
} else {
mRow.removeView(button);
@@ -258,7 +259,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
}
private void updateTextColors() {
- final int blendedColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ final int blendedColor = getTextColor();
mTitle.setTextColor(blendedColor);
int childCount = mRow.getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -322,6 +323,10 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
}
}
+ public int getTextColor() {
+ return ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ }
+
/**
* Representation of an item that appears under the clock on main keyguard message.
* Shows optional separator.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 4b9a8744900d..2873afbca8e9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
-import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -40,7 +39,6 @@ import android.widget.TextClock;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.ChargingView;
import com.google.android.collect.Sets;
@@ -60,7 +58,6 @@ public class KeyguardStatusView extends GridLayout {
private View mClockSeparator;
private TextView mOwnerInfo;
private ViewGroup mClockContainer;
- private ChargingView mBatteryDoze;
private KeyguardSliceView mKeyguardSlice;
private Runnable mPendingMarqueeStart;
private Handler mHandler;
@@ -155,11 +152,9 @@ public class KeyguardStatusView extends GridLayout {
mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
}
mOwnerInfo = findViewById(R.id.owner_info);
- mBatteryDoze = findViewById(R.id.battery_doze);
mKeyguardSlice = findViewById(R.id.keyguard_status_area);
mClockSeparator = findViewById(R.id.clock_separator);
- mVisibleInDoze = Sets.newArraySet(mBatteryDoze, mClockView, mKeyguardSlice,
- mClockSeparator);
+ mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice, mClockSeparator);
mTextColor = mClockView.getCurrentTextColor();
mKeyguardSlice.setListener(this::onSliceContentChanged);
@@ -184,10 +179,6 @@ public class KeyguardStatusView extends GridLayout {
mClockView.setTranslationY(translation);
mClockView.setScaleX(clockScale);
mClockView.setScaleY(clockScale);
- final float batteryTranslation =
- -(mClockView.getWidth() - (mClockView.getWidth() * clockScale)) / 2;
- mBatteryDoze.setTranslationX(batteryTranslation);
- mBatteryDoze.setTranslationY(translation);
mClockSeparator.setVisibility(hasHeader ? VISIBLE : GONE);
}
@@ -310,7 +301,7 @@ public class KeyguardStatusView extends GridLayout {
}
}
- public void setDark(float darkAmount) {
+ public void setDarkAmount(float darkAmount) {
if (mDarkAmount == darkAmount) {
return;
}
@@ -331,7 +322,6 @@ public class KeyguardStatusView extends GridLayout {
final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount);
updateDozeVisibleViews();
- mBatteryDoze.setDark(dark);
mKeyguardSlice.setDark(darkAmount);
mClockView.setTextColor(blendedTextColor);
mClockSeparator.setBackgroundColor(blendedTextColor);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ab2bce8cc094..e58ad0589c8a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1613,11 +1613,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
}
- private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
+ private boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
- final boolean stateChangedWhilePluggedIn =
- wasPluggedIn == true && nowPluggedIn == true
+ final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
&& (old.status != current.status);
// change in plug state is always interesting
@@ -1630,6 +1629,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
return true;
}
+ // change in battery level while keyguard visible
+ if (mKeyguardIsVisible && old.level != current.level) {
+ return true;
+ }
+
// change where battery needs charging
if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/ChargingView.java b/packages/SystemUI/src/com/android/systemui/ChargingView.java
deleted file mode 100644
index 33f8b069b751..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ChargingView.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-/**
- * A view that only shows its drawable while the phone is charging.
- *
- * Also reloads its drawable upon density changes.
- */
-public class ChargingView extends ImageView implements
- BatteryController.BatteryStateChangeCallback,
- ConfigurationController.ConfigurationListener {
-
- private static final long CHARGING_INDICATION_DELAY_MS = 1000;
-
- private final AmbientDisplayConfiguration mConfig;
- private final Runnable mClearSuppressCharging = this::clearSuppressCharging;
- private BatteryController mBatteryController;
- private int mImageResource;
- private boolean mCharging;
- private boolean mDark;
- private boolean mSuppressCharging;
-
-
- private void clearSuppressCharging() {
- mSuppressCharging = false;
- removeCallbacks(mClearSuppressCharging);
- updateVisibility();
- }
-
- public ChargingView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
-
- mConfig = new AmbientDisplayConfiguration(context);
-
- TypedArray a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.src});
- int srcResId = a.getResourceId(0, 0);
-
- if (srcResId != 0) {
- mImageResource = srcResId;
- }
-
- a.recycle();
-
- updateVisibility();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- mBatteryController = Dependency.get(BatteryController.class);
- mBatteryController.addCallback(this);
- Dependency.get(ConfigurationController.class).addCallback(this);
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mBatteryController.removeCallback(this);
- Dependency.get(ConfigurationController.class).removeCallback(this);
- removeCallbacks(mClearSuppressCharging);
- }
-
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- boolean startCharging = charging && !mCharging;
- if (startCharging && deviceWillWakeUpWhenPluggedIn() && mDark) {
- // We're about to wake up, and thus don't want to show the indicator just for it to be
- // hidden again.
- clearSuppressCharging();
- mSuppressCharging = true;
- postDelayed(mClearSuppressCharging, CHARGING_INDICATION_DELAY_MS);
- }
- mCharging = charging;
- updateVisibility();
- }
-
- private boolean deviceWillWakeUpWhenPluggedIn() {
- boolean plugTurnsOnScreen = getResources().getBoolean(
- com.android.internal.R.bool.config_unplugTurnsOnScreen);
- boolean aod = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
- return !aod && plugTurnsOnScreen;
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- setImageResource(mImageResource);
- }
-
- public void setDark(boolean dark) {
- mDark = dark;
- if (!dark) {
- clearSuppressCharging();
- }
- updateVisibility();
- }
-
- private void updateVisibility() {
- setVisibility(mCharging && !mSuppressCharging && mDark ? VISIBLE : INVISIBLE);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index e7e70afa20ce..7403ddc441f6 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -40,6 +40,8 @@ import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.PluginManagerImpl;
import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
@@ -310,6 +312,8 @@ public class Dependency extends SystemUI {
mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
+ mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
index f41425a1f848..5d2e4d09ff43 100644
--- a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
+++ b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
@@ -16,20 +16,14 @@
package com.android.systemui;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
import android.content.Context;
-import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.Region;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
@@ -41,9 +35,6 @@ import android.view.WindowManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import java.util.Collections;
-import java.util.List;
-
/**
* Emulates a display cutout by drawing its shape in an overlay as supplied by
* {@link DisplayCutout}.
@@ -101,7 +92,7 @@ public class EmulatedDisplayCutout extends SystemUI implements ConfigurationList
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
- lp.flags2 |= WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
lp.setTitle("EmulatedDisplayCutout");
lp.gravity = Gravity.TOP;
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
index 6f7a270799bc..c960fa122d50 100644
--- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
+++ b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
@@ -14,6 +14,8 @@
package com.android.systemui;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
@@ -163,7 +165,7 @@ public class RoundedCorners extends SystemUI implements Tunable {
| WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
lp.setTitle("RoundedOverlay");
lp.gravity = Gravity.TOP;
- lp.flags2 |= WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index bfe07a980ce7..0486a9dcca74 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -373,7 +373,7 @@ public class PipMenuActivity extends Activity {
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
} else {
- mMenuContainerAnimator.playTogether(settingsAnim, dismissAnim);
+ mMenuContainerAnimator.playTogether(dismissAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java
new file mode 100644
index 000000000000..8f41a6036072
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java
@@ -0,0 +1,8 @@
+package com.android.systemui.power;
+
+public interface EnhancedEstimates {
+
+ boolean isHybridNotificationEnabled();
+
+ Estimate getEstimate();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java
new file mode 100644
index 000000000000..d447542588c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java
@@ -0,0 +1,16 @@
+package com.android.systemui.power;
+
+import android.util.Log;
+
+public class EnhancedEstimatesImpl implements EnhancedEstimates {
+
+ @Override
+ public boolean isHybridNotificationEnabled() {
+ return false;
+ }
+
+ @Override
+ public Estimate getEstimate() {
+ return null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/Estimate.java b/packages/SystemUI/src/com/android/systemui/power/Estimate.java
new file mode 100644
index 000000000000..12a8f0a435b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/Estimate.java
@@ -0,0 +1,11 @@
+package com.android.systemui.power;
+
+public class Estimate {
+ public final long estimateMillis;
+ public final boolean isBasedOnUsage;
+
+ public Estimate(long estimateMillis, boolean isBasedOnUsage) {
+ this.estimateMillis = estimateMillis;
+ this.isBasedOnUsage = isBasedOnUsage;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c29b362bda13..736286f21bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -17,40 +17,40 @@
package com.android.systemui.power;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
import android.media.AudioAttributes;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
+import android.text.format.DateUtils;
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.NotificationChannels;
import java.io.PrintWriter;
import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
@@ -96,8 +96,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private long mScreenOffTime;
private int mShowing;
- private long mBucketDroppedNegativeTimeMs;
+ private long mWarningTriggerTimeMs;
+ private Estimate mEstimate;
private boolean mWarning;
private boolean mPlaySound;
private boolean mInvalidCharger;
@@ -130,14 +131,22 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
public void update(int batteryLevel, int bucket, long screenOffTime) {
mBatteryLevel = batteryLevel;
if (bucket >= 0) {
- mBucketDroppedNegativeTimeMs = 0;
+ mWarningTriggerTimeMs = 0;
} else if (bucket < mBucket) {
- mBucketDroppedNegativeTimeMs = System.currentTimeMillis();
+ mWarningTriggerTimeMs = System.currentTimeMillis();
}
mBucket = bucket;
mScreenOffTime = screenOffTime;
}
+ @Override
+ public void updateEstimate(Estimate estimate) {
+ mEstimate = estimate;
+ if (estimate.estimateMillis <= PowerUI.THREE_HOURS_IN_MILLIS) {
+ mWarningTriggerTimeMs = System.currentTimeMillis();
+ }
+ }
+
private void updateNotification() {
if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mPlaySound="
+ mPlaySound + " mInvalidCharger=" + mInvalidCharger);
@@ -171,25 +180,43 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, n, UserHandle.ALL);
}
- private void showWarningNotification() {
- final int textRes = R.string.battery_low_percent_format;
+ protected void showWarningNotification() {
final String percentage = NumberFormat.getPercentInstance().format((double) mBatteryLevel / 100.0);
+ // get standard notification copy
+ String title = mContext.getString(R.string.battery_low_title);
+ String contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
+
+ // override notification copy if hybrid notification enabled
+ if (mEstimate != null) {
+ title = mContext.getString(R.string.battery_low_title_hybrid);
+ contentText = mContext.getString(
+ mEstimate.isBasedOnUsage
+ ? R.string.battery_low_percent_format_hybrid
+ : R.string.battery_low_percent_format_hybrid_short,
+ percentage,
+ getTimeRemainingFormatted());
+ }
+
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.BATTERY)
.setSmallIcon(R.drawable.ic_power_low)
// Bump the notification when the bucket dropped.
- .setWhen(mBucketDroppedNegativeTimeMs)
+ .setWhen(mWarningTriggerTimeMs)
.setShowWhen(false)
- .setContentTitle(mContext.getString(R.string.battery_low_title))
- .setContentText(mContext.getString(textRes, percentage))
+ .setContentTitle(title)
+ .setContentText(contentText)
.setOnlyAlertOnce(true)
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
+ .setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasBatterySettings()) {
nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
}
+ // Make the notification red if the percentage goes below a certain amount or the time
+ // remaining estimate is disabled
+ if (mEstimate == null || mBucket < 0) {
+ nb.setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
+ }
nb.addAction(0,
mContext.getString(R.string.battery_saver_start_action),
pendingBroadcast(ACTION_START_SAVER));
@@ -201,6 +228,23 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
}
+ @VisibleForTesting
+ String getTimeRemainingFormatted() {
+ final Locale currentLocale = mContext.getResources().getConfiguration().getLocales().get(0);
+ MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.NARROW);
+
+ final long remainder = mEstimate.estimateMillis % DateUtils.HOUR_IN_MILLIS;
+ final long hours = TimeUnit.MILLISECONDS.toHours(
+ mEstimate.estimateMillis - remainder);
+ // round down to the nearest 15 min for now to not appear overly precise
+ final long minutes = TimeUnit.MILLISECONDS.toMinutes(
+ remainder - (remainder % TimeUnit.MINUTES.toMillis(15)));
+ final Measure hoursMeasure = new Measure(hours, MeasureUnit.HOUR);
+ final Measure minutesMeasure = new Measure(minutes, MeasureUnit.MINUTE);
+
+ return frmt.formatMeasures(hoursMeasure, minutesMeasure);
+ }
+
private PendingIntent pendingBroadcast(String action) {
return PendingIntent.getBroadcastAsUser(mContext,
0, new Intent(action), 0, UserHandle.CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index a351c09f68b7..c5aab601283c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
@@ -59,6 +60,7 @@ public class PowerUI extends SystemUI {
private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
+ static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
private final Handler mHandler = new Handler();
private final Receiver mReceiver = new Receiver();
@@ -68,9 +70,11 @@ public class PowerUI extends SystemUI {
private WarningsUI mWarnings;
private final Configuration mLastConfiguration = new Configuration();
private int mBatteryLevel = 100;
+ private long mTimeRemaining = Long.MAX_VALUE;
private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
private int mPlugType = 0;
private int mInvalidCharger = 0;
+ private EnhancedEstimates mEnhancedEstimates;
private int mLowBatteryAlertCloseLevel;
private final int[] mLowBatteryReminderLevels = new int[2];
@@ -83,8 +87,8 @@ public class PowerUI extends SystemUI {
private long mNextLogTime;
private IThermalService mThermalService;
- // We create a method reference here so that we are guaranteed that we can remove a callback
// by using the same instance (method references are not guaranteed to be the same object
+ // We create a method reference here so that we are guaranteed that we can remove a callback
// each time they are created).
private final Runnable mUpdateTempCallback = this::updateTemperatureWarning;
@@ -94,6 +98,7 @@ public class PowerUI extends SystemUI {
mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
mWarnings = Dependency.get(WarningsUI.class);
+ mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
mLastConfiguration.setTo(mContext.getResources().getConfiguration());
ContentObserver obs = new ContentObserver(mHandler) {
@@ -236,21 +241,9 @@ public class PowerUI extends SystemUI {
return;
}
- boolean isPowerSaver = mPowerManager.isPowerSaveMode();
- if (!plugged
- && !isPowerSaver
- && (bucket < oldBucket || oldPlugged)
- && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && bucket < 0) {
-
- // only play SFX when the dialog comes up or the bucket changes
- final boolean playSound = bucket != oldBucket || oldPlugged;
- mWarnings.showLowBatteryWarning(playSound);
- } else if (isPowerSaver || plugged || (bucket > oldBucket && bucket > 0)) {
- mWarnings.dismissLowBatteryWarning();
- } else {
- mWarnings.updateLowBatteryWarning();
- }
+ // Show the correct version of low battery warning if needed
+ maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
+
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOffTime = SystemClock.elapsedRealtime();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
@@ -261,7 +254,65 @@ public class PowerUI extends SystemUI {
Slog.w(TAG, "unknown intent: " + intent);
}
}
- };
+ }
+
+ protected void maybeShowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
+ int bucket) {
+ boolean isPowerSaver = mPowerManager.isPowerSaveMode();
+ // only play SFX when the dialog comes up or the bucket changes
+ final boolean playSound = bucket != oldBucket || oldPlugged;
+ long oldTimeRemaining = mTimeRemaining;
+ if (mEnhancedEstimates.isHybridNotificationEnabled()) {
+ final Estimate estimate = mEnhancedEstimates.getEstimate();
+ // Turbo is not always booted once SysUI is running so we have ot make sure we actually
+ // get data back
+ if (estimate != null) {
+ mTimeRemaining = estimate.estimateMillis;
+ mWarnings.updateEstimate(estimate);
+ }
+ }
+
+ if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket, oldTimeRemaining,
+ mTimeRemaining,
+ isPowerSaver, mBatteryStatus)) {
+ mWarnings.showLowBatteryWarning(playSound);
+ } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
+ isPowerSaver)) {
+ mWarnings.dismissLowBatteryWarning();
+ } else {
+ mWarnings.updateLowBatteryWarning();
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
+ int bucket, long oldTimeRemaining, long timeRemaining,
+ boolean isPowerSaver, int mBatteryStatus) {
+ return !plugged
+ && !isPowerSaver
+ && (((bucket < oldBucket || oldPlugged) && bucket < 0)
+ || (mEnhancedEstimates.isHybridNotificationEnabled()
+ && timeRemaining < THREE_HOURS_IN_MILLIS
+ && isHourLess(oldTimeRemaining, timeRemaining)))
+ && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
+ }
+
+ private boolean isHourLess(long oldTimeRemaining, long timeRemaining) {
+ final long dif = oldTimeRemaining - timeRemaining;
+ return dif >= TimeUnit.HOURS.toMillis(1);
+ }
+
+ @VisibleForTesting
+ boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
+ long timeRemaining, boolean isPowerSaver) {
+ final boolean hybridWouldDismiss = mEnhancedEstimates.isHybridNotificationEnabled()
+ && timeRemaining > THREE_HOURS_IN_MILLIS;
+ final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
+ return isPowerSaver
+ || plugged
+ || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
+ || hybridWouldDismiss));
+ }
private void initTemperatureWarning() {
ContentResolver resolver = mContext.getContentResolver();
@@ -433,6 +484,7 @@ public class PowerUI extends SystemUI {
public interface WarningsUI {
void update(int batteryLevel, int bucket, long screenOffTime);
+ void updateEstimate(Estimate estimate);
void dismissLowBatteryWarning();
void showLowBatteryWarning(boolean playSound);
void dismissInvalidChargerWarning();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 33b5268e03e1..7f0acc254a7c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -17,13 +17,18 @@
package com.android.systemui.qs;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.ExpandableOutlineView;
/**
* Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -31,6 +36,7 @@ import com.android.systemui.qs.customize.QSCustomizer;
public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
+ private final Path mClipPath = new Path();
private int mHeightOverride = -1;
protected View mQSPanel;
@@ -40,6 +46,7 @@ public class QSContainerImpl extends FrameLayout {
private QSCustomizer mQSCustomizer;
private View mQSFooter;
private float mFullElevation;
+ private float mRadius;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -54,6 +61,8 @@ public class QSContainerImpl extends FrameLayout {
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
mFullElevation = mQSPanel.getElevation();
+ mRadius = getResources().getDimensionPixelSize(
+ Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
setClickable(true);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -93,6 +102,18 @@ public class QSContainerImpl extends FrameLayout {
updateExpansion();
}
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ boolean ret;
+ canvas.save();
+ if (child != mQSCustomizer) {
+ canvas.clipPath(mClipPath);
+ }
+ ret = super.drawChild(canvas, child, drawingTime);
+ canvas.restore();
+ return ret;
+ }
+
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -110,6 +131,10 @@ public class QSContainerImpl extends FrameLayout {
mQSDetail.setBottom(getTop() + height);
// Pin QS Footer to the bottom of the panel.
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
+
+ ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
+ mRadius,
+ mClipPath);
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index c249e3778c0a..0f83078e0738 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -103,7 +103,7 @@ public class QSIconViewImpl extends QSIconView {
if (iv instanceof SlashImageView) {
((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
- ((SlashImageView) iv).setState(state.slash, d);
+ ((SlashImageView) iv).setState(null, d);
} else {
iv.setImageDrawable(d);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 4d0e60d505f6..acd327b8eae2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -13,7 +13,12 @@
*/
package com.android.systemui.qs.tileimpl;
+import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
@@ -22,16 +27,21 @@ import android.os.Looper;
import android.os.Message;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
import android.widget.Switch;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.*;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
@@ -47,6 +57,12 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private boolean mCollapsedView;
private boolean mClicked;
+ private final ImageView mBg;
+ private final int mColorActive;
+ private final int mColorInactive;
+ private final int mColorDisabled;
+ private int mCircleColor;
+
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
}
@@ -60,6 +76,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
mIconFrame.setForegroundGravity(Gravity.CENTER);
int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
addView(mIconFrame, new LayoutParams(size, size));
+ mBg = new ImageView(getContext());
+ mBg.setScaleType(ScaleType.FIT_CENTER);
+ mBg.setImageResource(R.drawable.ic_qs_circle);
+ mIconFrame.addView(mBg);
mIcon = icon;
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -73,6 +93,11 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
setBackground(mTileBackground);
+ mColorActive = Utils.getColorAttr(context, android.R.attr.colorAccent);
+ mColorDisabled = Utils.getDisabled(context,
+ Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+ mColorInactive = Utils.getColorAttr(context, android.R.attr.textColorSecondary);
+
setPadding(0, 0, 0, 0);
setClipChildren(false);
setClipToPadding(false);
@@ -80,6 +105,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
setFocusable(true);
}
+ public View getBgCicle() {
+ return mBg;
+ }
+
protected Drawable newTileBackground() {
final int[] attrs = new int[]{android.R.attr.selectableItemBackgroundBorderless};
final TypedArray ta = getContext().obtainStyledAttributes(attrs);
@@ -150,6 +179,20 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
protected void handleStateChanged(QSTile.State state) {
+ int circleColor = getCircleColor(state.state);
+ if (circleColor != mCircleColor) {
+ if (mBg.isShown()) {
+ ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
+ .setDuration(QS_ANIM_LENGTH);
+ animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
+ (Integer) animation.getAnimatedValue())));
+ animator.start();
+ } else {
+ QSIconViewImpl.setTint(mBg, circleColor);
+ }
+ mCircleColor = circleColor;
+ }
+
setClickable(state.state != Tile.STATE_UNAVAILABLE);
mIcon.setIcon(state);
setContentDescription(state.contentDescription);
@@ -163,6 +206,19 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
}
+ private int getCircleColor(int state) {
+ switch (state) {
+ case Tile.STATE_ACTIVE:
+ return mColorActive;
+ case Tile.STATE_INACTIVE:
+ case Tile.STATE_UNAVAILABLE:
+ return mColorDisabled;
+ default:
+ Log.e(TAG, "Invalid state " + state);
+ return 0;
+ }
+ }
+
@Override
public void setClickable(boolean clickable) {
super.setClickable(clickable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 576a447447b5..7259282935a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -373,11 +373,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile {
switch (state) {
case Tile.STATE_UNAVAILABLE:
return Utils.getDisabled(context,
- Utils.getColorAttr(context, android.R.attr.colorForeground));
+ Utils.getColorAttr(context, android.R.attr.textColorSecondary));
case Tile.STATE_INACTIVE:
- return Utils.getColorAttr(context, android.R.attr.textColorHint);
+ return Utils.getColorAttr(context, android.R.attr.textColorSecondary);
case Tile.STATE_ACTIVE:
- return Utils.getColorAttr(context, android.R.attr.textColorPrimary);
+ return Utils.getColorAttr(context, android.R.attr.colorPrimary);
default:
Log.e("QSTile", "Invalid state " + state);
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 263dac0f44ec..a226f3cac83a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -18,6 +18,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -28,6 +29,7 @@ import android.widget.TextView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.R.id;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -36,8 +38,10 @@ import libcore.util.Objects;
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
+ private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
+ private TextView mSecondLine;
private ImageView mPadLock;
private int mState;
private ViewGroup mLabelContainer;
@@ -86,6 +90,8 @@ public class QSTileView extends QSTileBaseView {
mDivider = mLabelContainer.findViewById(R.id.underline);
mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
+ mSecondLine = mLabelContainer.findViewById(R.id.app_label);
+ mSecondLine.setAlpha(.6f);
addView(mLabelContainer);
}
@@ -103,14 +109,20 @@ public class QSTileView extends QSTileBaseView {
mState = state.state;
mLabel.setText(state.label);
}
- mExpandIndicator.setVisibility(state.dualTarget ? View.VISIBLE : View.GONE);
- mExpandSpace.setVisibility(state.dualTarget ? View.VISIBLE : View.GONE);
- mLabelContainer.setContentDescription(state.dualTarget ? state.dualLabelContentDescription
+ if (!Objects.equal(mSecondLine.getText(), state.secondaryLabel)) {
+ mSecondLine.setText(state.secondaryLabel);
+ mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
+ : View.VISIBLE);
+ }
+ boolean dualTarget = DUAL_TARGET_ALLOWED && state.dualTarget;
+ mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
: null);
- if (state.dualTarget != mLabelContainer.isClickable()) {
- mLabelContainer.setClickable(state.dualTarget);
- mLabelContainer.setLongClickable(state.dualTarget);
- mLabelContainer.setBackground(state.dualTarget ? newTileBackground() : null);
+ if (dualTarget != mLabelContainer.isClickable()) {
+ mLabelContainer.setClickable(dualTarget);
+ mLabelContainer.setLongClickable(dualTarget);
+ mLabelContainer.setBackground(dualTarget ? newTileBackground() : null);
}
mLabel.setEnabled(!state.disabledByPolicy);
mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 0e4a9fe32911..fff9f8e7e038 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -125,11 +125,11 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
state.slash = new SlashState();
}
state.slash.isSlashed = !enabled;
+ state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
if (enabled) {
- state.label = null;
if (connected) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected);
- state.label = mController.getLastDeviceName();
+ state.secondaryLabel = mController.getLastDeviceName();
CachedBluetoothDevice lastDevice = mController.getLastDevice();
if (lastDevice != null) {
int batteryLevel = lastDevice.getBatteryLevel();
@@ -140,25 +140,20 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
}
}
state.contentDescription = mContext.getString(
- R.string.accessibility_bluetooth_name, state.label);
+ R.string.accessibility_bluetooth_name, state.secondaryLabel);
} else if (state.isTransient) {
state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_connecting);
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_on) + ","
+ mContext.getString(R.string.accessibility_not_connected);
}
- if (TextUtils.isEmpty(state.label)) {
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
- }
state.state = Tile.STATE_ACTIVE;
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_off);
state.state = Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index d1e6dcc9e480..bf8a64cee974 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -33,6 +33,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -173,6 +174,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private FalsingManager mFalsingManager;
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
+ private View mHelperButton;
private boolean mJustClicked;
private boolean mIconAnimationRunning;
@@ -387,6 +389,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
updateLimits();
updateIconVisibilities();
updateShelfIconColor();
+
+ showBlockingHelper(mEntry.userSentiment ==
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@VisibleForTesting
@@ -1318,6 +1323,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
requestLayout();
}
+ public void showBlockingHelper(boolean show) {
+ mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -1325,6 +1334,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
+ final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
+ mHelperButton = findViewById(R.id.helper);
+ mHelperButton.setOnClickListener(view -> {
+ doLongClickCallback();
+ });
+
for (NotificationContentView l : mLayouts) {
l.setExpandClickListener(mExpandClickListener);
l.setContainingNotification(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 85400a15dfc5..43047ed6a5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -52,6 +52,8 @@ import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import java.text.NumberFormat;
+
/**
* Controls the indications and error messages shown on the Keyguard
*/
@@ -87,6 +89,7 @@ public class KeyguardIndicationController {
private boolean mPowerCharged;
private int mChargingSpeed;
private int mChargingWattage;
+ private int mBatteryLevel;
private String mMessageToShowOnScreenOn;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -285,14 +288,18 @@ public class KeyguardIndicationController {
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
if (mDozing) {
- // If we're dozing, never show a persistent indication.
+ mTextView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
- mTextView.setTextColor(Color.WHITE);
mTextView.switchIndication(mTransientIndication);
+ } else if (mPowerPluggedIn) {
+ String indication = computePowerIndication();
+ mTextView.switchIndication(indication);
} else {
- mTextView.switchIndication(null);
+ String percentage = NumberFormat.getPercentInstance()
+ .format(mBatteryLevel / 100f);
+ mTextView.switchIndication(percentage);
}
return;
}
@@ -422,6 +429,7 @@ public class KeyguardIndicationController {
mPowerCharged = status.isCharged();
mChargingWattage = status.maxChargingWattage;
mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
+ mBatteryLevel = status.level;
updateIndication();
if (mDozing) {
if (!wasPluggedIn && mPowerPluggedIn) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index d0417b59448d..7e0dba5e129d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -86,6 +86,8 @@ public class NotificationData {
public RemoteViews cachedAmbientContentView;
public CharSequence remoteInputText;
public List<SnoozeCriterion> snoozeCriteria;
+ public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
@@ -463,6 +465,7 @@ public class NotificationData {
}
entry.channel = getChannel(entry.key);
entry.snoozeCriteria = getSnoozeCriteria(entry.key);
+ entry.userSentiment = mTmpRanking.getUserSentiment();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index 6cbbd6cd1f18..e5a311d099d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -17,11 +17,15 @@
package com.android.systemui.statusbar.car;
import android.content.Context;
+import android.graphics.Canvas;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.phone.NavGesture;
+import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
import com.android.systemui.statusbar.phone.NavigationBarView;
/**
@@ -72,4 +76,68 @@ class CarNavigationBarView extends NavigationBarView {
// Calling setNavigationIconHints in the base class will result in a NPE as the car
// navigation bar does not have a back button.
}
+
+ @Override
+ public void onPluginConnected(NavGesture plugin, Context context) {
+ // set to null version of the plugin ignoring incoming arg.
+ super.onPluginConnected(new NullNavGesture(), context);
+ }
+
+ @Override
+ public void onPluginDisconnected(NavGesture plugin) {
+ // reinstall the null nav gesture plugin
+ super.onPluginConnected(new NullNavGesture(), getContext());
+ }
+
+ /**
+ * Null object pattern to work around expectations of the base class.
+ * This is a temporary solution to have the car system ui working.
+ * Already underway is a refactor of they car sys ui as to not use this class
+ * hierarchy.
+ */
+ private static class NullNavGesture implements NavGesture {
+ @Override
+ public GestureHelper getGestureHelper() {
+ return new GestureHelper() {
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public void setBarState(boolean vertical, boolean isRtl) {
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ }
+
+ @Override
+ public void onDarkIntensityChange(float intensity) {
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ }
+ };
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @Override
+ public void onCreate(Context sysuiContext, Context pluginContext) {
+ }
+
+ @Override
+ public void onDestroy() {
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 78ee04048644..9b123cbea8d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -14,7 +14,6 @@
package com.android.systemui.statusbar.phone;
-import android.graphics.drawable.Drawable;
import android.view.View;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
@@ -39,6 +38,7 @@ public class ButtonDispatcher {
private Integer mAlpha;
private Float mDarkIntensity;
private Integer mVisibility = -1;
+ private Boolean mDelayTouchFeedback;
private KeyButtonDrawable mImageDrawable;
private View mCurrentView;
private boolean mVertical;
@@ -71,10 +71,10 @@ public class ButtonDispatcher {
if (mImageDrawable != null) {
((ButtonInterface) view).setImageDrawable(mImageDrawable);
}
-
- if (view instanceof ButtonInterface) {
- ((ButtonInterface) view).setVertical(mVertical);
+ if (mDelayTouchFeedback != null) {
+ ((ButtonInterface) view).setDelayTouchFeedback(mDelayTouchFeedback);
}
+ ((ButtonInterface) view).setVertical(mVertical);
}
public int getId() {
@@ -134,6 +134,14 @@ public class ButtonDispatcher {
}
}
+ public void setDelayTouchFeedback(boolean delay) {
+ mDelayTouchFeedback = delay;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ ((ButtonInterface) mViews.get(i)).setDelayTouchFeedback(delay);
+ }
+ }
+
public void setOnClickListener(View.OnClickListener clickListener) {
mClickListener = clickListener;
final int N = mViews.size();
@@ -166,6 +174,14 @@ public class ButtonDispatcher {
}
}
+ public void setClickable(boolean clickable) {
+ abortCurrentGesture();
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setClickable(clickable);
+ }
+ }
+
public ArrayList<View> getViews() {
return mViews;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 01b3b442f2b6..ca66e987933c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -51,6 +51,7 @@ import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
@@ -166,6 +167,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private String mLeftButtonStr;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mDozing;
+ private int mIndicationBottomMargin;
+ private int mIndicationBottomMarginAmbient;
+ private float mDarkAmount;
+ private int mBurnInXOffset;
public KeyguardBottomAreaView(Context context) {
this(context, null);
@@ -235,6 +240,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mEnterpriseDisclosure = findViewById(
R.id.keyguard_indication_enterprise_disclosure);
mIndicationText = findViewById(R.id.keyguard_indication_text);
+ mIndicationBottomMargin = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom);
+ mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom_ambient);
updateCameraVisibility();
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
@@ -303,11 +312,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- int indicationBottomMargin = getResources().getDimensionPixelSize(
+ mIndicationBottomMargin = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom);
+ mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom_ambient);
MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
- if (mlp.bottomMargin != indicationBottomMargin) {
- mlp.bottomMargin = indicationBottomMargin;
+ if (mlp.bottomMargin != mIndicationBottomMargin) {
+ mlp.bottomMargin = mIndicationBottomMargin;
mIndicationArea.setLayoutParams(mlp);
}
@@ -543,6 +554,22 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
}
+ public void setDarkAmount(float darkAmount) {
+ if (darkAmount == mDarkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ // Let's randomize the bottom margin every time we wake up to avoid burn-in.
+ if (darkAmount == 0) {
+ mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom_ambient)
+ + (int) (Math.random() * mIndicationText.getTextSize());
+ }
+ mIndicationArea.setAlpha(MathUtils.lerp(1f, 0.7f, darkAmount));
+ mIndicationArea.setTranslationY(MathUtils.lerp(0,
+ mIndicationBottomMargin - mIndicationBottomMarginAmbient, darkAmount));
+ }
+
private static boolean isSuccessfulLaunch(int result) {
return result == ActivityManager.START_SUCCESS
|| result == ActivityManager.START_DELIVERED_TO_TOP
@@ -687,11 +714,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
if (mRightAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mRightAffordanceView, delay);
}
- mIndicationArea.setAlpha(0f);
- mIndicationArea.animate()
- .alpha(1f)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
}
private void startFinishDozeAnimationElement(View element, long delay) {
@@ -815,6 +837,22 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
}
+ public void dozeTimeTick() {
+ if (mDarkAmount == 1) {
+ // Move indication every minute to avoid burn-in
+ final int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient;
+ mIndicationArea.setTranslationY(dozeTranslation + (float) Math.random() * 5);
+ }
+ }
+
+ public void setBurnInXOffset(int burnInXOffset) {
+ if (mBurnInXOffset == burnInXOffset) {
+ return;
+ }
+ mBurnInXOffset = burnInXOffset;
+ mIndicationArea.setTranslationX(burnInXOffset);
+ }
+
private class DefaultLeftButton implements IntentButton {
private IconState mIconState = new IconState();
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 34486dbcaf43..264f5749fa73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -250,7 +250,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
}
break;
case STATE_FACE_UNLOCK:
- iconRes = R.drawable.ic_account_circle;
+ iconRes = R.drawable.ic_face_unlock;
break;
case STATE_FINGERPRINT:
// If screen is off and device asleep, use the draw on animation so the first frame
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 6f636aa6299d..4faa84aca099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -19,15 +19,14 @@ package com.android.systemui.statusbar.phone;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
-import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewConfiguration;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -37,6 +36,7 @@ import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
@@ -72,6 +72,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
private NavigationBarView mNavigationBarView;
private boolean mIsVertical;
+ private final QuickScrubController mQuickScrubController;
private final int mScrollTouchSlop;
private final Matrix mTransformGlobalMatrix = new Matrix();
private final Matrix mTransformLocalMatrix = new Matrix();
@@ -89,6 +90,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
mContext = context;
Resources r = context.getResources();
mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
+ mQuickScrubController = new QuickScrubController(context);
Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
}
@@ -101,10 +103,12 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
mRecentsComponent = recentsComponent;
mDivider = divider;
mNavigationBarView = navigationBarView;
+ mQuickScrubController.setComponents(mNavigationBarView);
}
public void setBarState(boolean isVertical, boolean isRTL) {
mIsVertical = isVertical;
+ mQuickScrubController.setBarState(isVertical, isRTL);
}
private boolean proxyMotionEvents(MotionEvent event) {
@@ -126,7 +130,6 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
- boolean result = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mTouchDownX = (int) event.getX();
@@ -137,24 +140,26 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
break;
}
- case MotionEvent.ACTION_MOVE: {
- int x = (int) event.getX();
- int y = (int) event.getY();
- int xDiff = Math.abs(x - mTouchDownX);
- int yDiff = Math.abs(y - mTouchDownY);
- boolean exceededTouchSlop = xDiff > mScrollTouchSlop && xDiff > yDiff
- || yDiff > mScrollTouchSlop && yDiff > xDiff;
- if (exceededTouchSlop) {
- result = true;
- }
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- break;
}
- proxyMotionEvents(event);
- return result || (mDockWindowEnabled && interceptDockWindowEvent(event));
+ if (!mQuickScrubController.onInterceptTouchEvent(event)) {
+ proxyMotionEvents(event);
+ return false;
+ }
+ return (mDockWindowEnabled && interceptDockWindowEvent(event));
+ }
+
+ public void onDraw(Canvas canvas) {
+ if (mOverviewEventSender.getProxy() != null) {
+ mQuickScrubController.onDraw(canvas);
+ }
+ }
+
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mQuickScrubController.onLayout(changed, left, top, right, bottom);
+ }
+
+ public void onDarkIntensityChange(float intensity) {
+ mQuickScrubController.onDarkIntensityChange(intensity);
}
private boolean interceptDockWindowEvent(MotionEvent event) {
@@ -294,7 +299,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
}
public boolean onTouchEvent(MotionEvent event) {
- boolean result = proxyMotionEvents(event);
+ boolean result = mQuickScrubController.onTouchEvent(event) || proxyMotionEvents(event);
if (mDockWindowEnabled) {
result |= handleDockWindowEvent(event);
}
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 bd6421c20980..b11367523c08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -138,6 +138,7 @@ public final class NavigationBarTransitions extends BarTransitions {
if (mAutoDim) {
applyLightsOut(false, true);
}
+ mView.onDarkIntensityChange(darkIntensity);
}
private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
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 006a85b7ef8c..059ce929b290 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -26,6 +26,7 @@ import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
@@ -625,6 +626,24 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
updateRotatedViews();
}
+ public void onDarkIntensityChange(float intensity) {
+ if (mGestureHelper != null) {
+ mGestureHelper.onDarkIntensityChange(intensity);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mGestureHelper.onDraw(canvas);
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mGestureHelper.onLayout(changed, left, top, right, bottom);
+ }
+
private void updateRotatedViews() {
mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 32675d3b2aac..0cc7f5dfbae1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -478,6 +478,7 @@ public class NotificationPanelView extends PanelView implements
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
mNotificationStackScroller.setDarkShelfOffsetX(mClockPositionResult.clockX);
+ mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
requestScrollerTopPaddingUpdate(animate);
}
@@ -2608,7 +2609,8 @@ public class NotificationPanelView extends PanelView implements
private void setDarkAmount(float amount) {
mDarkAmount = amount;
- mKeyguardStatusView.setDark(mDarkAmount);
+ mKeyguardStatusView.setDarkAmount(mDarkAmount);
+ mKeyguardBottomArea.setDarkAmount(mDarkAmount);
positionClockAndNotifications();
}
@@ -2630,8 +2632,9 @@ public class NotificationPanelView extends PanelView implements
}
}
- public void refreshTime() {
+ public void dozeTimeTick() {
mKeyguardStatusView.refreshTime();
+ mKeyguardBottomArea.dozeTimeTick();
if (mDarkAmount > 0) {
positionClockAndNotifications();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
new file mode 100644
index 000000000000..9f8a7efa04f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.support.annotation.DimenRes;
+import com.android.systemui.Dependency;
+import com.android.systemui.OverviewProxyService;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.utilities.Utilities;
+
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+
+/**
+ * Class to detect gestures on the navigation bar and implement quick scrub and switch.
+ */
+public class QuickScrubController extends GestureDetector.SimpleOnGestureListener implements
+ GestureHelper {
+
+ private static final String TAG = "QuickScrubController";
+ private static final int QUICK_SWITCH_FLING_VELOCITY = 0;
+ private static final int ANIM_DURATION_MS = 200;
+ private static final long LONG_PRESS_DELAY_MS = 150;
+
+ /**
+ * For quick step, set a damping value to allow the button to stick closer its origin position
+ * when dragging before quick scrub is active.
+ */
+ private static final int SWITCH_STICKINESS = 4;
+
+ private NavigationBarView mNavigationBarView;
+ private GestureDetector mGestureDetector;
+
+ private boolean mDraggingActive;
+ private boolean mQuickScrubActive;
+ private float mDownOffset;
+ private float mTranslation;
+ private int mTouchDownX;
+ private int mTouchDownY;
+ private boolean mDragPositive;
+ private boolean mIsVertical;
+ private boolean mIsRTL;
+ private float mMaxTrackPaintAlpha;
+
+ private final Handler mHandler = new Handler();
+ private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator();
+ private final Rect mTrackRect = new Rect();
+ private final Rect mHomeButtonRect = new Rect();
+ private final Paint mTrackPaint = new Paint();
+ private final int mScrollTouchSlop;
+ private final OverviewProxyService mOverviewEventSender;
+ private final Display mDisplay;
+ private final int mTrackThickness;
+ private final int mTrackPadding;
+ private final ValueAnimator mTrackAnimator;
+ private final ValueAnimator mButtonAnimator;
+ private final AnimatorSet mQuickScrubEndAnimator;
+ private final Context mContext;
+
+ private final AnimatorUpdateListener mTrackAnimatorListener = valueAnimator -> {
+ mTrackPaint.setAlpha(Math.round((float) valueAnimator.getAnimatedValue() * 255));
+ mNavigationBarView.invalidate();
+ };
+
+ private final AnimatorUpdateListener mButtonTranslationListener = animator -> {
+ int pos = (int) animator.getAnimatedValue();
+ if (!mQuickScrubActive) {
+ pos = mDragPositive ? Math.min((int) mTranslation, pos) : Math.max((int) mTranslation, pos);
+ }
+ final View homeView = mNavigationBarView.getHomeButton().getCurrentView();
+ if (mIsVertical) {
+ homeView.setTranslationY(pos);
+ } else {
+ homeView.setTranslationX(pos);
+ }
+ };
+
+ private AnimatorListenerAdapter mQuickScrubEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mNavigationBarView.getHomeButton().setClickable(true);
+ mQuickScrubActive = false;
+ mTranslation = 0;
+ }
+ };
+
+ private Runnable mLongPressRunnable = this::startQuickScrub;
+
+ private final GestureDetector.SimpleOnGestureListener mGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
+ if (mQuickScrubActive) {
+ return false;
+ }
+ float velocityX = mIsRTL ? -velX : velX;
+ float absVelY = Math.abs(velY);
+ final boolean isValidFling = velocityX > QUICK_SWITCH_FLING_VELOCITY &&
+ mIsVertical ? (absVelY > velocityX) : (velocityX > absVelY);
+ if (isValidFling) {
+ mDraggingActive = false;
+ mButtonAnimator.setIntValues((int) mTranslation, 0);
+ mButtonAnimator.start();
+ mHandler.removeCallbacks(mLongPressRunnable);
+ try {
+ final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ overviewProxy.onQuickSwitch();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send start of quick switch.", e);
+ }
+ }
+ return true;
+ }
+ };
+
+ public QuickScrubController(Context context) {
+ mContext = context;
+ mScrollTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mDisplay = ((WindowManager) context.getSystemService(
+ Context.WINDOW_SERVICE)).getDefaultDisplay();
+ mOverviewEventSender = Dependency.get(OverviewProxyService.class);
+ mGestureDetector = new GestureDetector(mContext, mGestureListener);
+ mTrackThickness = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_thickness);
+ mTrackPadding = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_edge_padding);
+
+ mTrackAnimator = ObjectAnimator.ofFloat();
+ mTrackAnimator.addUpdateListener(mTrackAnimatorListener);
+ mButtonAnimator = ObjectAnimator.ofInt();
+ mButtonAnimator.addUpdateListener(mButtonTranslationListener);
+ mQuickScrubEndAnimator = new AnimatorSet();
+ mQuickScrubEndAnimator.playTogether(mTrackAnimator, mButtonAnimator);
+ mQuickScrubEndAnimator.setDuration(ANIM_DURATION_MS);
+ mQuickScrubEndAnimator.addListener(mQuickScrubEndListener);
+ mQuickScrubEndAnimator.setInterpolator(mQuickScrubEndInterpolator);
+ }
+
+ public void setComponents(NavigationBarView navigationBarView) {
+ mNavigationBarView = navigationBarView;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
+ if (overviewProxy == null) {
+ homeButton.setDelayTouchFeedback(false);
+ return false;
+ }
+ mGestureDetector.onTouchEvent(event);
+ int action = event.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ if (mHomeButtonRect.contains(x, y)) {
+ mTouchDownX = x;
+ mTouchDownY = y;
+ homeButton.setDelayTouchFeedback(true);
+ mHandler.postDelayed(mLongPressRunnable, LONG_PRESS_DELAY_MS);
+ } else {
+ mTouchDownX = mTouchDownY = -1;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ if (mTouchDownX != -1) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ int xDiff = Math.abs(x - mTouchDownX);
+ int yDiff = Math.abs(y - mTouchDownY);
+ boolean exceededTouchSlop;
+ int pos, touchDown, offset, trackSize;
+ if (mIsVertical) {
+ exceededTouchSlop = yDiff > mScrollTouchSlop && yDiff > xDiff;
+ pos = y;
+ touchDown = mTouchDownY;
+ offset = pos - mTrackRect.top;
+ trackSize = mTrackRect.height();
+ } else {
+ exceededTouchSlop = xDiff > mScrollTouchSlop && xDiff > yDiff;
+ pos = x;
+ touchDown = mTouchDownX;
+ offset = pos - mTrackRect.left;
+ trackSize = mTrackRect.width();
+ }
+ if (!mDragPositive) {
+ offset -= mIsVertical ? mTrackRect.height() : mTrackRect.width();
+ }
+
+ // Control the button movement
+ if (!mDraggingActive && exceededTouchSlop) {
+ boolean allowDrag = !mDragPositive
+ ? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
+ if (allowDrag) {
+ mDownOffset = offset;
+ homeButton.setClickable(false);
+ mDraggingActive = true;
+ }
+ }
+ if (mDraggingActive && (mDragPositive && offset >= 0
+ || !mDragPositive && offset <= 0)) {
+ float scrubFraction =
+ Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
+ mTranslation = !mDragPositive
+ ? Utilities.clamp(offset - mDownOffset, -trackSize, 0)
+ : Utilities.clamp(offset - mDownOffset, 0, trackSize);
+ if (mQuickScrubActive) {
+ try {
+ overviewProxy.onQuickScrubProgress(scrubFraction);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send progress of quick scrub.", e);
+ }
+ } else {
+ mTranslation /= SWITCH_STICKINESS;
+ }
+ if (mIsVertical) {
+ homeButton.getCurrentView().setTranslationY(mTranslation);
+ } else {
+ homeButton.getCurrentView().setTranslationX(mTranslation);
+ }
+ }
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ endQuickScrub();
+ break;
+ }
+ return mDraggingActive || mQuickScrubActive;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ canvas.drawRect(mTrackRect, mTrackPaint);
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int width = right - left;
+ final int height = bottom - top;
+ final int x1, x2, y1, y2;
+ if (mIsVertical) {
+ x1 = (width - mTrackThickness) / 2;
+ x2 = x1 + mTrackThickness;
+ y1 = mDragPositive ? height / 2 : mTrackPadding;
+ y2 = y1 + height / 2 - mTrackPadding;
+ } else {
+ y1 = (height - mTrackThickness) / 2;
+ y2 = y1 + mTrackThickness;
+ x1 = mDragPositive ? width / 2 : mTrackPadding;
+ x2 = x1 + width / 2 - mTrackPadding;
+ }
+ mTrackRect.set(x1, y1, x2, y2);
+
+ // Get the touch rect of the home button location
+ View homeView = mNavigationBarView.getHomeButton().getCurrentView();
+ int[] globalHomePos = homeView.getLocationOnScreen();
+ int[] globalNavBarPos = mNavigationBarView.getLocationOnScreen();
+ int homeX = globalHomePos[0] - globalNavBarPos[0];
+ int homeY = globalHomePos[1] - globalNavBarPos[1];
+ mHomeButtonRect.set(homeX, homeY, homeX + homeView.getMeasuredWidth(),
+ homeY + homeView.getMeasuredHeight());
+ }
+
+ @Override
+ public void onDarkIntensityChange(float intensity) {
+ if (intensity == 0) {
+ mTrackPaint.setColor(mContext.getColor(R.color.quick_step_track_background_light));
+ } else if (intensity == 1) {
+ mTrackPaint.setColor(mContext.getColor(R.color.quick_step_track_background_dark));
+ }
+ mMaxTrackPaintAlpha = mTrackPaint.getAlpha() * 1f / 255;
+ mTrackPaint.setAlpha(0);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ endQuickScrub();
+ }
+ return false;
+ }
+
+ @Override
+ public void setBarState(boolean isVertical, boolean isRTL) {
+ mIsVertical = isVertical;
+ mIsRTL = isRTL;
+ try {
+ int navbarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ mDragPositive = navbarPos == NAV_BAR_LEFT || navbarPos == NAV_BAR_BOTTOM;
+ if (isRTL) {
+ mDragPositive = !mDragPositive;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get nav bar position.", e);
+ }
+ }
+
+ private void startQuickScrub() {
+ if (!mQuickScrubActive) {
+ mQuickScrubActive = true;
+ mTrackAnimator.setFloatValues(0, mMaxTrackPaintAlpha);
+ mTrackAnimator.start();
+ try {
+ mOverviewEventSender.getProxy().onQuickScrubStart();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send start of quick scrub.", e);
+ }
+ }
+ }
+
+ private void endQuickScrub() {
+ mHandler.removeCallbacks(mLongPressRunnable);
+ if (mDraggingActive || mQuickScrubActive) {
+ mButtonAnimator.setIntValues((int) mTranslation, 0);
+ mTrackAnimator.setFloatValues(mTrackPaint.getAlpha() * 1f / 255, 0);
+ mQuickScrubEndAnimator.start();
+ try {
+ mOverviewEventSender.getProxy().onQuickScrubEnd();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send end of quick scrub.", e);
+ }
+ }
+ mDraggingActive = false;
+ }
+
+ private int getDimensionPixelSize(Context context, @DimenRes int resId) {
+ return context.getResources().getDimensionPixelSize(resId);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 2da1e4d108b4..af65a86676e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1596,7 +1596,7 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean hasArtwork = artworkDrawable != null;
- if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
+ if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && !mDozing
&& (mState != StatusBarState.SHADE || allowWhenShade)
&& mFingerprintUnlockController.getMode()
!= FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
@@ -1657,15 +1657,16 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
} else {
- // need to hide the album art, either because we are unlocked or because
- // the metadata isn't there to support it
+ // need to hide the album art, either because we are unlocked, on AOD
+ // or because the metadata isn't there to support it
if (mBackdrop.getVisibility() != View.GONE) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
}
+ boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange();
if (mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- || hideBecauseOccluded) {
+ || hideBecauseOccluded || cannotAnimateDoze) {
// We are unlocking directly - no animation!
mBackdrop.setVisibility(View.GONE);
@@ -4644,7 +4645,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void dozeTimeTick() {
- mNotificationPanel.refreshTime();
+ mNotificationPanel.dozeTimeTick();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index cc7943b85bc1..a2bec982ed58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -26,9 +26,11 @@ import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
import com.android.systemui.Interpolators;
@@ -56,14 +58,17 @@ public class KeyButtonRipple extends Drawable {
private float mGlowAlpha = 0f;
private float mGlowScale = 1f;
private boolean mPressed;
+ private boolean mVisible;
private boolean mDrawingHardwareGlow;
private int mMaxWidth;
private boolean mLastDark;
private boolean mDark;
+ private boolean mDelayTouchFeedback;
private final Interpolator mInterpolator = new LogInterpolator();
private boolean mSupportHardware;
private final View mTargetView;
+ private final Handler mHandler = new Handler();
private final HashSet<Animator> mRunningAnimations = new HashSet<>();
private final ArrayList<Animator> mTmpArray = new ArrayList<>();
@@ -77,6 +82,10 @@ public class KeyButtonRipple extends Drawable {
mDark = darkIntensity >= 0.5f;
}
+ public void setDelayTouchFeedback(boolean delay) {
+ mDelayTouchFeedback = delay;
+ }
+
private Paint getRipplePaint() {
if (mRipplePaint == null) {
mRipplePaint = new Paint();
@@ -211,7 +220,16 @@ public class KeyButtonRipple extends Drawable {
}
}
+ /**
+ * Abort the ripple while it is delayed and before shown used only when setShouldDelayStartTouch
+ * is enabled.
+ */
+ public void abortDelayedRipple() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
private void cancelAnimations() {
+ mVisible = false;
mTmpArray.addAll(mRunningAnimations);
int size = mTmpArray.size();
for (int i = 0; i < size; i++) {
@@ -220,11 +238,21 @@ public class KeyButtonRipple extends Drawable {
}
mTmpArray.clear();
mRunningAnimations.clear();
+ mHandler.removeCallbacksAndMessages(null);
}
private void setPressedSoftware(boolean pressed) {
if (pressed) {
- enterSoftware();
+ if (mDelayTouchFeedback) {
+ if (mRunningAnimations.isEmpty()) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout());
+ } else if (mVisible) {
+ enterSoftware();
+ }
+ } else {
+ enterSoftware();
+ }
} else {
exitSoftware();
}
@@ -232,6 +260,7 @@ public class KeyButtonRipple extends Drawable {
private void enterSoftware() {
cancelAnimations();
+ mVisible = true;
mGlowAlpha = getMaxGlowAlpha();
ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
0f, GLOW_MAX_SCALE_FACTOR);
@@ -240,6 +269,12 @@ public class KeyButtonRipple extends Drawable {
scaleAnimator.addListener(mAnimatorListener);
scaleAnimator.start();
mRunningAnimations.add(scaleAnimator);
+
+ // With the delay, it could eventually animate the enter animation with no pressed state,
+ // then immediately show the exit animation. If this is skipped there will be no ripple.
+ if (mDelayTouchFeedback && !mPressed) {
+ exitSoftware();
+ }
}
private void exitSoftware() {
@@ -253,7 +288,16 @@ public class KeyButtonRipple extends Drawable {
private void setPressedHardware(boolean pressed) {
if (pressed) {
- enterHardware();
+ if (mDelayTouchFeedback) {
+ if (mRunningAnimations.isEmpty()) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout());
+ } else if (mVisible) {
+ enterHardware();
+ }
+ } else {
+ enterHardware();
+ }
} else {
exitHardware();
}
@@ -302,6 +346,7 @@ public class KeyButtonRipple extends Drawable {
private void enterHardware() {
cancelAnimations();
+ mVisible = true;
mDrawingHardwareGlow = true;
setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(),
@@ -343,6 +388,12 @@ public class KeyButtonRipple extends Drawable {
mRunningAnimations.add(endAnim);
invalidateSelf();
+
+ // With the delay, it could eventually animate the enter animation with no pressed state,
+ // then immediately show the exit animation. If this is skipped there will be no ripple.
+ if (mDelayTouchFeedback && !mPressed) {
+ exitHardware();
+ }
}
private void exitHardware() {
@@ -366,6 +417,7 @@ public class KeyButtonRipple extends Drawable {
public void onAnimationEnd(Animator animation) {
mRunningAnimations.remove(animation);
if (mRunningAnimations.isEmpty() && !mPressed) {
+ mVisible = false;
mDrawingHardwareGlow = false;
invalidateSelf();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 05017718d42b..077c6c38c516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -284,6 +284,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
@Override
public void abortCurrentGesture() {
setPressed(false);
+ mRipple.abortDelayedRipple();
mGestureAborted = true;
}
@@ -301,6 +302,11 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
}
@Override
+ public void setDelayTouchFeedback(boolean shouldDelay) {
+ mRipple.setDelayTouchFeedback(shouldDelay);
+ }
+
+ @Override
public void setVertical(boolean vertical) {
//no op
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
new file mode 100644
index 000000000000..1dcdf6325809
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -0,0 +1,63 @@
+package com.android.systemui.statusbar.policy;
+
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+/** View which displays smart reply buttons in notifications. */
+public class SmartReplyView extends LinearLayout {
+
+ private static final String TAG = "SmartReplyView";
+
+ public SmartReplyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent) {
+ removeAllViews();
+ if (remoteInput != null && pendingIntent != null) {
+ CharSequence[] choices = remoteInput.getChoices();
+ if (choices != null) {
+ for (CharSequence choice : choices) {
+ Button replyButton = inflateReplyButton(
+ getContext(), this, choice, remoteInput, pendingIntent);
+ addView(replyButton);
+ }
+ }
+ }
+ }
+
+ public static SmartReplyView inflate(Context context, ViewGroup root) {
+ return (SmartReplyView)
+ LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false);
+ }
+
+ private static Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
+ RemoteInput remoteInput, PendingIntent pendingIntent) {
+ Button b = (Button) LayoutInflater.from(context).inflate(
+ R.layout.smart_reply_button, root, false);
+ b.setText(choice);
+ b.setOnClickListener(view -> {
+ Bundle results = new Bundle();
+ results.putString(remoteInput.getResultKey(), choice.toString());
+ Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+ try {
+ pendingIntent.send(context, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Unable to send smart reply", e);
+ }
+ });
+ return b;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index bc98140f5af5..efa8386802e2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -52,7 +52,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent";
public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
- public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = true;
+ public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = false;
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = true;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = true;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7b91f1475ca2..5a19a7677649 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -20,6 +20,7 @@ import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
+import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -30,14 +31,13 @@ import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Debug;
@@ -45,9 +45,8 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.provider.Settings.Global;
-import android.transition.AutoTransition;
-import android.transition.TransitionManager;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -72,7 +71,6 @@ import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
@@ -104,7 +102,6 @@ public class VolumeDialogImpl implements VolumeDialog {
private CustomDialog mDialog;
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
- private ImageButton mExpandButton;
private ImageButton mRingerIcon;
private ImageButton mOutputChooser;
private TextView mRingerStatus;
@@ -120,8 +117,6 @@ public class VolumeDialogImpl implements VolumeDialog {
private final ColorStateList mInactiveSliderTint;
private boolean mShowing;
- private boolean mExpanded;
- private boolean mExpandButtonAnimationRunning;
private boolean mShowA11yStream;
private int mActiveStream;
@@ -182,11 +177,11 @@ public class VolumeDialogImpl implements VolumeDialog {
mDialog.setContentView(R.layout.volume_dialog);
mDialog.setOnShowListener(dialog -> {
- mDialogView.setTranslationY(-mDialogView.getHeight());
+ mDialogView.setTranslationX(mDialogView.getWidth() / 2);
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
- .translationY(0)
+ .translationX(0)
.setDuration(300)
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
@@ -205,20 +200,10 @@ public class VolumeDialogImpl implements VolumeDialog {
VolumeUiLayout hardwareLayout = VolumeUiLayout.get(mDialogView);
hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
- ViewGroup dialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
- mDialogRowsView = dialogContentView.findViewById(R.id.volume_dialog_rows);
+ mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
mRingerIcon = mDialog.findViewById(R.id.ringer_icon);
mRingerStatus = mDialog.findViewById(R.id.ringer_status);
- mExpanded = false;
- mExpandButton = mDialogView.findViewById(R.id.volume_expand_button);
- mExpandButton.setOnClickListener(mClickExpand);
- mExpandButton.setVisibility(
- AudioSystem.isSingleVolume(mContext) ? View.GONE : View.VISIBLE);
-
- mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
- mOutputChooser.setOnClickListener(mClickOutputChooser);
-
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
@@ -239,6 +224,10 @@ public class VolumeDialogImpl implements VolumeDialog {
} else {
addExistingRows();
}
+
+ mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
+ mOutputChooser.setOnClickListener(mClickOutputChooser);
+
updateRowsH(getActiveRow());
initRingerH();
}
@@ -273,11 +262,9 @@ public class VolumeDialogImpl implements VolumeDialog {
VolumeRow row = new VolumeRow();
initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
int rowSize;
- int viewSize;
- if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1
- && (viewSize = mDialogRowsView.getChildCount()) > 1) {
- // A11y Stream should be the last in the list
- mDialogRowsView.addView(row.view, viewSize - 2);
+ if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1) {
+ // A11y Stream should be the first in the list, so it's shown to start of other rows
+ mDialogRowsView.addView(row.view, 0);
mRows.add(rowSize - 2, row);
} else {
mDialogRowsView.addView(row.view);
@@ -315,7 +302,6 @@ public class VolumeDialogImpl implements VolumeDialog {
public void dump(PrintWriter writer) {
writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
writer.print(" mShowing: "); writer.println(mShowing);
- writer.print(" mExpanded: "); writer.println(mExpanded);
writer.print(" mActiveStream: "); writer.println(mActiveStream);
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mAutomute: "); writer.println(mAutomute);
@@ -432,6 +418,13 @@ public class VolumeDialogImpl implements VolumeDialog {
}
updateRingerH();
});
+ mRingerIcon.setOnLongClickListener(v -> {
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+ mContext.startActivity(intent);
+ return true;
+ });
updateRingerH();
}
@@ -468,7 +461,6 @@ public class VolumeDialogImpl implements VolumeDialog {
private int computeTimeoutH() {
if (mAccessibility.mFeedbackEnabled) return 20000;
if (mHovering) return 16000;
- if (mExpanded) return 5000;
if (mSafetyWarning != null) return 5000;
return 3000;
}
@@ -480,13 +472,11 @@ public class VolumeDialogImpl implements VolumeDialog {
mDialogView.animate().cancel();
mShowing = false;
- updateExpandedH(false /* expanding */, true /* dismissing */);
-
- mDialogView.setTranslationY(0);
+ mDialogView.setTranslationX(0);
mDialogView.setAlpha(1);
mDialogView.animate()
.alpha(0)
- .translationY(-mDialogView.getHeight())
+ .translationX(mDialogView.getWidth() / 2)
.setDuration(250)
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
.withEndAction(() -> mHandler.postDelayed(() -> {
@@ -514,67 +504,6 @@ public class VolumeDialogImpl implements VolumeDialog {
}
}
- private void updateExpandedH(final boolean expanded, final boolean dismissing) {
- if (D.BUG) Log.d(TAG, "updateExpandedH " + expanded);
-
- if (mExpanded == expanded) return;
- mExpanded = expanded;
- mExpandButtonAnimationRunning = isAttached();
- updateExpandButtonH();
- TransitionManager.endTransitions(mDialogView);
- final VolumeRow activeRow = getActiveRow();
- if (!dismissing) {
- mWindow.setLayout(mWindow.getAttributes().width, ViewGroup.LayoutParams.MATCH_PARENT);
- TransitionManager.beginDelayedTransition(mDialogView, getTransition());
- }
- updateRowsH(activeRow);
- rescheduleTimeoutH();
- }
-
- private AutoTransition getTransition() {
- AutoTransition transition = new AutoTransition();
- transition.setDuration(300);
- transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- return transition;
- }
-
- private void updateExpandButtonH() {
- if (D.BUG) Log.d(TAG, "updateExpandButtonH");
-
- mExpandButton.setClickable(!mExpandButtonAnimationRunning);
- if (!(mExpandButtonAnimationRunning && isAttached())) {
- final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
- : R.drawable.ic_volume_expand_animation;
- if (hasTouchFeature()) {
- mExpandButton.setImageResource(res);
- } else {
- // if there is no touch feature, show the volume ringer instead
- mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
- mExpandButton.setBackgroundResource(0); // remove gray background emphasis
- }
- mExpandButton.setContentDescription(mContext.getString(mExpanded ?
- R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
- }
- if (mExpandButtonAnimationRunning) {
- final Drawable d = mExpandButton.getDrawable();
- if (d instanceof AnimatedVectorDrawable) {
- // workaround to reset drawable
- final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d.getConstantState()
- .newDrawable();
- mExpandButton.setImageDrawable(avd);
- avd.start();
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mExpandButtonAnimationRunning = false;
- updateExpandButtonH();
- rescheduleTimeoutH();
- }
- }, 300);
- }
- }
- }
-
private boolean isAttached() {
return mDialogView != null && mDialogView.isAttachedToWindow();
}
@@ -597,7 +526,7 @@ public class VolumeDialogImpl implements VolumeDialog {
return true;
}
- return row.defaultStream || isActive || (mExpanded && row.important);
+ return row.defaultStream || isActive;
}
private void updateRowsH(final VolumeRow activeRow) {
@@ -954,16 +883,6 @@ public class VolumeDialogImpl implements VolumeDialog {
}
}
- private final OnClickListener mClickExpand = new OnClickListener() {
- @Override
- public void onClick(View v) {
- mExpandButton.animate().cancel();
- final boolean newExpand = !mExpanded;
- Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand);
- updateExpandedH(newExpand, false /* dismissing */);
- }
- };
-
private final OnClickListener mClickOutputChooser = new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index 49ac9b6b7ffe..1c9cbc139504 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -14,15 +14,37 @@
package com.android.systemui.volume;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
public class VolumeUiLayout extends FrameLayout {
+ private View mChild;
+ private int mOldHeight;
+ private boolean mAnimating;
+ private AnimatorSet mAnimation;
+ private boolean mHasOutsideTouch;
+ private int mRotation = ROTATION_NONE;
public VolumeUiLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -40,11 +62,245 @@ public class VolumeUiLayout extends FrameLayout {
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mChild == null) {
+ if (getChildCount() != 0) {
+ mChild = getChildAt(0);
+ mOldHeight = mChild.getMeasuredHeight();
+ updateRotation();
+ } else {
+ return;
+ }
+ }
+ int newHeight = mChild.getMeasuredHeight();
+ if (newHeight != mOldHeight) {
+ animateChild(mOldHeight, newHeight);
+ }
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateRotation();
+ }
+
+ private void updateRotation() {
+ int rotation = RotationUtils.getRotation(getContext());
+ if (rotation != mRotation) {
+ rotate(mRotation, rotation);
+ mRotation = rotation;
+ }
+ }
+
+ private void rotate(View view, int from, int to, boolean swapDimens) {
+ if (from != ROTATION_NONE && to != ROTATION_NONE) {
+ // Rather than handling this confusing case, just do 2 rotations.
+ rotate(view, from, ROTATION_NONE, swapDimens);
+ rotate(view, ROTATION_NONE, to, swapDimens);
+ return;
+ }
+ if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) {
+ rotateRight(view);
+ } else {
+ rotateLeft(view);
+ }
+ if (to != ROTATION_NONE) {
+ if (swapDimens && view instanceof LinearLayout) {
+ LinearLayout linearLayout = (LinearLayout) view;
+ linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ swapDimens(view);
+ }
+ } else {
+ if (swapDimens && view instanceof LinearLayout) {
+ LinearLayout linearLayout = (LinearLayout) view;
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ swapDimens(view);
+ }
+ }
+ }
+
+ private void rotate(int from, int to) {
+ View footer = mChild.findViewById(R.id.footer);
+ rotate(footer, from, to, false);
+ rotate(this, from, to, true);
+ rotate(mChild, from, to, true);
+ ViewGroup rows = mChild.findViewById(R.id.volume_dialog_rows);
+ rotate(rows, from, to, true);
+ int rowCount = rows.getChildCount();
+ for (int i = 0; i < rowCount; i++) {
+ View child = rows.getChildAt(i);
+ if (to == ROTATION_SEASCAPE) {
+ rotateSeekBars(to, 0);
+ } else if (to == ROTATION_LANDSCAPE) {
+ rotateSeekBars(to, 180);
+ } else {
+ rotateSeekBars(to, 270);
+ }
+ rotate(child, from, to, true);
+ }
+ }
+
+ private void swapDimens(View v) {
+ if (v == null) {
+ return;
+ }
+ ViewGroup.LayoutParams params = v.getLayoutParams();
+ int h = params.width;
+ params.width = params.height;
+ params.height = h;
+ v.setLayoutParams(params);
+ }
+
+ private void rotateSeekBars(int to, int rotation) {
+ SeekBar seekbar = mChild.findViewById(R.id.volume_row_slider);
+ if (seekbar != null) {
+ seekbar.setRotation((float) rotation);
+ }
+
+ View parent = mChild.findViewById(R.id.volume_row_slider_frame);
+ swapDimens(parent);
+ ViewGroup.LayoutParams params = seekbar.getLayoutParams();
+ ViewGroup.LayoutParams parentParams = parent.getLayoutParams();
+ if (to != ROTATION_NONE) {
+ params.height = parentParams.height;
+ params.width = parentParams.width;
+ } else {
+ params.height = parentParams.width;
+ params.width = parentParams.height;
+ }
+ seekbar.setLayoutParams(params);
+ }
+
+ private int rotateGravityRight(int gravity) {
+ int retGravity = 0;
+ int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+ final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ retGravity |= Gravity.CENTER_VERTICAL;
+ break;
+ case Gravity.RIGHT:
+ retGravity |= Gravity.BOTTOM;
+ break;
+ case Gravity.LEFT:
+ default:
+ retGravity |= Gravity.TOP;
+ break;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.CENTER_VERTICAL:
+ retGravity |= Gravity.CENTER_HORIZONTAL;
+ break;
+ case Gravity.BOTTOM:
+ retGravity |= Gravity.LEFT;
+ break;
+ case Gravity.TOP:
+ default:
+ retGravity |= Gravity.RIGHT;
+ break;
+ }
+ return retGravity;
+ }
+
+ private int rotateGravityLeft(int gravity) {
+ if (gravity == -1) {
+ gravity = Gravity.TOP | Gravity.START;
+ }
+ int retGravity = 0;
+ int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+ final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ retGravity |= Gravity.CENTER_VERTICAL;
+ break;
+ case Gravity.RIGHT:
+ retGravity |= Gravity.TOP;
+ break;
+ case Gravity.LEFT:
+ default:
+ retGravity |= Gravity.BOTTOM;
+ break;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.CENTER_VERTICAL:
+ retGravity |= Gravity.CENTER_HORIZONTAL;
+ break;
+ case Gravity.BOTTOM:
+ retGravity |= Gravity.RIGHT;
+ break;
+ case Gravity.TOP:
+ default:
+ retGravity |= Gravity.LEFT;
+ break;
+ }
+ return retGravity;
+ }
+
+ private void rotateLeft(View v) {
+ if (v.getParent() instanceof FrameLayout) {
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ p.gravity = rotateGravityLeft(p.gravity);
+ }
+
+ v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(),
+ v.getPaddingLeft());
+ MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+ params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin,
+ params.leftMargin);
+ v.setLayoutParams(params);
+ }
+
+ private void rotateRight(View v) {
+ if (v.getParent() instanceof FrameLayout) {
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ p.gravity = rotateGravityRight(p.gravity);
+ }
+
+ v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(),
+ v.getPaddingRight());
+ MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+ params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin,
+ params.rightMargin);
+ v.setLayoutParams(params);
+ }
+
+ private void animateChild(int oldHeight, int newHeight) {
+ if (true) return;
+ if (mAnimating) {
+ mAnimation.cancel();
+ }
+ mAnimating = true;
+ mAnimation = new AnimatorSet();
+ mAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimating = false;
+ }
+ });
+ int fromTop = mChild.getTop();
+ int fromBottom = mChild.getBottom();
+ int toTop = fromTop - ((newHeight - oldHeight) / 2);
+ int toBottom = fromBottom + ((newHeight - oldHeight) / 2);
+ ObjectAnimator top = ObjectAnimator.ofInt(mChild, "top", fromTop, toTop);
+ mAnimation.playTogether(top,
+ ObjectAnimator.ofInt(mChild, "bottom", fromBottom, toBottom));
+ }
+
+
+ @Override
public ViewOutlineProvider getOutlineProvider() {
return super.getOutlineProvider();
}
public void setOutsideTouchListener(OnClickListener onClickListener) {
+ mHasOutsideTouch = true;
requestLayout();
setOnClickListener(onClickListener);
setClickable(true);
@@ -60,7 +316,14 @@ public class VolumeUiLayout extends FrameLayout {
}
private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
+ if (mHasOutsideTouch || (mChild == null)) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
+ }
inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT);
+ inoutInfo.contentInsets.set(mChild.getLeft(), mChild.getTop(),
+ 0, getBottom() - mChild.getBottom());
};
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 7f07e0c70e8a..3e37cfe75e0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -38,6 +38,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.NotificationChannels;
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +47,9 @@ import org.mockito.ArgumentCaptor;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PowerNotificationWarningsTest extends SysuiTestCase {
+
+ public static final String FORMATTED_45M = "0h 45m";
+ public static final String FORMATTED_HOUR = "1h 0m";
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
private PowerNotificationWarnings mPowerNotificationWarnings;
@@ -147,4 +151,22 @@ public class PowerNotificationWarningsTest extends SysuiTestCase {
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
}
+
+ @Test
+ public void testGetTimeRemainingFormatted_roundsDownTo15() {
+ mPowerNotificationWarnings.updateEstimate(
+ new Estimate(TimeUnit.MINUTES.toMillis(57), true));
+ String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
+
+ assertTrue("time:" + time + ", expected: " + FORMATTED_45M, time.equals(FORMATTED_45M));
+ }
+
+ @Test
+ public void testGetTimeRemainingFormatted_keepsMinutesWhenZero() {
+ mPowerNotificationWarnings.updateEstimate(
+ new Estimate(TimeUnit.MINUTES.toMillis(65), true));
+ String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
+
+ assertTrue("time:" + time + ", expected: " + FORMATTED_HOUR, time.equals(FORMATTED_HOUR));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index e4734a474b62..fdb7f8d005b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -19,12 +19,15 @@ import static android.os.HardwarePropertiesManager.TEMPERATURE_CURRENT;
import static android.os.HardwarePropertiesManager.TEMPERATURE_SHUTDOWN;
import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
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.content.Context;
+import android.os.BatteryManager;
import android.os.HardwarePropertiesManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -37,6 +40,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,14 +50,23 @@ import org.junit.runner.RunWith;
@SmallTest
public class PowerUITest extends SysuiTestCase {
+ private static final boolean UNPLUGGED = false;
+ private static final boolean POWER_SAVER_OFF = false;
+ private static final int ABOVE_WARNING_BUCKET = 1;
+ public static final int BELOW_WARNING_BUCKET = -1;
+ public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
+ public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
private HardwarePropertiesManager mHardProps;
private WarningsUI mMockWarnings;
private PowerUI mPowerUI;
+ private EnhancedEstimates mEnhacedEstimates;
@Before
public void setup() {
mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
+ mEnhacedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
mHardProps = mock(HardwarePropertiesManager.class);
+
mContext.putComponent(StatusBar.class, mock(StatusBar.class));
mContext.addMockSystemService(Context.HARDWARE_PROPERTIES_SERVICE, mHardProps);
@@ -128,6 +141,180 @@ public class PowerUITest extends SysuiTestCase {
verify(mMockWarnings).showHighTemperatureWarning();
}
+ @Test
+ public void testShouldShowLowBatteryWarning_showHybridOnly_returnsShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would not show the non-hybrid notification but would show the
+ // hybrid
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_showHybrid_showStandard_returnsShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would show the non-hybrid notification and the hybrid
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_showStandardOnly_returnsShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would show the non-hybrid but not the hybrid
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, Long.MAX_VALUE, ABOVE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_deviceHighBattery_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would show the neither due to battery level being good
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, ABOVE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_devicePlugged_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // plugged device that would show the neither due to being plugged
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(!UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnkown_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // Unknown battery status device that would show the neither due
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ !POWER_SAVER_OFF, BatteryManager.BATTERY_STATUS_UNKNOWN);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_batterySaverEnabled_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // BatterySaverEnabled device that would show the neither due to battery saver
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ !POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissWhenPowerSaverEnabled() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ // device that gets power saver turned on should dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, !POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissWhenPlugged() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ // device that gets plugged in should dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(!UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissHybridSignal_showStandardSignal_shouldShow() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ // would dismiss hybrid but not non-hybrid should not dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertFalse(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_showHybridSignal_dismissStandardSignal_shouldShow() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ // would dismiss non-hybrid but not hybrid should not dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertFalse(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_showBothSignal_shouldShow() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ // should not dismiss when both would not dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertFalse(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissBothSignal_shouldDismiss() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ //should dismiss if both would dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissStandardSignal_hybridDisabled_shouldDismiss() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(false);
+
+ // would dismiss non-hybrid with hybrid disabled should dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
private void setCurrentTemp(float temp) {
when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_CURRENT))
.thenReturn(new float[] { temp });
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index 0a683891e66f..f9ec3f92181f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -32,11 +32,14 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -120,6 +123,23 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
}
}
+ private void setUserSentiment(String key, int sentiment) {
+ doAnswer(invocationOnMock -> {
+ NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+ invocationOnMock.getArguments()[1];
+ ranking.populate(
+ key,
+ 0,
+ false,
+ 0,
+ 0,
+ NotificationManager.IMPORTANCE_DEFAULT,
+ null, null,
+ null, null, null, true, sentiment);
+ return true;
+ }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -158,6 +178,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+
+ setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
@@ -196,6 +218,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
assertNotNull(entry.row);
+ assertEquals(mEntry.userSentiment,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
@@ -204,6 +228,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.getNotificationData().add(mEntry);
+ setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+
mHandler.post(() -> {
mEntryManager.updateNotification(mSbn, mRankingMap);
});
@@ -219,6 +245,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
verify(mCallback).onNotificationUpdated(mSbn);
assertNotNull(mEntry.row);
+ assertEquals(mEntry.userSentiment,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@Test
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 10a809aaae08..04cee676075d 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5134,6 +5134,15 @@ message MetricsEvent {
// OS: P
ACTION_SCREENSHOT_POWER_MENU = 1282;
+ // OPEN: Settings > Apps & Notifications -> Special app access -> Storage Access
+ // CATEGORY: SETTINGS
+ // OS: P
+ STORAGE_ACCESS = 1283;
+
+ // OPEN: Settings > Apps & Notifications -> Special app access -> Storage Access -> Package
+ // CATEGORY: SETTINGS
+ // OS: P
+ APPLICATIONS_STORAGE_DETAIL = 1284;
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 307f74d36ac5..f96fa7c237f7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -231,8 +231,7 @@ final class SaveUi {
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
- window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
diff --git a/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java b/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
new file mode 100644
index 000000000000..158084a4be21
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
@@ -0,0 +1,25 @@
+package com.android.server.backup;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A helper class to decouple this service from {@link DevicePolicyManager} in order to improve
+ * testability.
+ */
+@VisibleForTesting
+public class BackupPolicyEnforcer {
+ private DevicePolicyManager mDevicePolicyManager;
+
+ public BackupPolicyEnforcer(Context context) {
+ mDevicePolicyManager =
+ (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ }
+
+ public ComponentName getMandatoryBackupTransport() {
+ return mDevicePolicyManager.getMandatoryBackupTransport();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 03591a812bdd..518891006b37 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -38,6 +38,7 @@ import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IBackupAgent;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.FullBackup;
@@ -207,6 +208,10 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+ // Time delay for initialization operations that can be delayed so as not to consume too much CPU
+ // on bring-up and increase time-to-UI.
+ private static final long INITIALIZATION_DELAY_MILLIS = 3000;
+
// Timeout interval for deciding that a bind or clear-data has taken too long
private static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -282,6 +287,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
private final BackupPasswordManager mBackupPasswordManager;
+ // Time when we post the transport registration operation
+ private final long mRegisterTransportsRequestedTime;
+
@GuardedBy("mPendingRestores")
private boolean mIsRestoreInProgress;
@GuardedBy("mPendingRestores")
@@ -674,6 +682,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
@GuardedBy("mQueueLock")
private ArrayList<FullBackupEntry> mFullBackupQueue;
+ private BackupPolicyEnforcer mBackupPolicyEnforcer;
+
// Utility: build a new random integer token. The low bits are the ordinal of the
// operation for near-time uniqueness, and the upper bits are random for app-
// side unpredictability.
@@ -735,6 +745,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// Set up our transport options and initialize the default transport
SystemConfig systemConfig = SystemConfig.getInstance();
Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
+ if (transportWhitelist == null) {
+ transportWhitelist = Collections.emptySet();
+ }
String transport =
Settings.Secure.getString(
@@ -749,8 +762,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
new TransportManager(
context,
transportWhitelist,
- transport,
- backupThread.getLooper());
+ transport);
// If encrypted file systems is enabled or disabled, this call will return the
// correct directory.
@@ -852,15 +864,19 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
mTransportManager = transportManager;
- mTransportManager.setTransportBoundListener(mTransportBoundListener);
- mTransportManager.registerAllTransports();
+ mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+ mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+ mBackupHandler.postDelayed(
+ mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
- // Now that we know about valid backup participants, parse any
- // leftover journal files into the pending backup set
- mBackupHandler.post(this::parseLeftoverJournals);
+ // Now that we know about valid backup participants, parse any leftover journal files into
+ // the pending backup set
+ mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+
+ mBackupPolicyEnforcer = new BackupPolicyEnforcer(context);
}
private void initPackageTracking() {
@@ -1151,39 +1167,28 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
}
- private TransportManager.TransportBoundListener mTransportBoundListener =
- new TransportManager.TransportBoundListener() {
- @Override
- public boolean onTransportBound(IBackupTransport transport) {
- // If the init sentinel file exists, we need to be sure to perform the init
- // as soon as practical. We also create the state directory at registration
- // time to ensure it's present from the outset.
- String name = null;
- try {
- name = transport.name();
- String transportDirName = transport.transportDirName();
- File stateDir = new File(mBaseStateDir, transportDirName);
- stateDir.mkdirs();
+ private void onTransportRegistered(String transportName, String transportDirName) {
+ if (DEBUG) {
+ long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
+ Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
+ + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
+ }
- File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
- if (initSentinel.exists()) {
- synchronized (mQueueLock) {
- mPendingInits.add(name);
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ stateDir.mkdirs();
- // TODO: pick a better starting time than now + 1 minute
- long delay = 1000 * 60; // one minute, in milliseconds
- mAlarmManager.set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + delay, mRunInitIntent);
- }
- }
- return true;
- } catch (Exception e) {
- // the transport threw when asked its file naming prefs; declare it invalid
- Slog.w(TAG, "Failed to regiser transport: " + name);
- return false;
- }
- }
- };
+ File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+ if (initSentinel.exists()) {
+ synchronized (mQueueLock) {
+ mPendingInits.add(transportName);
+
+ // TODO: pick a better starting time than now + 1 minute
+ long delay = 1000 * 60; // one minute, in milliseconds
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay, mRunInitIntent);
+ }
+ }
+ }
// ----- Track installation/removal of packages -----
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -2774,6 +2779,10 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
+ if (!enable && mBackupPolicyEnforcer.getMandatoryBackupTransport() != null) {
+ Slog.w(TAG, "Cannot disable backups when the mandatory backups policy is active.");
+ return;
+ }
Slog.i(TAG, "Backup enabled => " + enable);
@@ -2891,14 +2900,14 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransports");
- return mTransportManager.getBoundTransportNames();
+ return mTransportManager.getRegisteredTransportNames();
}
@Override
public ComponentName[] listAllTransportComponents() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransportComponents");
- return mTransportManager.getAllTransportComponents();
+ return mTransportManager.getRegisteredTransportComponents();
}
@Override
@@ -3003,10 +3012,18 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
/** Selects transport {@code transportName} and returns previous selected transport. */
@Override
+ @Deprecated
+ @Nullable
public String selectBackupTransport(String transportName) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransport");
+ if (!isAllowedByMandatoryBackupTransportPolicy(transportName)) {
+ // Don't change the transport if it is not allowed.
+ Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
+ return mTransportManager.getCurrentTransportName();
+ }
+
final long oldId = Binder.clearCallingIdentity();
try {
String previousTransportName = mTransportManager.selectTransport(transportName);
@@ -3021,10 +3038,20 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
@Override
public void selectBackupTransportAsync(
- ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+ ComponentName transportComponent, @Nullable ISelectBackupTransportCallback listener) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
-
+ if (!isAllowedByMandatoryBackupTransportPolicy(transportComponent)) {
+ try {
+ if (listener != null) {
+ Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
+ listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
+ }
+ return;
+ }
final long oldId = Binder.clearCallingIdentity();
try {
String transportString = transportComponent.flattenToShortString();
@@ -3046,10 +3073,12 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
try {
- if (transportName != null) {
- listener.onSuccess(transportName);
- } else {
- listener.onFailure(result);
+ if (listener != null) {
+ if (transportName != null) {
+ listener.onSuccess(transportName);
+ } else {
+ listener.onFailure(result);
+ }
}
} catch (RemoteException e) {
Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
@@ -3060,6 +3089,38 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
}
+ /**
+ * Returns if the specified transport can be set as the current transport without violating the
+ * mandatory backup transport policy.
+ */
+ private boolean isAllowedByMandatoryBackupTransportPolicy(String transportName) {
+ ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
+ if (mandatoryBackupTransport == null) {
+ return true;
+ }
+ final String mandatoryBackupTransportName;
+ try {
+ mandatoryBackupTransportName =
+ mTransportManager.getTransportName(mandatoryBackupTransport);
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "mandatory backup transport not registered!");
+ return false;
+ }
+ return TextUtils.equals(mandatoryBackupTransportName, transportName);
+ }
+
+ /**
+ * Returns if the specified transport can be set as the current transport without violating the
+ * mandatory backup transport policy.
+ */
+ private boolean isAllowedByMandatoryBackupTransportPolicy(ComponentName transport) {
+ ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
+ if (mandatoryBackupTransport == null) {
+ return true;
+ }
+ return mandatoryBackupTransport.equals(transport);
+ }
+
private void updateStateForTransport(String newTransportName) {
// Publish the name change
Settings.Secure.putString(mContext.getContentResolver(),
@@ -3079,6 +3140,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
mCurrentToken = 0;
Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
}
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
} else {
Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
// The named transport isn't registered, so we can't know what its current dataset token
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index a628c9dbb1a9..540f5a15e6f7 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -177,12 +177,15 @@ public class Trampoline extends IBackupManager.Stub {
}
}
+ // IBackupManager binder API
+
/**
* Querying activity state of backup service. Calling this method before initialize yields
* undefined result.
* @param userHandle The user in which the activity state of backup service is queried.
* @return true if the service is active.
*/
+ @Override
public boolean isBackupServiceActive(final int userHandle) {
// TODO: http://b/22388012
if (userHandle == UserHandle.USER_SYSTEM) {
@@ -193,7 +196,6 @@ public class Trampoline extends IBackupManager.Stub {
return false;
}
- // IBackupManager binder API
@Override
public void dataChanged(String packageName) throws RemoteException {
BackupManagerServiceInterface svc = mService;
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 34b8935939ff..30fd25a92484 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -16,93 +16,54 @@
package com.android.server.backup;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-
import android.annotation.Nullable;
+import android.annotation.WorkerThread;
import android.app.backup.BackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
-import com.android.server.EventLogTags;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportClientManager;
import com.android.server.backup.transport.TransportConnectionListener;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
-import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
-/**
- * Handles in-memory bookkeeping of all BackupTransport objects.
- */
+/** Handles in-memory bookkeeping of all BackupTransport objects. */
public class TransportManager {
-
private static final String TAG = "BackupTransportManager";
@VisibleForTesting
public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
- private static final long REBINDING_TIMEOUT_UNPROVISIONED_MS = 30 * 1000; // 30 sec
- private static final long REBINDING_TIMEOUT_PROVISIONED_MS = 5 * 60 * 1000; // 5 mins
- private static final int REBINDING_TIMEOUT_MSG = 1;
-
private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
private final Context mContext;
private final PackageManager mPackageManager;
private final Set<ComponentName> mTransportWhitelist;
- private final Handler mHandler;
private final TransportClientManager mTransportClientManager;
-
- /**
- * This listener is called after we bind to any transport. If it returns true, this is a valid
- * transport.
- */
- private TransportBoundListener mTransportBoundListener;
-
private final Object mTransportLock = new Object();
-
- /**
- * We have detected these transports on the device. Unless in exceptional cases, we are also
- * bound to all of these.
- */
- @GuardedBy("mTransportLock")
- private final Map<ComponentName, TransportConnection> mValidTransports = new ArrayMap<>();
-
- /** We are currently bound to these transports. */
- @GuardedBy("mTransportLock")
- private final Map<String, ComponentName> mBoundTransports = new ArrayMap<>();
-
- /** @see #getEligibleTransportComponents() */
- @GuardedBy("mTransportLock")
- private final Set<ComponentName> mEligibleTransports = new ArraySet<>();
+ private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {};
/** @see #getRegisteredTransportNames() */
@GuardedBy("mTransportLock")
@@ -110,120 +71,98 @@ public class TransportManager {
new ArrayMap<>();
@GuardedBy("mTransportLock")
+ @Nullable
private volatile String mCurrentTransportName;
- TransportManager(
- Context context,
- Set<ComponentName> whitelist,
- String defaultTransport,
- TransportBoundListener listener,
- Looper looper) {
- this(context, whitelist, defaultTransport, looper);
- mTransportBoundListener = listener;
+ TransportManager(Context context, Set<ComponentName> whitelist, String selectedTransport) {
+ this(context, whitelist, selectedTransport, new TransportClientManager(context));
}
+ @VisibleForTesting
TransportManager(
Context context,
Set<ComponentName> whitelist,
- String defaultTransport,
- Looper looper) {
+ String selectedTransport,
+ TransportClientManager transportClientManager) {
mContext = context;
mPackageManager = context.getPackageManager();
- if (whitelist != null) {
- mTransportWhitelist = whitelist;
- } else {
- mTransportWhitelist = new ArraySet<>();
- }
- mCurrentTransportName = defaultTransport;
- mHandler = new RebindOnTimeoutHandler(looper);
- mTransportClientManager = new TransportClientManager(context);
+ mTransportWhitelist = Preconditions.checkNotNull(whitelist);
+ mCurrentTransportName = selectedTransport;
+ mTransportClientManager = transportClientManager;
}
- public void setTransportBoundListener(TransportBoundListener transportBoundListener) {
- mTransportBoundListener = transportBoundListener;
+ /* Sets a listener to be called whenever a transport is registered. */
+ public void setOnTransportRegisteredListener(OnTransportRegisteredListener listener) {
+ mOnTransportRegisteredListener = listener;
}
+ @WorkerThread
void onPackageAdded(String packageName) {
- // New package added. Bind to all transports it contains.
- synchronized (mTransportLock) {
- log_verbose("Package added. Binding to all transports. " + packageName);
- bindToAllInternal(packageName, null /* all components */);
- }
+ registerTransportsFromPackage(packageName, transportComponent -> true);
}
void onPackageRemoved(String packageName) {
- // Package removed. Remove all its transports from our list. These transports have already
- // been removed from mBoundTransports because onServiceDisconnected would already been
- // called on TransportConnection objects.
synchronized (mTransportLock) {
- Iterator<Map.Entry<ComponentName, TransportConnection>> iter =
- mValidTransports.entrySet().iterator();
- while (iter.hasNext()) {
- Map.Entry<ComponentName, TransportConnection> validTransport = iter.next();
- ComponentName componentName = validTransport.getKey();
- if (componentName.getPackageName().equals(packageName)) {
- TransportConnection transportConnection = validTransport.getValue();
- iter.remove();
- if (transportConnection != null) {
- mContext.unbindService(transportConnection);
- log_verbose("Package removed, removing transport: "
- + componentName.flattenToShortString());
- }
- }
- }
- removeTransportsIfLocked(
- componentName -> packageName.equals(componentName.getPackageName()));
+ mRegisteredTransportsDescriptionMap.keySet().removeIf(fromPackageFilter(packageName));
}
}
- void onPackageChanged(String packageName, String[] components) {
+ @WorkerThread
+ void onPackageChanged(String packageName, String... components) {
synchronized (mTransportLock) {
- // Remove all changed components from mValidTransports. We'll bind to them again
- // and re-add them if still valid.
- Set<ComponentName> transportsToBeRemoved = new ArraySet<>();
- for (String component : components) {
- ComponentName componentName = new ComponentName(packageName, component);
- transportsToBeRemoved.add(componentName);
- TransportConnection removed = mValidTransports.remove(componentName);
- if (removed != null) {
- mContext.unbindService(removed);
- log_verbose("Package changed. Removing transport: " +
- componentName.flattenToShortString());
- }
- }
- removeTransportsIfLocked(transportsToBeRemoved::contains);
- bindToAllInternal(packageName, components);
+ Set<ComponentName> transportComponents =
+ Stream.of(components)
+ .map(component -> new ComponentName(packageName, component))
+ .collect(Collectors.toSet());
+
+ mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains);
+ registerTransportsFromPackage(packageName, transportComponents::contains);
}
}
- @GuardedBy("mTransportLock")
- private void removeTransportsIfLocked(Predicate<ComponentName> filter) {
- mEligibleTransports.removeIf(filter);
- mRegisteredTransportsDescriptionMap.keySet().removeIf(filter);
+ /**
+ * Returns the {@link ComponentName}s of the registered transports.
+ *
+ * <p>A *registered* transport is a transport that satisfies intent with action
+ * android.backup.TRANSPORT_HOST, returns true for {@link #isTransportTrusted(ComponentName)}
+ * and that we have successfully connected to once.
+ */
+ ComponentName[] getRegisteredTransportComponents() {
+ synchronized (mTransportLock) {
+ return mRegisteredTransportsDescriptionMap
+ .keySet()
+ .toArray(new ComponentName[mRegisteredTransportsDescriptionMap.size()]);
+ }
}
- public IBackupTransport getTransportBinder(String transportName) {
+ /**
+ * Returns the names of the registered transports.
+ *
+ * @see #getRegisteredTransportComponents()
+ */
+ String[] getRegisteredTransportNames() {
synchronized (mTransportLock) {
- ComponentName component = mBoundTransports.get(transportName);
- if (component == null) {
- Slog.w(TAG, "Transport " + transportName + " not bound.");
- return null;
- }
- TransportConnection conn = mValidTransports.get(component);
- if (conn == null) {
- Slog.w(TAG, "Transport " + transportName + " not valid.");
- return null;
- }
- return conn.getBinder();
+ return mRegisteredTransportsDescriptionMap
+ .values()
+ .stream()
+ .map(transportDescription -> transportDescription.name)
+ .toArray(String[]::new);
}
}
- public IBackupTransport getCurrentTransportBinder() {
- return getTransportBinder(mCurrentTransportName);
+ /** Returns a set with the whitelisted transports. */
+ Set<ComponentName> getTransportWhitelist() {
+ return mTransportWhitelist;
+ }
+
+ @Nullable
+ String getCurrentTransportName() {
+ return mCurrentTransportName;
}
/**
* Returns the transport name associated with {@code transportComponent}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
public String getTransportName(ComponentName transportComponent)
@@ -234,33 +173,46 @@ public class TransportManager {
}
/**
- * Retrieve the configuration intent of {@code transportName}.
+ * Retrieves the transport dir name of {@code transportComponent}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
- @Nullable
- public Intent getTransportConfigurationIntent(String transportName)
+ public String getTransportDirName(ComponentName transportComponent)
throws TransportNotRegisteredException {
synchronized (mTransportLock) {
- return getRegisteredTransportDescriptionOrThrowLocked(transportName)
- .configurationIntent;
+ return getRegisteredTransportDescriptionOrThrowLocked(transportComponent)
+ .transportDirName;
}
}
/**
- * Retrieve the data management intent of {@code transportName}.
+ * Retrieves the transport dir name of {@code transportName}.
+ *
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ public String getTransportDirName(String transportName) throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportName).transportDirName;
+ }
+ }
+
+ /**
+ * Retrieves the configuration intent of {@code transportName}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
@Nullable
- public Intent getTransportDataManagementIntent(String transportName)
+ public Intent getTransportConfigurationIntent(String transportName)
throws TransportNotRegisteredException {
synchronized (mTransportLock) {
return getRegisteredTransportDescriptionOrThrowLocked(transportName)
- .dataManagementIntent;
+ .configurationIntent;
}
}
/**
- * Retrieve the current destination string of {@code transportName}.
+ * Retrieves the current destination string of {@code transportName}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
public String getTransportCurrentDestinationString(String transportName)
@@ -272,66 +224,101 @@ public class TransportManager {
}
/**
- * Retrieve the data management label of {@code transportName}.
+ * Retrieves the data management intent of {@code transportName}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
@Nullable
- public String getTransportDataManagementLabel(String transportName)
+ public Intent getTransportDataManagementIntent(String transportName)
throws TransportNotRegisteredException {
synchronized (mTransportLock) {
return getRegisteredTransportDescriptionOrThrowLocked(transportName)
- .dataManagementLabel;
+ .dataManagementIntent;
}
}
/**
- * Retrieve the transport dir name of {@code transportName}.
+ * Retrieves the data management label of {@code transportName}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
- public String getTransportDirName(String transportName)
+ @Nullable
+ public String getTransportDataManagementLabel(String transportName)
throws TransportNotRegisteredException {
synchronized (mTransportLock) {
return getRegisteredTransportDescriptionOrThrowLocked(transportName)
- .transportDirName;
+ .dataManagementLabel;
}
}
- /**
- * Retrieve the transport dir name of {@code transportComponent}.
- * @throws TransportNotRegisteredException if the transport is not registered.
- */
- public String getTransportDirName(ComponentName transportComponent)
- throws TransportNotRegisteredException {
+ /* Returns true if the transport identified by {@code transportName} is registered. */
+ public boolean isTransportRegistered(String transportName) {
synchronized (mTransportLock) {
- return getRegisteredTransportDescriptionOrThrowLocked(transportComponent)
- .transportDirName;
+ return getRegisteredTransportEntryLocked(transportName) != null;
}
}
/**
* Execute {@code transportConsumer} for each registered transport passing the transport name.
* This is called with an internal lock held, ensuring that the transport will remain registered
- * while {@code transportConsumer} is being executed. Don't do heavy operations in
- * {@code transportConsumer}.
+ * while {@code transportConsumer} is being executed. Don't do heavy operations in {@code
+ * transportConsumer}.
*/
public void forEachRegisteredTransport(Consumer<String> transportConsumer) {
synchronized (mTransportLock) {
- for (TransportDescription transportDescription
- : mRegisteredTransportsDescriptionMap.values()) {
+ for (TransportDescription transportDescription :
+ mRegisteredTransportsDescriptionMap.values()) {
transportConsumer.accept(transportDescription.name);
}
}
}
- public String getTransportName(IBackupTransport binder) {
+ /**
+ * Updates given values for the transport already registered and identified with {@param
+ * transportComponent}. If the transport is not registered it will log and return.
+ */
+ public void updateTransportAttributes(
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ @Nullable String dataManagementLabel) {
synchronized (mTransportLock) {
- for (TransportConnection conn : mValidTransports.values()) {
- if (conn.getBinder() == binder) {
- return conn.getName();
- }
+ TransportDescription description =
+ mRegisteredTransportsDescriptionMap.get(transportComponent);
+ if (description == null) {
+ Slog.e(TAG, "Transport " + name + " not registered tried to change description");
+ return;
}
+ description.name = name;
+ description.configurationIntent = configurationIntent;
+ description.currentDestinationString = currentDestinationString;
+ description.dataManagementIntent = dataManagementIntent;
+ description.dataManagementLabel = dataManagementLabel;
+ Slog.d(TAG, "Transport " + name + " updated its attributes");
}
- return null;
+ }
+
+ @GuardedBy("mTransportLock")
+ private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
+ ComponentName transportComponent) throws TransportNotRegisteredException {
+ TransportDescription description =
+ mRegisteredTransportsDescriptionMap.get(transportComponent);
+ if (description == null) {
+ throw new TransportNotRegisteredException(transportComponent);
+ }
+ return description;
+ }
+
+ @GuardedBy("mTransportLock")
+ private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
+ String transportName) throws TransportNotRegisteredException {
+ TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
+ if (description == null) {
+ throw new TransportNotRegisteredException(transportName);
+ }
+ return description;
}
@GuardedBy("mTransportLock")
@@ -351,21 +338,11 @@ public class TransportManager {
}
@GuardedBy("mTransportLock")
- private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
- String transportName) throws TransportNotRegisteredException {
- TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
- if (description == null) {
- throw new TransportNotRegisteredException(transportName);
- }
- return description;
- }
-
- @GuardedBy("mTransportLock")
@Nullable
private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked(
String transportName) {
- for (Map.Entry<ComponentName, TransportDescription> entry
- : mRegisteredTransportsDescriptionMap.entrySet()) {
+ for (Map.Entry<ComponentName, TransportDescription> entry :
+ mRegisteredTransportsDescriptionMap.entrySet()) {
TransportDescription description = entry.getValue();
if (transportName.equals(description.name)) {
return entry;
@@ -374,17 +351,16 @@ public class TransportManager {
return null;
}
- @GuardedBy("mTransportLock")
- private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
- ComponentName transportComponent) throws TransportNotRegisteredException {
- TransportDescription description =
- mRegisteredTransportsDescriptionMap.get(transportComponent);
- if (description == null) {
- throw new TransportNotRegisteredException(transportComponent);
- }
- return description;
- }
-
+ /**
+ * Returns a {@link TransportClient} for {@code transportName} or {@code null} if not
+ * registered.
+ *
+ * @param transportName The name of the transport.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportClient} or null if not registered.
+ */
@Nullable
public TransportClient getTransportClient(String transportName, String caller) {
try {
@@ -395,6 +371,16 @@ public class TransportManager {
}
}
+ /**
+ * Returns a {@link TransportClient} for {@code transportName} or throws if not registered.
+ *
+ * @param transportName The name of the transport.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportClient}.
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
public TransportClient getTransportClientOrThrow(String transportName, String caller)
throws TransportNotRegisteredException {
synchronized (mTransportLock) {
@@ -406,19 +392,14 @@ public class TransportManager {
}
}
- public boolean isTransportRegistered(String transportName) {
- synchronized (mTransportLock) {
- return getRegisteredTransportEntryLocked(transportName) != null;
- }
- }
-
/**
- * Returns a {@link TransportClient} for the current transport or null if not found.
+ * Returns a {@link TransportClient} for the current transport or {@code null} if not
+ * registered.
*
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
* {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
* details.
- * @return A {@link TransportClient} or null if not found.
+ * @return A {@link TransportClient} or null if not registered.
*/
@Nullable
public TransportClient getCurrentTransportClient(String caller) {
@@ -455,130 +436,88 @@ public class TransportManager {
mTransportClientManager.disposeOfTransportClient(transportClient, caller);
}
- String[] getBoundTransportNames() {
- synchronized (mTransportLock) {
- return mBoundTransports.keySet().toArray(new String[mBoundTransports.size()]);
- }
- }
-
- ComponentName[] getAllTransportComponents() {
- synchronized (mTransportLock) {
- return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]);
- }
- }
-
/**
- * An *eligible* transport is a service component that satisfies intent with action
- * android.backup.TRANSPORT_HOST and returns true for
- * {@link #isTransportTrusted(ComponentName)}. It may be registered or not registered.
- * This method returns the {@link ComponentName}s of those transports.
+ * Sets {@code transportName} as selected transport and returns previously selected transport
+ * name. If there was no previous transport it returns null.
+ *
+ * <p>You should NOT call this method in new code. This won't make any checks against {@code
+ * transportName}, putting any operation at risk of a {@link TransportNotRegisteredException} or
+ * another error at the time it's being executed.
+ *
+ * <p>{@link Deprecated} as public, this method can be used as private.
*/
- ComponentName[] getEligibleTransportComponents() {
+ @Deprecated
+ @Nullable
+ String selectTransport(String transportName) {
synchronized (mTransportLock) {
- return mEligibleTransports.toArray(new ComponentName[mEligibleTransports.size()]);
+ String prevTransport = mCurrentTransportName;
+ mCurrentTransportName = transportName;
+ return prevTransport;
}
}
- Set<ComponentName> getTransportWhitelist() {
- return mTransportWhitelist;
- }
-
/**
- * A *registered* transport is an eligible transport that has been successfully connected and
- * that returned true for method
- * {@link TransportBoundListener#onTransportBound(IBackupTransport)} of TransportBoundListener
- * provided in the constructor. This method returns the names of the registered transports.
+ * Tries to register the transport if not registered. If successful also selects the transport.
+ *
+ * @param transportComponent Host of the transport.
+ * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
+ * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
*/
- String[] getRegisteredTransportNames() {
+ @WorkerThread
+ public int registerAndSelectTransport(ComponentName transportComponent) {
synchronized (mTransportLock) {
- return mRegisteredTransportsDescriptionMap.values().stream()
- .map(transportDescription -> transportDescription.name)
- .toArray(String[]::new);
- }
- }
+ if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) {
+ int result = registerTransport(transportComponent);
+ if (result != BackupManager.SUCCESS) {
+ return result;
+ }
+ }
- /**
- * Updates given values for the transport already registered and identified with
- * {@param transportComponent}. If the transport is not registered it will log and return.
- */
- public void updateTransportAttributes(
- ComponentName transportComponent,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- @Nullable String dataManagementLabel) {
- synchronized (mTransportLock) {
- TransportDescription description =
- mRegisteredTransportsDescriptionMap.get(transportComponent);
- if (description == null) {
- Slog.e(TAG, "Transport " + name + " not registered tried to change description");
- return;
+ try {
+ selectTransport(getTransportName(transportComponent));
+ return BackupManager.SUCCESS;
+ } catch (TransportNotRegisteredException e) {
+ // Shouldn't happen because we are holding the lock
+ Slog.wtf(TAG, "Transport unexpectedly not registered");
+ return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
- description.name = name;
- description.configurationIntent = configurationIntent;
- description.currentDestinationString = currentDestinationString;
- description.dataManagementIntent = dataManagementIntent;
- description.dataManagementLabel = dataManagementLabel;
- Slog.d(TAG, "Transport " + name + " updated its attributes");
}
}
- @Nullable
- String getCurrentTransportName() {
- return mCurrentTransportName;
- }
-
- // This is for mocking, Mockito can't mock if package-protected and in the same package but
- // different class loaders. Checked with the debugger and class loaders are different
- // See https://github.com/mockito/mockito/issues/796
- @VisibleForTesting(visibility = PACKAGE)
- public void registerAllTransports() {
- bindToAllInternal(null /* all packages */, null /* all components */);
+ @WorkerThread
+ public void registerTransports() {
+ registerTransportsForIntent(mTransportServiceIntent, transportComponent -> true);
}
- /**
- * Bind to all transports belonging to the given package and the given component list.
- * null acts a wildcard.
- *
- * If packageName is null, bind to all transports in all packages.
- * If components is null, bind to all transports in the given package.
- */
- private void bindToAllInternal(String packageName, String[] components) {
- PackageInfo pkgInfo = null;
- if (packageName != null) {
- try {
- pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Package not found: " + packageName);
- return;
- }
+ @WorkerThread
+ private void registerTransportsFromPackage(
+ String packageName, Predicate<ComponentName> transportComponentFilter) {
+ try {
+ mPackageManager.getPackageInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Trying to register transports from package not found " + packageName);
+ return;
}
- Intent intent = new Intent(mTransportServiceIntent);
- if (packageName != null) {
- intent.setPackage(packageName);
- }
+ registerTransportsForIntent(
+ new Intent(mTransportServiceIntent).setPackage(packageName),
+ transportComponentFilter.and(fromPackageFilter(packageName)));
+ }
- List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
- intent, 0, UserHandle.USER_SYSTEM);
- if (hosts != null) {
+ @WorkerThread
+ private void registerTransportsForIntent(
+ Intent intent, Predicate<ComponentName> transportComponentFilter) {
+ List<ResolveInfo> hosts =
+ mPackageManager.queryIntentServicesAsUser(intent, 0, UserHandle.USER_SYSTEM);
+ if (hosts == null) {
+ return;
+ }
+ synchronized (mTransportLock) {
for (ResolveInfo host : hosts) {
- final ComponentName infoComponentName = getComponentName(host.serviceInfo);
- boolean shouldBind = false;
- if (components != null && packageName != null) {
- for (String component : components) {
- ComponentName cn = new ComponentName(pkgInfo.packageName, component);
- if (infoComponentName.equals(cn)) {
- shouldBind = true;
- break;
- }
- }
- } else {
- shouldBind = true;
- }
- if (shouldBind && isTransportTrusted(infoComponentName)) {
- tryBindTransport(infoComponentName);
+ ComponentName transportComponent = host.serviceInfo.getComponentName();
+ if (transportComponentFilter.test(transportComponent)
+ && isTransportTrusted(transportComponent)) {
+ registerTransport(transportComponent);
}
}
}
@@ -605,64 +544,6 @@ public class TransportManager {
return true;
}
- private void tryBindTransport(ComponentName transportComponentName) {
- Slog.d(TAG, "Binding to transport: " + transportComponentName.flattenToShortString());
- // TODO: b/22388012 (Multi user backup and restore)
- TransportConnection connection = new TransportConnection(transportComponentName);
- synchronized (mTransportLock) {
- mEligibleTransports.add(transportComponentName);
- }
- if (bindToTransport(transportComponentName, connection)) {
- synchronized (mTransportLock) {
- mValidTransports.put(transportComponentName, connection);
- }
- } else {
- Slog.w(TAG, "Couldn't bind to transport " + transportComponentName);
- }
- }
-
- private boolean bindToTransport(ComponentName componentName, ServiceConnection connection) {
- Intent intent = new Intent(mTransportServiceIntent)
- .setComponent(componentName);
- return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
- createSystemUserHandle());
- }
-
- String selectTransport(String transportName) {
- synchronized (mTransportLock) {
- String prevTransport = mCurrentTransportName;
- mCurrentTransportName = transportName;
- return prevTransport;
- }
- }
-
- /**
- * Tries to register the transport if not registered. If successful also selects the transport.
- *
- * @param transportComponent Host of the transport.
- * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
- * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
- */
- public int registerAndSelectTransport(ComponentName transportComponent) {
- synchronized (mTransportLock) {
- if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) {
- int result = registerTransport(transportComponent);
- if (result != BackupManager.SUCCESS) {
- return result;
- }
- }
-
- try {
- selectTransport(getTransportName(transportComponent));
- return BackupManager.SUCCESS;
- } catch (TransportNotRegisteredException e) {
- // Shouldn't happen because we are holding the lock
- Slog.wtf(TAG, "Transport unexpectedly not registered");
- return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
- }
- }
- }
-
/**
* Tries to register transport represented by {@code transportComponent}.
*
@@ -670,7 +551,12 @@ public class TransportManager {
* @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
* or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
*/
+ @WorkerThread
private int registerTransport(ComponentName transportComponent) {
+ if (!isTransportTrusted(transportComponent)) {
+ return BackupManager.ERROR_TRANSPORT_INVALID;
+ }
+
String transportString = transportComponent.flattenToShortString();
String callerLogString = "TransportManager.registerTransport()";
@@ -686,29 +572,21 @@ public class TransportManager {
return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1);
-
int result;
- if (isTransportValid(transport)) {
- try {
- registerTransport(transportComponent, transport);
- // If registerTransport() hasn't thrown...
- result = BackupManager.SUCCESS;
- } catch (RemoteException e) {
- Slog.e(TAG, "Transport " + transportString + " died while registering");
- result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
- }
- } else {
- Slog.w(TAG, "Can't register invalid transport " + transportString);
- result = BackupManager.ERROR_TRANSPORT_INVALID;
+ try {
+ String transportName = transport.name();
+ String transportDirName = transport.transportDirName();
+ registerTransport(transportComponent, transport);
+ // If registerTransport() hasn't thrown...
+ Slog.d(TAG, "Transport " + transportString + " registered");
+ mOnTransportRegisteredListener.onTransportRegistered(transportName, transportDirName);
+ result = BackupManager.SUCCESS;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Transport " + transportString + " died while registering");
+ result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
- if (result == BackupManager.SUCCESS) {
- Slog.d(TAG, "Transport " + transportString + " registered");
- } else {
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0);
- }
return result;
}
@@ -717,204 +595,20 @@ public class TransportManager {
throws RemoteException {
synchronized (mTransportLock) {
String name = transport.name();
- TransportDescription description = new TransportDescription(
- name,
- transport.transportDirName(),
- transport.configurationIntent(),
- transport.currentDestinationString(),
- transport.dataManagementIntent(),
- transport.dataManagementLabel());
+ TransportDescription description =
+ new TransportDescription(
+ name,
+ transport.transportDirName(),
+ transport.configurationIntent(),
+ transport.currentDestinationString(),
+ transport.dataManagementIntent(),
+ transport.dataManagementLabel());
mRegisteredTransportsDescriptionMap.put(transportComponent, description);
}
}
- private boolean isTransportValid(IBackupTransport transport) {
- if (mTransportBoundListener == null) {
- Slog.w(TAG, "setTransportBoundListener() not called, assuming transport invalid");
- return false;
- }
- return mTransportBoundListener.onTransportBound(transport);
- }
-
- private class TransportConnection implements ServiceConnection {
-
- // Hold mTransportLock to access these fields so as to provide a consistent view of them.
- private volatile IBackupTransport mBinder;
- private volatile String mTransportName;
-
- private final ComponentName mTransportComponent;
-
- private TransportConnection(ComponentName transportComponent) {
- mTransportComponent = transportComponent;
- }
-
- @Override
- public void onServiceConnected(ComponentName component, IBinder binder) {
- synchronized (mTransportLock) {
- mBinder = IBackupTransport.Stub.asInterface(binder);
- boolean success = false;
-
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
- component.flattenToShortString(), 1);
-
- try {
- mTransportName = mBinder.name();
- // BackupManager requests some fields from the transport. If they are
- // invalid, throw away this transport.
- if (isTransportValid(mBinder)) {
- // We're now using the always-bound connection to do the registration but
- // when we remove the always-bound code this will be in the first binding
- // TODO: Move registration to first binding
- registerTransport(component, mBinder);
- // If registerTransport() hasn't thrown...
- success = true;
- }
- } catch (RemoteException e) {
- success = false;
- Slog.e(TAG, "Couldn't get transport name.", e);
- } finally {
- // we need to intern() the String of the component, so that we can use it with
- // Handler's removeMessages(), which uses == operator to compare the tokens
- String componentShortString = component.flattenToShortString().intern();
- if (success) {
- Slog.d(TAG, "Bound to transport: " + componentShortString);
- mBoundTransports.put(mTransportName, component);
- // cancel rebinding on timeout for this component as we've already connected
- mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString);
- } else {
- Slog.w(TAG, "Bound to transport " + componentShortString +
- " but it is invalid");
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
- componentShortString, 0);
- mContext.unbindService(this);
- mValidTransports.remove(component);
- mEligibleTransports.remove(component);
- mBinder = null;
- }
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName component) {
- synchronized (mTransportLock) {
- mBinder = null;
- mBoundTransports.remove(mTransportName);
- }
- String componentShortString = component.flattenToShortString();
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, componentShortString, 0);
- Slog.w(TAG, "Disconnected from transport " + componentShortString);
- scheduleRebindTimeout(component);
- }
-
- /**
- * We'll attempt to explicitly rebind to a transport if it hasn't happened automatically
- * for a few minutes after the binding went away.
- */
- private void scheduleRebindTimeout(ComponentName component) {
- // we need to intern() the String of the component, so that we can use it with Handler's
- // removeMessages(), which uses == operator to compare the tokens
- final String componentShortString = component.flattenToShortString().intern();
- final long rebindTimeout = getRebindTimeout();
- mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString);
- Message msg = mHandler.obtainMessage(REBINDING_TIMEOUT_MSG);
- msg.obj = componentShortString;
- mHandler.sendMessageDelayed(msg, rebindTimeout);
- Slog.d(TAG, "Scheduled explicit rebinding for " + componentShortString + " in "
- + rebindTimeout + "ms");
- }
-
- // Intentionally not synchronized -- the variable is volatile and changes to its value
- // are inside synchronized blocks, providing a memory sync barrier; and this method
- // does not touch any other state protected by that lock.
- private IBackupTransport getBinder() {
- return mBinder;
- }
-
- // Intentionally not synchronized; same as getBinder()
- private String getName() {
- return mTransportName;
- }
-
- // Intentionally not synchronized; same as getBinder()
- private void bindIfUnbound() {
- if (mBinder == null) {
- Slog.d(TAG,
- "Rebinding to transport " + mTransportComponent.flattenToShortString());
- bindToTransport(mTransportComponent, this);
- }
- }
-
- private long getRebindTimeout() {
- final boolean isDeviceProvisioned = Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- return isDeviceProvisioned
- ? REBINDING_TIMEOUT_PROVISIONED_MS
- : REBINDING_TIMEOUT_UNPROVISIONED_MS;
- }
- }
-
- public interface TransportBoundListener {
- /** Should return true if this is a valid transport. */
- boolean onTransportBound(IBackupTransport binder);
- }
-
- private class RebindOnTimeoutHandler extends Handler {
-
- RebindOnTimeoutHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == REBINDING_TIMEOUT_MSG) {
- String componentShortString = (String) msg.obj;
- ComponentName transportComponent =
- ComponentName.unflattenFromString(componentShortString);
- synchronized (mTransportLock) {
- if (mBoundTransports.containsValue(transportComponent)) {
- Slog.d(TAG, "Explicit rebinding timeout passed, but already bound to "
- + componentShortString + " so not attempting to rebind");
- return;
- }
- Slog.d(TAG, "Explicit rebinding timeout passed, attempting rebinding to: "
- + componentShortString);
- // unbind the existing (broken) connection
- TransportConnection conn = mValidTransports.get(transportComponent);
- if (conn != null) {
- mContext.unbindService(conn);
- Slog.d(TAG, "Unbinding the existing (broken) connection to transport: "
- + componentShortString);
- }
- }
- // rebind to transport
- tryBindTransport(transportComponent);
- } else {
- Slog.e(TAG, "Unknown message sent to RebindOnTimeoutHandler, msg.what: "
- + msg.what);
- }
- }
- }
-
- private static void log_verbose(String message) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, message);
- }
- }
-
- // These only exists to make it testable with Robolectric, which is not updated to API level 24
- // yet.
- // TODO: Get rid of this once Robolectric is updated.
- private static ComponentName getComponentName(ServiceInfo serviceInfo) {
- return new ComponentName(serviceInfo.packageName, serviceInfo.name);
- }
-
- // These only exists to make it testable with Robolectric, which is not updated to API level 24
- // yet.
- // TODO: Get rid of this once Robolectric is updated.
- public static UserHandle createSystemUserHandle() {
- return new UserHandle(UserHandle.USER_SYSTEM);
+ private static Predicate<ComponentName> fromPackageFilter(String packageName) {
+ return transportComponent -> packageName.equals(transportComponent.getPackageName());
}
private static class TransportDescription {
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index c2322413d2a7..cc3af8c8c933 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -907,7 +907,15 @@ public class PerformBackupTask implements BackupRestoreTask {
backupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
backupManagerService.addBackupTrace("sending data to transport");
- int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
+
+ int userInitiatedFlag =
+ mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
+ int incrementalFlag =
+ mSavedStateName.length() == 0
+ ? BackupTransport.FLAG_NON_INCREMENTAL
+ : BackupTransport.FLAG_INCREMENTAL;
+ int flags = userInitiatedFlag | incrementalFlag;
+
mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
}
diff --git a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
new file mode 100644
index 000000000000..391ec2d7f294
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.transport;
+
+import com.android.server.backup.TransportManager;
+
+/**
+ * Listener called when a transport is registered with the {@link TransportManager}. Can be set
+ * using {@link TransportManager#setOnTransportRegisteredListener(OnTransportRegisteredListener)}.
+ */
+@FunctionalInterface
+public interface OnTransportRegisteredListener {
+ /**
+ * Called when a transport is successfully registered.
+ * @param transportName The name of the transport.
+ * @param transportDirName The dir name of the transport.
+ */
+ public void onTransportRegistered(String transportName, String transportDirName);
+}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index 7bd9111028cb..399f338d26b2 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -29,12 +29,14 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.EventLog;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
+import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import java.lang.annotation.Retention;
@@ -236,7 +238,7 @@ public class TransportClient {
mBindIntent,
mConnection,
Context.BIND_AUTO_CREATE,
- TransportManager.createSystemUserHandle());
+ UserHandle.SYSTEM);
if (hasBound) {
// We don't need to set a time-out because we are guaranteed to get a call
// back in ServiceConnection, either an onServiceConnected() or
@@ -419,10 +421,45 @@ public class TransportClient {
@GuardedBy("mStateLock")
private void setStateLocked(@State int state, @Nullable IBackupTransport transport) {
log(Log.VERBOSE, "State: " + stateToString(mState) + " => " + stateToString(state));
+ onStateTransition(mState, state);
mState = state;
mTransport = transport;
}
+ private void onStateTransition(int oldState, int newState) {
+ String transport = mTransportComponent.flattenToShortString();
+ int bound = transitionThroughState(oldState, newState, State.BOUND_AND_CONNECTING);
+ int connected = transitionThroughState(oldState, newState, State.CONNECTED);
+ if (bound != Transition.NO_TRANSITION) {
+ int value = (bound == Transition.UP) ? 1 : 0; // 1 is bound, 0 is not bound
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transport, value);
+ }
+ if (connected != Transition.NO_TRANSITION) {
+ int value = (connected == Transition.UP) ? 1 : 0; // 1 is connected, 0 is not connected
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_CONNECTION, transport, value);
+ }
+ }
+
+ /**
+ * Returns:
+ *
+ * <ul>
+ * <li>{@link Transition#UP}, if oldState < stateReference <= newState
+ * <li>{@link Transition#DOWN}, if oldState >= stateReference > newState
+ * <li>{@link Transition#NO_TRANSITION}, otherwise
+ */
+ @Transition
+ private int transitionThroughState(
+ @State int oldState, @State int newState, @State int stateReference) {
+ if (oldState < stateReference && stateReference <= newState) {
+ return Transition.UP;
+ }
+ if (oldState >= stateReference && stateReference > newState) {
+ return Transition.DOWN;
+ }
+ return Transition.NO_TRANSITION;
+ }
+
@GuardedBy("mStateLock")
private void checkStateIntegrityLocked() {
switch (mState) {
@@ -481,6 +518,14 @@ public class TransportClient {
// CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
}
+ @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface Transition {
+ int DOWN = -1;
+ int NO_TRANSITION = 0;
+ int UP = 1;
+ }
+
@IntDef({State.UNUSABLE, State.IDLE, State.BOUND_AND_CONNECTING, State.CONNECTED})
@Retention(RetentionPolicy.SOURCE)
private @interface State {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9fb26819dae0..3369458595a8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -16,6 +16,7 @@ java_library_static {
":installd_aidl",
":storaged_aidl",
":vold_aidl",
+ ":mediaupdateservice_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
],
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index af0b66daad50..04d292fa1ae4 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -293,15 +293,10 @@ public final class BatteryService extends SystemService {
private void updateBatteryWarningLevelLocked() {
final ContentResolver resolver = mContext.getContentResolver();
- final int defWarnLevel = mContext.getResources().getInteger(
+ int defWarnLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
- final int lowPowerModeTriggerLevel = Settings.Global.getInt(resolver,
+ mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
-
- // NOTE: Keep the logic in sync with PowerUI.java in systemUI.
- // TODO: Propagate this value from BatteryService to system UI, really.
- mLowBatteryWarningLevel = Math.min(defWarnLevel, lowPowerModeTriggerLevel);
-
if (mLowBatteryWarningLevel == 0) {
mLowBatteryWarningLevel = defWarnLevel;
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d9713a517a94..337406d58f9d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -60,6 +60,7 @@ import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.util.DumpUtils;
import com.android.server.pm.UserRestrictionsUtils;
@@ -415,9 +416,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
int systemUiUid = -1;
try {
- systemUiUid = mContext.getPackageManager()
- .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
- UserHandle.USER_SYSTEM);
+ // Check if device is configured with no home screen, which implies no SystemUI.
+ boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
+ if (!noHome) {
+ systemUiUid = mContext.getPackageManager()
+ .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
+ }
+ Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bbba878b4ff2..77521df7e06a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -226,7 +226,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
@GuardedBy("mVpns")
private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
+ // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
+ // a direct call to LockdownVpnTracker.isEnabled().
+ @GuardedBy("mVpns")
private boolean mLockdownEnabled;
+ @GuardedBy("mVpns")
private LockdownVpnTracker mLockdownTracker;
final private Context mContext;
@@ -997,9 +1001,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private Network[] getVpnUnderlyingNetworks(int uid) {
- if (!mLockdownEnabled) {
- int user = UserHandle.getUserId(uid);
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ if (!mLockdownEnabled) {
+ int user = UserHandle.getUserId(uid);
Vpn vpn = mVpns.get(user);
if (vpn != null && vpn.appliesToUid(uid)) {
return vpn.getUnderlyingNetworks();
@@ -1087,8 +1091,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) {
state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
}
- if (mLockdownTracker != null) {
- mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+ }
}
}
@@ -1253,8 +1259,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
result.put(nai.network, nc);
}
- if (!mLockdownEnabled) {
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ if (!mLockdownEnabled) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
Network[] networks = vpn.getUnderlyingNetworks();
@@ -1580,9 +1586,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
- if (mLockdownTracker != null) {
- info = new NetworkInfo(info);
- mLockdownTracker.augmentNetworkInfo(info);
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ info = new NetworkInfo(info);
+ mLockdownTracker.augmentNetworkInfo(info);
+ }
}
Intent intent = new Intent(bcastType);
@@ -3436,9 +3444,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
int userId) {
enforceCrossUserPermission(userId);
- throwIfLockdownEnabled();
synchronized (mVpns) {
+ throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
return vpn.prepare(oldPackage, newPackage);
@@ -3482,9 +3490,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).establish(config);
}
}
@@ -3495,13 +3503,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
@Override
public void startLegacyVpn(VpnProfile profile) {
- throwIfLockdownEnabled();
+ int user = UserHandle.getUserId(Binder.getCallingUid());
final LinkProperties egress = getActiveLinkProperties();
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
}
- int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
}
}
@@ -3527,11 +3535,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public VpnInfo[] getAllVpnInfo() {
enforceConnectivityInternalPermission();
- if (mLockdownEnabled) {
- return new VpnInfo[0];
- }
-
synchronized (mVpns) {
+ if (mLockdownEnabled) {
+ return new VpnInfo[0];
+ }
+
List<VpnInfo> infoList = new ArrayList<>();
for (int i = 0; i < mVpns.size(); i++) {
VpnInfo info = createVpnInfo(mVpns.valueAt(i));
@@ -3596,33 +3604,33 @@ public class ConnectivityService extends IConnectivityManager.Stub
return false;
}
- // Tear down existing lockdown if profile was removed
- mLockdownEnabled = LockdownVpnTracker.isEnabled();
- if (mLockdownEnabled) {
- byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
- if (profileTag == null) {
- Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
- return false;
- }
- String profileName = new String(profileTag);
- final VpnProfile profile = VpnProfile.decode(
- profileName, mKeyStore.get(Credentials.VPN + profileName));
- if (profile == null) {
- Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
- setLockdownTracker(null);
- return true;
- }
- int user = UserHandle.getUserId(Binder.getCallingUid());
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ // Tear down existing lockdown if profile was removed
+ mLockdownEnabled = LockdownVpnTracker.isEnabled();
+ if (mLockdownEnabled) {
+ byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
+ if (profileTag == null) {
+ Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
+ return false;
+ }
+ String profileName = new String(profileTag);
+ final VpnProfile profile = VpnProfile.decode(
+ profileName, mKeyStore.get(Credentials.VPN + profileName));
+ if (profile == null) {
+ Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+ setLockdownTracker(null);
+ return true;
+ }
+ int user = UserHandle.getUserId(Binder.getCallingUid());
Vpn vpn = mVpns.get(user);
if (vpn == null) {
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+ } else {
+ setLockdownTracker(null);
}
- } else {
- setLockdownTracker(null);
}
return true;
@@ -3632,6 +3640,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
* Internally set new {@link LockdownVpnTracker}, shutting down any existing
* {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
*/
+ @GuardedBy("mVpns")
private void setLockdownTracker(LockdownVpnTracker tracker) {
// Shutdown any existing tracker
final LockdownVpnTracker existing = mLockdownTracker;
@@ -3646,6 +3655,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ @GuardedBy("mVpns")
private void throwIfLockdownEnabled() {
if (mLockdownEnabled) {
throw new IllegalStateException("Unavailable in lockdown mode");
@@ -3693,12 +3703,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceConnectivityInternalPermission();
enforceCrossUserPermission(userId);
- // Can't set always-on VPN if legacy VPN is already in lockdown mode.
- if (LockdownVpnTracker.isEnabled()) {
- return false;
- }
-
synchronized (mVpns) {
+ // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+ if (LockdownVpnTracker.isEnabled()) {
+ return false;
+ }
+
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
@@ -3874,9 +3884,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
mVpns.put(userId, userVpn);
- }
- if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
- updateLockdownVpn();
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ }
}
}
@@ -3913,11 +3923,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void onUserUnlocked(int userId) {
- // User present may be sent because of an unlock, which might mean an unlocked keystore.
- if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
- updateLockdownVpn();
- } else {
- startAlwaysOnVpn(userId);
+ synchronized (mVpns) {
+ // User present may be sent because of an unlock, which might mean an unlocked keystore.
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ } else {
+ startAlwaysOnVpn(userId);
+ }
}
}
@@ -4597,51 +4609,67 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities}
- * augmented with any stateful capabilities implied from {@code networkAgent}
- * (e.g., validated status and captive portal status).
- *
- * @param oldScore score of the network before any of the changes that prompted us
- * to call this function.
- * @param nai the network having its capabilities updated.
- * @param networkCapabilities the new network capabilities.
+ * Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
+ * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
+ * and foreground status).
*/
- private void updateCapabilities(
- int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
+ private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
- if (nai.everConnected && !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
- networkCapabilities)) {
- // TODO: consider not complaining when a network agent degrade its capabilities if this
+ if (nai.everConnected &&
+ !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(nc)) {
+ // TODO: consider not complaining when a network agent degrades its capabilities if this
// does not cause any request (that is not a listen) currently matching that agent to
// stop being matched by the updated agent.
- String diff = nai.networkCapabilities.describeImmutableDifferences(networkCapabilities);
+ String diff = nai.networkCapabilities.describeImmutableDifferences(nc);
if (!TextUtils.isEmpty(diff)) {
Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
}
}
// Don't modify caller's NetworkCapabilities.
- networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ NetworkCapabilities newNc = new NetworkCapabilities(nc);
if (nai.lastValidated) {
- networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
+ newNc.addCapability(NET_CAPABILITY_VALIDATED);
} else {
- networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
+ newNc.removeCapability(NET_CAPABILITY_VALIDATED);
}
if (nai.lastCaptivePortalDetected) {
- networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ newNc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
} else {
- networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ newNc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
}
if (nai.isBackgroundNetwork()) {
- networkCapabilities.removeCapability(NET_CAPABILITY_FOREGROUND);
+ newNc.removeCapability(NET_CAPABILITY_FOREGROUND);
} else {
- networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
+ newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
- if (Objects.equals(nai.networkCapabilities, networkCapabilities)) return;
+ return newNc;
+ }
+
+ /**
+ * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
+ *
+ * 1. Calls mixInCapabilities to merge the passed-in NetworkCapabilities {@code nc} with the
+ * capabilities we manage and store in {@code nai}, such as validated status and captive
+ * portal status)
+ * 2. Takes action on the result: changes network permissions, sends CAP_CHANGED callbacks, and
+ * potentially triggers rematches.
+ * 3. Directly informs other network stack components (NetworkStatsService, VPNs, etc. of the
+ * change.)
+ *
+ * @param oldScore score of the network before any of the changes that prompted us
+ * to call this function.
+ * @param nai the network having its capabilities updated.
+ * @param nc the new network capabilities.
+ */
+ private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
+ NetworkCapabilities newNc = mixInCapabilities(nai, nc);
+
+ if (Objects.equals(nai.networkCapabilities, newNc)) return;
final String oldPermission = getNetworkPermission(nai.networkCapabilities);
- final String newPermission = getNetworkPermission(networkCapabilities);
+ final String newPermission = getNetworkPermission(newNc);
if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
try {
mNetd.setNetworkPermission(nai.network.netId, newPermission);
@@ -4653,11 +4681,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkCapabilities prevNc;
synchronized (nai) {
prevNc = nai.networkCapabilities;
- nai.networkCapabilities = networkCapabilities;
+ nai.networkCapabilities = newNc;
}
- if (nai.getCurrentScore() == oldScore &&
- networkCapabilities.equalRequestableCapabilities(prevNc)) {
+ if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) {
// If the requestable capabilities haven't changed, and the score hasn't changed, then
// the change we're processing can't affect any requests, it can only affect the listens
// on this network. We might have been called by rematchNetworkAndRequests when a
@@ -4673,15 +4700,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Report changes that are interesting for network statistics tracking.
if (prevNc != null) {
final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
- networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
+ newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
- networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
if (meteredChanged || roamingChanged) {
notifyIfacesChangedForNetworkStats();
}
}
- if (!networkCapabilities.hasTransport(TRANSPORT_VPN)) {
+ if (!newNc.hasTransport(TRANSPORT_VPN)) {
// Tell VPNs about updated capabilities, since they may need to
// bubble those changes through.
synchronized (mVpns) {
@@ -5205,11 +5232,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void notifyLockdownVpn(NetworkAgentInfo nai) {
- if (mLockdownTracker != null) {
- if (nai != null && nai.isVPN()) {
- mLockdownTracker.onVpnStateChanged(nai.networkInfo);
- } else {
- mLockdownTracker.onNetworkInfoChanged();
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ if (nai != null && nai.isVPN()) {
+ mLockdownTracker.onVpnStateChanged(nai.networkInfo);
+ } else {
+ mLockdownTracker.onNetworkInfoChanged();
+ }
}
}
}
@@ -5439,28 +5468,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public boolean addVpnAddress(String address, int prefixLength) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).addAddress(address, prefixLength);
}
}
@Override
public boolean removeVpnAddress(String address, int prefixLength) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).removeAddress(address, prefixLength);
}
}
@Override
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
- boolean success;
+ final boolean success;
synchronized (mVpns) {
+ throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
@@ -5520,31 +5549,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
setAlwaysOnVpnPackage(userId, null, false);
setVpnPackageAuthorization(alwaysOnPackage, userId, false);
}
- }
- // Turn Always-on VPN off
- if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mKeyStore.delete(Credentials.LOCKDOWN_VPN);
- mLockdownEnabled = false;
- setLockdownTracker(null);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ // Turn Always-on VPN off
+ if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+ mLockdownEnabled = false;
+ setLockdownTracker(null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- // Turn VPN off
- VpnConfig vpnConfig = getVpnConfig(userId);
- if (vpnConfig != null) {
- if (vpnConfig.legacy) {
- prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
- } else {
- // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
- // in the future without user intervention.
- setVpnPackageAuthorization(vpnConfig.user, userId, false);
+ // Turn VPN off
+ VpnConfig vpnConfig = getVpnConfig(userId);
+ if (vpnConfig != null) {
+ if (vpnConfig.legacy) {
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
+ } else {
+ // Prevent this app (packagename = vpnConfig.user) from initiating
+ // VPN connections in the future without user intervention.
+ setVpnPackageAuthorization(vpnConfig.user, userId, false);
- prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+ prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 985f16d910bc..a12c85aef85e 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1591,6 +1591,8 @@ public class DeviceIdleController extends SystemService
mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(
mPowerSaveWhitelistAppsExceptIdle, mPowerSaveWhitelistUserApps,
mPowerSaveWhitelistExceptIdleAppIds);
+
+ passWhiteListToForceAppStandbyTrackerLocked();
}
return true;
} catch (PackageManager.NameNotFoundException e) {
@@ -1608,6 +1610,8 @@ public class DeviceIdleController extends SystemService
mPowerSaveWhitelistAppsExceptIdle, mPowerSaveWhitelistUserApps,
mPowerSaveWhitelistExceptIdleAppIds);
mPowerSaveWhitelistUserAppsExceptIdle.clear();
+
+ passWhiteListToForceAppStandbyTrackerLocked();
}
}
}
@@ -2572,7 +2576,7 @@ public class DeviceIdleController extends SystemService
private void passWhiteListToForceAppStandbyTrackerLocked() {
ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds(
- mPowerSaveWhitelistAllAppIdArray,
+ mPowerSaveWhitelistExceptIdleAppIdArray,
mTempWhitelistAppIdArray);
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 8361132f24bc..732ac66b41de 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -133,6 +133,7 @@ option java_package com.android.server
2846 full_backup_cancelled (Package|3),(Message|3)
2850 backup_transport_lifecycle (Transport|3),(Bound|1|1)
+2851 backup_transport_connection (Transport|3),(Connected|1|1)
# ---------------------------
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 8776f3a9f6c8..45516115b629 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -26,6 +26,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -89,6 +91,9 @@ public class ForceAppStandbyTracker {
private final MyHandler mHandler;
+ @VisibleForTesting
+ FeatureFlagsObserver mFlagsObserver;
+
/**
* Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
*/
@@ -98,6 +103,9 @@ public class ForceAppStandbyTracker {
@GuardedBy("mLock")
final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+ /**
+ * System except-idle + user whitelist in the device idle controller.
+ */
@GuardedBy("mLock")
private int[] mPowerWhitelistedAllAppIds = new int[0];
@@ -111,13 +119,32 @@ public class ForceAppStandbyTracker {
boolean mStarted;
@GuardedBy("mLock")
- boolean mForceAllAppsStandby; // True if device is in extreme battery saver mode
+ boolean mIsCharging;
+
+ @GuardedBy("mLock")
+ boolean mBatterySaverEnabled;
+
+ /**
+ * True if the forced app standby is currently enabled
+ */
+ @GuardedBy("mLock")
+ boolean mForceAllAppsStandby;
+
+ /**
+ * True if the forced app standby for small battery devices feature is enabled in settings
+ */
+ @GuardedBy("mLock")
+ boolean mForceAllAppStandbyForSmallBattery;
+ /**
+ * True if the forced app standby feature is enabled in settings
+ */
@GuardedBy("mLock")
- boolean mForcedAppStandbyEnabled; // True if the forced app standby feature is enabled
+ boolean mForcedAppStandbyEnabled;
- private class FeatureFlagObserver extends ContentObserver {
- FeatureFlagObserver() {
+ @VisibleForTesting
+ class FeatureFlagsObserver extends ContentObserver {
+ FeatureFlagsObserver() {
super(null);
}
@@ -125,6 +152,9 @@ public class ForceAppStandbyTracker {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
false, this);
+
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
}
boolean isForcedAppStandbyEnabled() {
@@ -132,20 +162,43 @@ public class ForceAppStandbyTracker {
Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
}
+ boolean isForcedAppStandbyForSmallBatteryEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
+ }
+
@Override
- public void onChange(boolean selfChange) {
- final boolean enabled = isForcedAppStandbyEnabled();
- synchronized (mLock) {
- if (mForcedAppStandbyEnabled == enabled) {
- return;
+ public void onChange(boolean selfChange, Uri uri) {
+ if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyEnabled();
+ synchronized (mLock) {
+ if (mForcedAppStandbyEnabled == enabled) {
+ return;
+ }
+ mForcedAppStandbyEnabled = enabled;
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+ }
}
- mForcedAppStandbyEnabled = enabled;
- if (DEBUG) {
- Slog.d(TAG,
- "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+ mHandler.notifyForcedAppStandbyFeatureFlagChanged();
+ } else if (Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
+ synchronized (mLock) {
+ if (mForceAllAppStandbyForSmallBattery == enabled) {
+ return;
+ }
+ mForceAllAppStandbyForSmallBattery = enabled;
+ if (DEBUG) {
+ Slog.d(TAG, "Forced app standby for small battery feature flag changed: "
+ + mForceAllAppStandbyForSmallBattery);
+ }
+ updateForceAllAppStandbyState();
}
+ } else {
+ Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri);
}
- mHandler.notifyFeatureFlagChanged();
}
}
@@ -286,9 +339,11 @@ public class ForceAppStandbyTracker {
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
- final FeatureFlagObserver flagObserver = new FeatureFlagObserver();
- flagObserver.register();
- mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled();
+ mFlagsObserver = new FeatureFlagsObserver();
+ mFlagsObserver.register();
+ mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
+ mForceAllAppStandbyForSmallBattery =
+ mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
try {
mIActivityManager.registerUidObserver(new UidObserver(),
@@ -303,16 +358,24 @@ public class ForceAppStandbyTracker {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new MyReceiver(), filter);
refreshForcedAppStandbyUidPackagesLocked();
mPowerManagerInternal.registerLowPowerModeObserver(
ServiceType.FORCE_ALL_APPS_STANDBY,
- (state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
+ (state) -> {
+ synchronized (mLock) {
+ mBatterySaverEnabled = state.batterySaverEnabled;
+ updateForceAllAppStandbyState();
+ }
+ });
+
+ mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState(
+ ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled;
- updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
- ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
+ updateForceAllAppStandbyState();
}
}
@@ -337,6 +400,11 @@ public class ForceAppStandbyTracker {
return LocalServices.getService(PowerManagerInternal.class);
}
+ @VisibleForTesting
+ boolean isSmallBatteryDevice() {
+ return ActivityManager.isSmallBatteryDevice();
+ }
+
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
@@ -366,18 +434,29 @@ public class ForceAppStandbyTracker {
}
}
- /**
- * Update {@link #mForceAllAppsStandby} and notifies the listeners.
- */
- void updateForceAllAppsStandby(boolean enable) {
+ private void updateForceAllAppStandbyState() {
synchronized (mLock) {
- if (enable == mForceAllAppsStandby) {
- return;
+ if (mIsCharging) {
+ toggleForceAllAppsStandbyLocked(false);
+ } else if (mForceAllAppStandbyForSmallBattery
+ && isSmallBatteryDevice()) {
+ toggleForceAllAppsStandbyLocked(true);
+ } else {
+ toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
}
- mForceAllAppsStandby = enable;
+ }
+ }
- mHandler.notifyForceAllAppsStandbyChanged();
+ /**
+ * Update {@link #mForceAllAppsStandby} and notifies the listeners.
+ */
+ private void toggleForceAllAppsStandbyLocked(boolean enable) {
+ if (enable == mForceAllAppsStandby) {
+ return;
}
+ mForceAllAppsStandby = enable;
+
+ mHandler.notifyForceAllAppsStandbyChanged();
}
private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
@@ -512,6 +591,13 @@ public class ForceAppStandbyTracker {
if (userId > 0) {
mHandler.doUserRemoved(userId);
}
+ } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+ synchronized (mLock) {
+ mIsCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status == BatteryManager.BATTERY_STATUS_FULL);
+ }
+ updateForceAllAppStandbyState();
}
}
}
@@ -530,7 +616,7 @@ public class ForceAppStandbyTracker {
private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
private static final int MSG_FORCE_ALL_CHANGED = 6;
private static final int MSG_USER_REMOVED = 7;
- private static final int MSG_FEATURE_FLAG_CHANGED = 8;
+ private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
public MyHandler(Looper looper) {
super(looper);
@@ -560,8 +646,8 @@ public class ForceAppStandbyTracker {
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
- public void notifyFeatureFlagChanged() {
- obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget();
+ public void notifyForcedAppStandbyFeatureFlagChanged() {
+ obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
}
public void doUserRemoved(int userId) {
@@ -615,7 +701,7 @@ public class ForceAppStandbyTracker {
l.onForceAllAppsStandbyChanged(sender);
}
return;
- case MSG_FEATURE_FLAG_CHANGED:
+ case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
// Feature flag for forced app standby changed.
final boolean unblockAlarms;
synchronized (mLock) {
@@ -839,6 +925,18 @@ public class ForceAppStandbyTracker {
pw.println(isForceAllAppsStandbyEnabled());
pw.print(indent);
+ pw.print("Small Battery Device: ");
+ pw.println(isSmallBatteryDevice());
+
+ pw.print(indent);
+ pw.print("Force all apps standby for small battery device: ");
+ pw.println(mForceAllAppStandbyForSmallBattery);
+
+ pw.print(indent);
+ pw.print("Charging: ");
+ pw.println(mIsCharging);
+
+ pw.print(indent);
pw.print("Foreground uids: [");
String sep = "";
@@ -877,6 +975,11 @@ public class ForceAppStandbyTracker {
final long token = proto.start(fieldId);
proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
+ proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE,
+ isSmallBatteryDevice());
+ proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
+ mForceAllAppStandbyForSmallBattery);
+ proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsCharging);
for (int i = 0; i < mForegroundUids.size(); i++) {
if (mForegroundUids.valueAt(i)) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 02cfe3dc75e5..46a35ec800ba 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -25,6 +25,7 @@ import static android.system.OsConstants.SOCK_DGRAM;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.IIpSecService;
import android.net.INetd;
import android.net.IpSecAlgorithm;
@@ -62,7 +63,6 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils;
@@ -83,7 +83,7 @@ public class IpSecService extends IIpSecService.Stub {
private static final String NETD_SERVICE_NAME = "netd";
private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
+ new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
private static final int MAX_PORT_BIND_ATTEMPTS = 10;
@@ -104,10 +104,10 @@ public class IpSecService extends IIpSecService.Stub {
private final Context mContext;
/**
- * The next non-repeating global ID for tracking resources between users, this service,
- * and kernel data structures. Accessing this variable is not thread safe, so it is
- * only read or modified within blocks synchronized on IpSecService.this. We want to
- * avoid -1 (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
+ * The next non-repeating global ID for tracking resources between users, this service, and
+ * kernel data structures. Accessing this variable is not thread safe, so it is only read or
+ * modified within blocks synchronized on IpSecService.this. We want to avoid -1
+ * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
*/
@GuardedBy("IpSecService.this")
private int mNextResourceId = 1;
@@ -536,14 +536,14 @@ public class IpSecService extends IIpSecService.Stub {
private final class TransformRecord extends KernelResourceRecord {
private final IpSecConfig mConfig;
- private final SpiRecord[] mSpis;
+ private final SpiRecord mSpi;
private final EncapSocketRecord mSocket;
TransformRecord(
- int resourceId, IpSecConfig config, SpiRecord[] spis, EncapSocketRecord socket) {
+ int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
super(resourceId);
mConfig = config;
- mSpis = spis;
+ mSpi = spi;
mSocket = socket;
}
@@ -551,29 +551,26 @@ public class IpSecService extends IIpSecService.Stub {
return mConfig;
}
- public SpiRecord getSpiRecord(int direction) {
- return mSpis[direction];
+ public SpiRecord getSpiRecord() {
+ return mSpi;
}
/** always guarded by IpSecService#this */
@Override
public void freeUnderlyingResources() {
- for (int direction : DIRECTIONS) {
- int spi = mSpis[direction].getSpi();
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityAssociation(
- mResourceId,
- direction,
- mConfig.getLocalAddress(),
- mConfig.getRemoteAddress(),
- spi);
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
- }
+ int spi = mSpi.getSpi();
+ try {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecDeleteSecurityAssociation(
+ mResourceId,
+ mConfig.getSourceAddress(),
+ mConfig.getDestinationAddress(),
+ spi);
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
}
getResourceTracker().give();
@@ -597,10 +594,8 @@ public class IpSecService extends IIpSecService.Stub {
.append(super.toString())
.append(", mSocket=")
.append(mSocket)
- .append(", mSpis[OUT].mResourceId=")
- .append(mSpis[IpSecTransform.DIRECTION_OUT].mResourceId)
- .append(", mSpis[IN].mResourceId=")
- .append(mSpis[IpSecTransform.DIRECTION_IN].mResourceId)
+ .append(", mSpi.mResourceId=")
+ .append(mSpi.mResourceId)
.append(", mConfig=")
.append(mConfig)
.append("}");
@@ -609,23 +604,16 @@ public class IpSecService extends IIpSecService.Stub {
}
private final class SpiRecord extends KernelResourceRecord {
- private final int mDirection;
- private final String mLocalAddress;
- private final String mRemoteAddress;
+ private final String mSourceAddress;
+ private final String mDestinationAddress;
private int mSpi;
private boolean mOwnedByTransform = false;
- SpiRecord(
- int resourceId,
- int direction,
- String localAddress,
- String remoteAddress,
- int spi) {
+ SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
super(resourceId);
- mDirection = direction;
- mLocalAddress = localAddress;
- mRemoteAddress = remoteAddress;
+ mSourceAddress = sourceAddress;
+ mDestinationAddress = destinationAddress;
mSpi = spi;
}
@@ -646,7 +634,7 @@ public class IpSecService extends IIpSecService.Stub {
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
+ mResourceId, mSourceAddress, mDestinationAddress, mSpi);
} catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception
} catch (RemoteException e) {
@@ -662,6 +650,10 @@ public class IpSecService extends IIpSecService.Stub {
return mSpi;
}
+ public String getDestinationAddress() {
+ return mDestinationAddress;
+ }
+
public void setOwnedByTransform() {
if (mOwnedByTransform) {
// Programming error
@@ -689,12 +681,10 @@ public class IpSecService extends IIpSecService.Stub {
.append(super.toString())
.append(", mSpi=")
.append(mSpi)
- .append(", mDirection=")
- .append(mDirection)
- .append(", mLocalAddress=")
- .append(mLocalAddress)
- .append(", mRemoteAddress=")
- .append(mRemoteAddress)
+ .append(", mSourceAddress=")
+ .append(mSourceAddress)
+ .append(", mDestinationAddress=")
+ .append(mDestinationAddress)
.append(", mOwnedByTransform=")
.append(mOwnedByTransform)
.append("}");
@@ -772,14 +762,17 @@ public class IpSecService extends IIpSecService.Stub {
/** @hide */
@VisibleForTesting
public IpSecService(Context context, IpSecServiceConfiguration config) {
- this(context, config, (fd, uid) -> {
- try{
- TrafficStats.setThreadStatsUid(uid);
- TrafficStats.tagFileDescriptor(fd);
- } finally {
- TrafficStats.clearThreadStatsUid();
- }
- });
+ this(
+ context,
+ config,
+ (fd, uid) -> {
+ try {
+ TrafficStats.setThreadStatsUid(uid);
+ TrafficStats.tagFileDescriptor(fd);
+ } finally {
+ TrafficStats.clearThreadStatsUid();
+ }
+ });
}
/** @hide */
@@ -845,8 +838,8 @@ public class IpSecService extends IIpSecService.Stub {
*/
private static void checkDirection(int direction) {
switch (direction) {
- case IpSecTransform.DIRECTION_OUT:
- case IpSecTransform.DIRECTION_IN:
+ case IpSecManager.DIRECTION_OUT:
+ case IpSecManager.DIRECTION_IN:
return;
}
throw new IllegalArgumentException("Invalid Direction: " + direction);
@@ -855,10 +848,8 @@ public class IpSecService extends IIpSecService.Stub {
/** Get a new SPI and maintain the reservation in the system server */
@Override
public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
- int direction, String remoteAddress, int requestedSpi, IBinder binder)
- throws RemoteException {
- checkDirection(direction);
- checkInetAddress(remoteAddress);
+ String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
+ checkInetAddress(destinationAddress);
/* requestedSpi can be anything in the int range, so no check is needed. */
checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
@@ -866,28 +857,21 @@ public class IpSecService extends IIpSecService.Stub {
final int resourceId = mNextResourceId++;
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
- String localAddress = "";
-
try {
if (!userRecord.mSpiQuotaTracker.isAvailable()) {
return new IpSecSpiResponse(
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
+
spi =
mSrvConfig
.getNetdInstance()
- .ipSecAllocateSpi(
- resourceId,
- direction,
- localAddress,
- remoteAddress,
- requestedSpi);
+ .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
userRecord.mSpiRecords.put(
resourceId,
new RefcountedResource<SpiRecord>(
- new SpiRecord(resourceId, direction, localAddress, remoteAddress, spi),
- binder));
+ new SpiRecord(resourceId, "", destinationAddress, spi), binder));
} catch (ServiceSpecificException e) {
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
return new IpSecSpiResponse(
@@ -1032,27 +1016,27 @@ public class IpSecService extends IIpSecService.Stub {
}
@VisibleForTesting
- void validateAlgorithms(IpSecConfig config, int direction) throws IllegalArgumentException {
- IpSecAlgorithm auth = config.getAuthentication(direction);
- IpSecAlgorithm crypt = config.getEncryption(direction);
- IpSecAlgorithm aead = config.getAuthenticatedEncryption(direction);
-
- // Validate the algorithm set
- Preconditions.checkArgument(
- aead != null || crypt != null || auth != null,
- "No Encryption or Authentication algorithms specified");
- Preconditions.checkArgument(
- auth == null || auth.isAuthentication(),
- "Unsupported algorithm for Authentication");
- Preconditions.checkArgument(
+ void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
+ IpSecAlgorithm auth = config.getAuthentication();
+ IpSecAlgorithm crypt = config.getEncryption();
+ IpSecAlgorithm aead = config.getAuthenticatedEncryption();
+
+ // Validate the algorithm set
+ Preconditions.checkArgument(
+ aead != null || crypt != null || auth != null,
+ "No Encryption or Authentication algorithms specified");
+ Preconditions.checkArgument(
+ auth == null || auth.isAuthentication(),
+ "Unsupported algorithm for Authentication");
+ Preconditions.checkArgument(
crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
- Preconditions.checkArgument(
- aead == null || aead.isAead(),
- "Unsupported algorithm for Authenticated Encryption");
- Preconditions.checkArgument(
- aead == null || (auth == null && crypt == null),
- "Authenticated Encryption is mutually exclusive with other Authentication "
- + "or Encryption algorithms");
+ Preconditions.checkArgument(
+ aead == null || aead.isAead(),
+ "Unsupported algorithm for Authenticated Encryption");
+ Preconditions.checkArgument(
+ aead == null || (auth == null && crypt == null),
+ "Authenticated Encryption is mutually exclusive with other Authentication "
+ + "or Encryption algorithms");
}
/**
@@ -1062,29 +1046,6 @@ public class IpSecService extends IIpSecService.Stub {
private void checkIpSecConfig(IpSecConfig config) {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- if (config.getLocalAddress() == null) {
- throw new IllegalArgumentException("Invalid null Local InetAddress");
- }
-
- if (config.getRemoteAddress() == null) {
- throw new IllegalArgumentException("Invalid null Remote InetAddress");
- }
-
- switch (config.getMode()) {
- case IpSecTransform.MODE_TRANSPORT:
- if (!config.getLocalAddress().isEmpty()) {
- throw new IllegalArgumentException("Non-empty Local Address");
- }
- // Must be valid, and not a wildcard
- checkInetAddress(config.getRemoteAddress());
- break;
- case IpSecTransform.MODE_TUNNEL:
- break;
- default:
- throw new IllegalArgumentException(
- "Invalid IpSecTransform.mode: " + config.getMode());
- }
-
switch (config.getEncapType()) {
case IpSecTransform.ENCAP_NONE:
break;
@@ -1103,11 +1064,36 @@ public class IpSecService extends IIpSecService.Stub {
throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
}
- for (int direction : DIRECTIONS) {
- validateAlgorithms(config, direction);
+ validateAlgorithms(config);
+
+ // Retrieve SPI record; will throw IllegalArgumentException if not found
+ SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
+
+ // If no remote address is supplied, then use one from the SPI.
+ if (TextUtils.isEmpty(config.getDestinationAddress())) {
+ config.setDestinationAddress(s.getDestinationAddress());
+ }
+
+ // All remote addresses must match
+ if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
+ throw new IllegalArgumentException("Mismatched remote addresseses.");
+ }
+
+ // This check is technically redundant due to the chain of custody between the SPI and
+ // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
+ // the transform, this will prevent us from messing up.
+ checkInetAddress(config.getDestinationAddress());
+
+ // Require a valid source address for all transforms.
+ checkInetAddress(config.getSourceAddress());
- // Retrieve SPI record; will throw IllegalArgumentException if not found
- userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId(direction));
+ switch (config.getMode()) {
+ case IpSecTransform.MODE_TRANSPORT:
+ case IpSecTransform.MODE_TUNNEL:
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid IpSecTransform.mode: " + config.getMode());
}
}
@@ -1127,13 +1113,12 @@ public class IpSecService extends IIpSecService.Stub {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- // Avoid resizing by creating a dependency array of min-size 3 (1 UDP encap + 2 SPIs)
- List<RefcountedResource> dependencies = new ArrayList<>(3);
+ // Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI)
+ List<RefcountedResource> dependencies = new ArrayList<>(2);
if (!userRecord.mTransformQuotaTracker.isAvailable()) {
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
- SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
int encapType, encapLocalPort = 0, encapRemotePort = 0;
EncapSocketRecord socketRecord = null;
@@ -1149,51 +1134,46 @@ public class IpSecService extends IIpSecService.Stub {
encapRemotePort = c.getEncapRemotePort();
}
- for (int direction : DIRECTIONS) {
- IpSecAlgorithm auth = c.getAuthentication(direction);
- IpSecAlgorithm crypt = c.getEncryption(direction);
- IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
+ IpSecAlgorithm auth = c.getAuthentication();
+ IpSecAlgorithm crypt = c.getEncryption();
+ IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
- RefcountedResource<SpiRecord> refcountedSpiRecord =
- userRecord.mSpiRecords.getRefcountedResourceOrThrow(
- c.getSpiResourceId(direction));
- dependencies.add(refcountedSpiRecord);
+ RefcountedResource<SpiRecord> refcountedSpiRecord =
+ userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
+ dependencies.add(refcountedSpiRecord);
+ SpiRecord spiRecord = refcountedSpiRecord.getResource();
- spis[direction] = refcountedSpiRecord.getResource();
- int spi = spis[direction].getSpi();
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecAddSecurityAssociation(
- resourceId,
- c.getMode(),
- direction,
- c.getLocalAddress(),
- c.getRemoteAddress(),
- (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
- spi,
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- (crypt != null) ? crypt.getName() : "",
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort);
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
+ try {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecAddSecurityAssociation(
+ resourceId,
+ c.getMode(),
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+ spiRecord.getSpi(),
+ (auth != null) ? auth.getName() : "",
+ (auth != null) ? auth.getKey() : new byte[] {},
+ (auth != null) ? auth.getTruncationLengthBits() : 0,
+ (crypt != null) ? crypt.getName() : "",
+ (crypt != null) ? crypt.getKey() : new byte[] {},
+ (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+ (authCrypt != null) ? authCrypt.getName() : "",
+ (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+ (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+ encapType,
+ encapLocalPort,
+ encapRemotePort);
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
// Both SAs were created successfully, time to construct a record and lock it away
userRecord.mTransformRecords.put(
resourceId,
new RefcountedResource<TransformRecord>(
- new TransformRecord(resourceId, c, spis, socketRecord),
+ new TransformRecord(resourceId, c, spiRecord, socketRecord),
binder,
dependencies.toArray(new RefcountedResource[dependencies.size()])));
return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
@@ -1217,9 +1197,9 @@ public class IpSecService extends IIpSecService.Stub {
*/
@Override
public synchronized void applyTransportModeTransform(
- ParcelFileDescriptor socket, int resourceId) throws RemoteException {
+ ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
+ checkDirection(direction);
// Get transform record; if no transform is found, will throw IllegalArgumentException
TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
@@ -1230,17 +1210,15 @@ public class IpSecService extends IIpSecService.Stub {
IpSecConfig c = info.getConfig();
try {
- for (int direction : DIRECTIONS) {
- mSrvConfig
- .getNetdInstance()
- .ipSecApplyTransportModeTransform(
- socket.getFileDescriptor(),
- resourceId,
- direction,
- c.getLocalAddress(),
- c.getRemoteAddress(),
- info.getSpiRecord(direction).getSpi());
- }
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecApplyTransportModeTransform(
+ socket.getFileDescriptor(),
+ resourceId,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ info.getSpiRecord().getSpi());
} catch (ServiceSpecificException e) {
if (e.errorCode == EINVAL) {
throw new IllegalArgumentException(e.toString());
@@ -1251,13 +1229,13 @@ public class IpSecService extends IIpSecService.Stub {
}
/**
- * Remove a transport mode transform from a socket, applying the default (empty) policy. This
- * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
- * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
- * used: reserved for future improved input validation.
+ * Remove transport mode transforms from a socket, applying the default (empty) policy. This
+ * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
+ * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
+ * reserved for future improved input validation.
*/
@Override
- public synchronized void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
+ public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
throws RemoteException {
try {
mSrvConfig
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 35f83e41071c..30432df418ed 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -84,6 +84,7 @@ public class Watchdog extends Thread {
"/system/bin/sdcard",
"/system/bin/surfaceflinger",
"media.extractor", // system/bin/mediaextractor
+ "media.metrics", // system/bin/mediametrics
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
"com.android.bluetooth", // Bluetooth service
};
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c9b9a4038d4e..48c678ebd0e8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
@@ -192,11 +193,11 @@ import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -370,6 +371,7 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.RemoteAnimationDefinition;
import android.view.View;
import android.view.WindowManager;
@@ -397,6 +399,7 @@ import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -6242,7 +6245,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Clear its pending alarms
AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
- ami.removeAlarmsForUid(uid);
+ ami.removeAlarmsForUid(appInfo.uid);
}
} catch (RemoteException e) {
}
@@ -7260,15 +7263,22 @@ public class ActivityManagerService extends IActivityManager.Stub
}
ProfilerInfo profilerInfo = null;
- String agent = null;
+ String preBindAgent = null;
if (mProfileApp != null && mProfileApp.equals(processName)) {
mProfileProc = app;
- profilerInfo = (mProfilerInfo != null && mProfilerInfo.profileFile != null) ?
- new ProfilerInfo(mProfilerInfo) : null;
- agent = mProfilerInfo != null ? mProfilerInfo.agent : null;
+ if (mProfilerInfo != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfilerInfo.profileFile != null
+ || mProfilerInfo.attachAgentDuringBind;
+ profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+ if (!mProfilerInfo.attachAgentDuringBind) {
+ preBindAgent = mProfilerInfo.agent;
+ }
+ }
} else if (app.instr != null && app.instr.mProfileFile != null) {
profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
- null);
+ null, false);
}
boolean enableTrackAllocation = false;
@@ -7337,8 +7347,8 @@ public class ActivityManagerService extends IActivityManager.Stub
// If we were asked to attach an agent on startup, do so now, before we're binding
// application code.
- if (agent != null) {
- thread.attachAgent(agent);
+ if (preBindAgent != null) {
+ thread.attachAgent(preBindAgent);
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
@@ -8386,21 +8396,11 @@ public class ActivityManagerService extends IActivityManager.Stub
// entering picture-in-picture (this will prompt the user to authenticate if the
// device is currently locked).
try {
- dismissKeyguard(token, new IKeyguardDismissCallback.Stub() {
- @Override
- public void onDismissError() throws RemoteException {
- // Do nothing
- }
-
+ dismissKeyguard(token, new KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() throws RemoteException {
mHandler.post(enterPipRunnable);
}
-
- @Override
- public void onDismissCancelled() throws RemoteException {
- // Do nothing
- }
}, null /* message */);
} catch (RemoteException e) {
// Local call
@@ -13676,7 +13676,8 @@ public class ActivityManagerService extends IActivityManager.Stub
* not.
*/
private void enforceSystemHasVrFeature() {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
throw new UnsupportedOperationException("VR mode not supported on this device!");
}
}
@@ -13735,9 +13736,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -13769,9 +13768,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public boolean isVrModePackageEnabled(ComponentName packageName) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -25135,6 +25132,16 @@ public class ActivityManagerService extends IActivityManager.Stub
public boolean canStartMoreUsers() {
return mUserController.canStartMoreUsers();
}
+
+ @Override
+ public void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) {
+ mUserController.setSwitchingFromSystemUserMessage(switchingFromSystemUserMessage);
+ }
+
+ @Override
+ public void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
+ mUserController.setSwitchingToSystemUserMessage(switchingToSystemUserMessage);
+ }
}
/**
@@ -25439,4 +25446,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
}
+
+ @Override
+ public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition)
+ throws RemoteException {
+ enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "registerRemoteAnimations");
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ r.registerRemoteAnimations(definition);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 4f60e1731a50..1240f5e664aa 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -109,6 +109,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
private boolean mAutoStop;
private boolean mStreaming; // Streaming the profiling output to a file.
private String mAgent; // Agent to attach on startup.
+ private boolean mAttachAgentDuringBind; // Whether agent should be attached late.
private int mDisplayId;
private int mWindowingMode;
private int mActivityType;
@@ -296,7 +297,21 @@ final class ActivityManagerShellCommand extends ShellCommand {
} else if (opt.equals("--streaming")) {
mStreaming = true;
} else if (opt.equals("--attach-agent")) {
+ if (mAgent != null) {
+ cmd.getErrPrintWriter().println(
+ "Multiple --attach-agent(-bind) not supported");
+ return false;
+ }
+ mAgent = getNextArgRequired();
+ mAttachAgentDuringBind = false;
+ } else if (opt.equals("--attach-agent-bind")) {
+ if (mAgent != null) {
+ cmd.getErrPrintWriter().println(
+ "Multiple --attach-agent(-bind) not supported");
+ return false;
+ }
mAgent = getNextArgRequired();
+ mAttachAgentDuringBind = true;
} else if (opt.equals("-R")) {
mRepeat = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("-S")) {
@@ -384,7 +399,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
}
}
profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
- mStreaming, mAgent);
+ mStreaming, mAgent, mAttachAgentDuringBind);
}
pw.println("Starting: " + intent);
@@ -766,7 +781,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
return -1;
}
profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
- null);
+ null, false);
}
try {
@@ -2544,6 +2559,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" (use with --start-profiler)");
pw.println(" -P <FILE>: like above, but profiling stops when app goes idle");
pw.println(" --attach-agent <agent>: attach the given agent before binding");
+ pw.println(" --attach-agent-bind <agent>: attach the given agent during binding");
pw.println(" -R: repeat the activity launch <COUNT> times. Prior to each repeat,");
pw.println(" the top activity will be finished.");
pw.println(" -S: force stop the target app before starting the activity");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b74c8da611a8..3bef87794c24 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -21,6 +21,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
@@ -170,6 +171,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
+import android.view.RemoteAnimationDefinition;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
@@ -372,6 +374,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
}
+ String getLifecycleDescription(String reason) {
+ return "packageName=" + packageName + ", state=" + state + ", reason=" + reason;
+ }
+
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
@@ -1481,6 +1487,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
case ANIM_OPEN_CROSS_PROFILE_APPS:
service.mWindowManager.overridePendingAppTransitionStartCrossProfileApps();
break;
+ case ANIM_REMOTE_ANIMATION:
+ service.mWindowManager.overridePendingAppTransitionRemote(
+ pendingOptions.getRemoteAnimationAdapter());
+ break;
default:
Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
@@ -1609,15 +1619,18 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
mStackSupervisor.mStoppingActivities.remove(this);
mStackSupervisor.mGoingToSleepActivities.remove(this);
- // If an activity is not in the paused state when becoming visible, cycle to the paused
- // state.
- if (state != PAUSED) {
+ // If the activity is stopped or stopping, cycle to the paused state.
+ if (state == STOPPED || state == STOPPING) {
+ // Capture reason before state change
+ final String reason = getLifecycleDescription("makeVisibleIfNeeded");
+
// An activity must be in the {@link PAUSING} state for the system to validate
// the move to {@link PAUSED}.
state = PAUSING;
service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
PauseActivityItem.obtain(finishing, false /* userLeaving */,
- configChangeFlags, false /* dontReport */));
+ configChangeFlags, false /* dontReport */)
+ .setDescription(reason));
}
} catch (Exception e) {
// Just skip on any failure; we'll make it visible when it next restarts.
@@ -2778,6 +2791,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return mStackSupervisor.topRunningActivityLocked() == this;
}
+ void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ mWindowContainerController.registerRemoteAnimations(definition);
+ }
+
@Override
public String toString() {
if (stringName != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a343965ab848..0904c8e71df2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -84,14 +84,14 @@ import static com.android.server.am.proto.ActivityStackProto.FULLSCREEN;
import static com.android.server.am.proto.ActivityStackProto.ID;
import static com.android.server.am.proto.ActivityStackProto.RESUMED_ACTIVITY;
import static com.android.server.am.proto.ActivityStackProto.TASKS;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static java.lang.Integer.MAX_VALUE;
@@ -2639,7 +2639,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
next.clearOptionsLocked();
mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken,
ResumeActivityItem.obtain(next.app.repProcState,
- mService.isNextTransitionForward()));
+ mService.isNextTransitionForward())
+ .setDescription(next.getLifecycleDescription(
+ "resumeTopActivityInnerLocked")));
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ next);
@@ -3419,7 +3421,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
EventLogTags.writeAmStopActivity(
r.userId, System.identityHashCode(r), r.shortComponentName);
mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
- StopActivityItem.obtain(r.visible, r.configChangeFlags));
+ StopActivityItem.obtain(r.visible, r.configChangeFlags)
+ .setDescription(r.getLifecycleDescription("stopActivityLocked")));
if (shouldSleepOrShutDownActivities()) {
r.setSleeping(true);
}
@@ -4225,7 +4228,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
- DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
+ DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)
+ .setDescription(r.getLifecycleDescription("destroyActivityLocked")));
} catch (Exception e) {
// We can just ignore exceptions here... if the process
// has crashed, our death notification will clean things
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b567303b3ae5..8168cba213f5 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.Manifest.permission.ACTIVITY_EMBEDDING;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
@@ -93,7 +94,7 @@ import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS;
import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static java.lang.Integer.MAX_VALUE;
@@ -162,6 +163,7 @@ import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
@@ -1422,7 +1424,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
+ lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward())
+ .setDescription(r.getLifecycleDescription("realStartActivityLocked"));
} else {
lifecycleItem = PauseActivityItem.obtain();
}
@@ -1680,6 +1683,18 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+
+ // Check permission for remote animations
+ final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
+ if (adapter != null && mService.checkPermission(
+ CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+ != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with remoteAnimationAdapter";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
}
return true;
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 4d7bc1e4af56..79f3fe3ffd9f 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -27,13 +27,13 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_OCCLUDED;
import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_SHOWING;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_UNSET;
import android.app.ActivityManagerInternal.SleepToken;
import android.os.IBinder;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a327a018bc25..c7210a835767 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -217,6 +217,18 @@ class UserController implements Handler.Callback {
private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
/**
+ * Messages for for switching from {@link android.os.UserHandle#SYSTEM}.
+ */
+ @GuardedBy("mLock")
+ private String mSwitchingFromSystemUserMessage;
+
+ /**
+ * Messages for for switching to {@link android.os.UserHandle#SYSTEM}.
+ */
+ @GuardedBy("mLock")
+ private String mSwitchingToSystemUserMessage;
+
+ /**
* Callbacks that are still active after {@link #USER_SWITCH_TIMEOUT_MS}
*/
@GuardedBy("mLock")
@@ -1189,7 +1201,8 @@ class UserController implements Handler.Callback {
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
- mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second);
+ mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
+ getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage());
}
private void dispatchForegroundProfileChanged(int userId) {
@@ -1799,6 +1812,30 @@ class UserController implements Handler.Callback {
return mLockPatternUtils.isLockScreenDisabled(userId);
}
+ void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) {
+ synchronized (mLock) {
+ mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
+ }
+ }
+
+ void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
+ synchronized (mLock) {
+ mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
+ }
+ }
+
+ private String getSwitchingFromSystemUserMessage() {
+ synchronized (mLock) {
+ return mSwitchingFromSystemUserMessage;
+ }
+ }
+
+ private String getSwitchingToSystemUserMessage() {
+ synchronized (mLock) {
+ return mSwitchingToSystemUserMessage;
+ }
+ }
+
void dump(PrintWriter pw, boolean dumpAll) {
synchronized (mLock) {
pw.println(" mStartedUsers:");
@@ -2079,9 +2116,11 @@ class UserController implements Handler.Callback {
mService.installEncryptionUnawareProviders(userId);
}
- void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser) {
+ void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, toUser,
- true /* above system */);
+ true /* above system */, switchingFromSystemUserMessage,
+ switchingToSystemUserMessage);
d.show();
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 3e6934f67e4e..afcba3bfe330 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -53,7 +53,8 @@ final class UserSwitchingDialog extends AlertDialog
private boolean mStartedUser;
public UserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser,
- UserInfo newUser, boolean aboveSystem) {
+ UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage,
+ String switchingToSystemUserMessage) {
super(context);
mService = service;
@@ -65,7 +66,7 @@ final class UserSwitchingDialog extends AlertDialog
// Custom view due to alignment and font size requirements
View view = LayoutInflater.from(getContext()).inflate(R.layout.user_switching_dialog, null);
- String viewMessage;
+ String viewMessage = null;
if (UserManager.isSplitSystemUser() && newUser.id == UserHandle.USER_SYSTEM) {
viewMessage = res.getString(R.string.user_logging_out_message, oldUser.name);
} else if (UserManager.isDeviceInDemoMode(context)) {
@@ -75,7 +76,17 @@ final class UserSwitchingDialog extends AlertDialog
viewMessage = res.getString(R.string.demo_starting_message);
}
} else {
- viewMessage = res.getString(R.string.user_switching_message, newUser.name);
+ if (oldUser.id == UserHandle.USER_SYSTEM) {
+ viewMessage = switchingFromSystemUserMessage;
+ } else if (newUser.id == UserHandle.USER_SYSTEM) {
+ viewMessage = switchingToSystemUserMessage;
+ }
+
+ // If switchingFromSystemUserMessage or switchingToSystemUserMessage is null, fallback
+ // to system message.
+ if (viewMessage == null) {
+ viewMessage = res.getString(R.string.user_switching_message, newUser.name);
+ }
}
((TextView) view.findViewById(R.id.message)).setText(viewMessage);
setView(view);
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 40a93c1c2808..51499f735bcf 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -122,10 +122,12 @@ public class SyncJobService extends JobService {
final long startUptime = mJobStartUptimes.get(jobId);
final long nowUptime = SystemClock.uptimeMillis();
+ final long runtime = nowUptime - startUptime;
+
if (startUptime == 0) {
wtf("Job " + jobId + " start uptime not found: "
+ " params=" + jobParametersToString(params));
- } else if ((nowUptime - startUptime) > 60 * 1000) {
+ } else if (runtime > 60 * 1000) {
// WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
// (1 minute threshold.)
if (!mStartedSyncs.get(jobId)) {
@@ -134,6 +136,12 @@ public class SyncJobService extends JobService {
+ " nowUptime=" + nowUptime
+ " params=" + jobParametersToString(params));
}
+ } else if (runtime < 10 * 1000) {
+ // Job stopped too soon. WTF.
+ wtf("Job " + jobId + " stopped in " + runtime + " ms: "
+ + " startUptime=" + startUptime
+ + " nowUptime=" + nowUptime
+ + " params=" + jobParametersToString(params));
}
mStartedSyncs.delete(jobId);
@@ -183,6 +191,7 @@ public class SyncJobService extends JobService {
return "job:null";
} else {
return "job:#" + params.getJobId() + ":"
+ + "sr=[" + params.getStopReason() + "/" + params.getDebugStopReason() + "]:"
+ SyncOperation.maybeCreateFromJobExtras(params.getExtras());
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 02e4fe00f893..a55fec5246d3 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2009,5 +2009,14 @@ public final class DisplayManagerService extends SystemService {
mDisplayPowerController.persistBrightnessSliderEvents();
}
}
+
+ @Override
+ public void onOverlayChanged() {
+ synchronized (mSyncRoot) {
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked(false);
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 733ed3d81555..bd1dbf9c46e8 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -934,12 +934,14 @@ public final class JobSchedulerService extends com.android.server.SystemService
* @param uid Uid of the calling client.
* @param jobId Id of the job, provided at schedule-time.
*/
- public boolean cancelJob(int uid, int jobId) {
+ public boolean cancelJob(int uid, int jobId, int callingUid) {
JobStatus toCancel;
synchronized (mLock) {
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
if (toCancel != null) {
- cancelJobImplLocked(toCancel, null, "cancel() called by app");
+ cancelJobImplLocked(toCancel, null,
+ "cancel() called by app, callingUid=" + callingUid
+ + " uid=" + uid + " jobId=" + jobId);
}
return (toCancel != null);
}
@@ -2341,7 +2343,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
final int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
+ JobSchedulerService.this.cancelJobsForUid(uid,
+ "cancelAll() called by app, callingUid=" + uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2353,7 +2356,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJob(uid, jobId);
+ JobSchedulerService.this.cancelJob(uid, jobId, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2466,7 +2469,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
for (int i=0; i<mActiveServices.size(); i++) {
final JobServiceContext jc = mActiveServices.get(i);
final JobStatus js = jc.getRunningJobLocked();
- if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId)) {
+ if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
foundSome = true;
pw.print("Timing out: ");
js.printUniqueId(pw);
@@ -2506,7 +2509,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
} else {
pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
- if (!cancelJob(pkgUid, jobId)) {
+ if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
pw.println("No matching job found.");
}
}
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 709deeb81ac9..83a3c1993d4b 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -312,13 +312,14 @@ public final class JobServiceContext implements ServiceConnection {
return mTimeoutElapsed;
}
- boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId) {
+ boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId,
+ String reason) {
final JobStatus executing = getRunningJobLocked();
if (executing != null && (userId == UserHandle.USER_ALL || userId == executing.getUserId())
&& (pkgName == null || pkgName.equals(executing.getSourcePackageName()))
&& (!matchJobId || jobId == executing.getJobId())) {
if (mVerb == VERB_EXECUTING) {
- mParams.setStopReason(JobParameters.REASON_TIMEOUT);
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT, reason);
sendStopMessageLocked("force timeout from shell");
return true;
}
@@ -537,7 +538,7 @@ public final class JobServiceContext implements ServiceConnection {
}
return;
}
- mParams.setStopReason(arg1);
+ mParams.setStopReason(arg1, debugReason);
if (arg1 == JobParameters.REASON_PREEMPT) {
mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
NO_PREFERRED_UID;
@@ -687,7 +688,7 @@ public final class JobServiceContext implements ServiceConnection {
// Not an error - client ran out of time.
Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
"sending onStop: " + getRunningJobNameLocked());
- mParams.setStopReason(JobParameters.REASON_TIMEOUT);
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out");
sendStopMessageLocked("timeout while executing");
break;
default:
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ee08c3874028..879c0242d936 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -63,7 +63,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemProperties;
@@ -78,11 +77,10 @@ import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.security.keystore.KeychainProtectionParameter;
import android.security.keystore.UserNotAuthenticatedException;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
-import android.security.keystore.RecoveryManagerException;
+import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.KeychainSnapshot;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -90,6 +88,7 @@ import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1874,6 +1873,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mSpManager.removeUser(userId);
mStorage.removeUser(userId);
mStrongAuth.removeUser(userId);
+ cleanSpCache();
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
@@ -1968,7 +1968,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public RecoveryData getRecoveryData(@NonNull byte[] account) throws RemoteException {
+ public KeychainSnapshot getRecoveryData(@NonNull byte[] account) throws RemoteException {
return mRecoverableKeyStoreManager.getRecoveryData(account);
}
@@ -1997,7 +1997,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public void setRecoverySecretTypes(@NonNull @RecoveryMetadata.UserSecretType
+ public void setRecoverySecretTypes(@NonNull @KeychainProtectionParameter.UserSecretType
int[] secretTypes) throws RemoteException {
mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
}
@@ -2014,7 +2014,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public void recoverySecretAvailable(@NonNull RecoveryMetadata recoverySecret)
+ public void recoverySecretAvailable(@NonNull KeychainProtectionParameter recoverySecret)
throws RemoteException {
mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
}
@@ -2022,7 +2022,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public byte[] startRecoverySession(@NonNull String sessionId,
@NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
- @NonNull byte[] vaultChallenge, @NonNull List<RecoveryMetadata> secrets)
+ @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParameter> secrets)
throws RemoteException {
return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey,
vaultParams, vaultChallenge, secrets);
@@ -2030,7 +2030,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
- @NonNull byte[] recoveryKeyBlob, @NonNull List<EntryRecoveryData> applicationKeys)
+ @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
return mRecoverableKeyStoreManager.recoverKeys(
sessionId, recoveryKeyBlob, applicationKeys);
@@ -2112,6 +2112,63 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
+ * A user's synthetic password does not change so it must be cached in certain circumstances to
+ * enable untrusted credential reset.
+ *
+ * Untrusted credential reset will be removed in a future version (b/68036371) at which point
+ * this cache is no longer needed as the SP will always be known when changing the user's
+ * credential.
+ */
+ @GuardedBy("mSpManager")
+ private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
+
+ private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
+ // Update the SP cache, removing the entry when allowed
+ synchronized (mSpManager) {
+ if (shouldCacheSpForUser(userId)) {
+ Slog.i(TAG, "Caching SP for user " + userId);
+ mSpCache.put(userId, auth);
+ } else {
+ Slog.i(TAG, "Not caching SP for user " + userId);
+ mSpCache.delete(userId);
+ }
+ }
+ }
+
+ /** Clean up the SP cache by removing unneeded entries. */
+ private void cleanSpCache() {
+ synchronized (mSpManager) {
+ // Preserve indicies after removal by iterating backwards
+ for (int i = mSpCache.size() - 1; i >= 0; --i) {
+ final int userId = mSpCache.keyAt(i);
+ if (!shouldCacheSpForUser(userId)) {
+ Slog.i(TAG, "Uncaching SP for user " + userId);
+ mSpCache.removeAt(i);
+ }
+ }
+ }
+ }
+
+ private boolean shouldCacheSpForUser(@UserIdInt int userId) {
+ // Before the user setup has completed, an admin could be installed that requires the SP to
+ // be cached (see below).
+ if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, userId) == 0) {
+ return true;
+ }
+
+ // If the user has an admin which can perform an untrusted credential reset, the SP needs to
+ // be cached. If there isn't a DevicePolicyManager then there can't be an admin in the first
+ // place so caching is not necessary.
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+ if (dpmi == null) {
+ return false;
+ }
+ return dpmi.canUserHaveUntrustedCredentialReset(userId);
+ }
+
+ /**
* Precondition: vold and keystore unlocked.
*
* Create new synthetic password, set up synthetic password blob protected by the supplied
@@ -2126,9 +2183,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* 3. Once a user is migrated to have synthetic password, its value will never change, no matter
* whether the user changes his lockscreen PIN or clear/reset it. When the user clears its
* lockscreen PIN, we still maintain the existing synthetic password in a password blob
- * protected by a default PIN. The only exception is when the DPC performs an untrusted
- * credential change, in which case we have no way to derive the existing synthetic password
- * and has to create a new one.
+ * protected by a default PIN.
* 4. The user SID is linked with synthetic password, but its cleared/re-created when the user
* clears/re-creates his lockscreen PIN.
*
@@ -2148,13 +2203,23 @@ public class LockSettingsService extends ILockSettings.Stub {
* This is the untrusted credential reset, OR the user sets a new lockscreen password
* FOR THE FIRST TIME on a SP-enabled device. New credential and new SID will be created
*/
+ @GuardedBy("mSpManager")
@VisibleForTesting
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
String credential, int credentialType, int requestedQuality,
int userId) throws RemoteException {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
- AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
- credentialHash, credential, userId);
+ // Load from the cache or a make a new one
+ AuthenticationToken auth = mSpCache.get(userId);
+ if (auth != null) {
+ // If the synthetic password has been cached, we can only be in case 3., described
+ // above, for an untrusted credential reset so a new SID is still needed.
+ mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
+ } else {
+ auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
+ credentialHash, credential, userId);
+ }
+ onAuthTokenKnownForUser(userId, auth);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
return null;
@@ -2269,6 +2334,8 @@ public class LockSettingsService extends ILockSettings.Stub {
trustManager.setDeviceLockedForUser(userId, false);
}
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
+
+ onAuthTokenKnownForUser(userId, authResult.authToken);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
@@ -2287,6 +2354,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* SID is gone. We also clear password from (software-based) keystore and vold, which will be
* added back when new password is set in future.
*/
+ @GuardedBy("mSpManager")
private long setLockCredentialWithAuthTokenLocked(String credential, int credentialType,
AuthenticationToken auth, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
@@ -2334,6 +2402,7 @@ public class LockSettingsService extends ILockSettings.Stub {
return newHandle;
}
+ @GuardedBy("mSpManager")
private void spBasedSetLockCredentialInternalLocked(String credential, int credentialType,
String savedCredential, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
@@ -2369,13 +2438,19 @@ public class LockSettingsService extends ILockSettings.Stub {
setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
userId);
mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
+ onAuthTokenKnownForUser(userId, auth);
} else if (response != null
- && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR){
+ && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
// We are performing an untrusted credential change i.e. by DevicePolicyManager.
// So provision a new SP and SID. This would invalidate existing escrow tokens.
// Still support this for now but this flow will be removed in the next release.
-
Slog.w(TAG, "Untrusted credential change invoked");
+
+ if (mSpCache.get(userId) == null) {
+ throw new IllegalStateException(
+ "Untrusted credential reset not possible without cached SP");
+ }
+
initializeSyntheticPasswordLocked(null, credential, credentialType, requestedQuality,
userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
@@ -2486,8 +2561,9 @@ public class LockSettingsService extends ILockSettings.Stub {
private boolean setLockCredentialWithTokenInternal(String credential, int type,
long tokenHandle, byte[] token, int requestedQuality, int userId) throws RemoteException {
+ final AuthenticationResult result;
synchronized (mSpManager) {
- AuthenticationResult result = mSpManager.unwrapTokenBasedSyntheticPassword(
+ result = mSpManager.unwrapTokenBasedSyntheticPassword(
getGateKeeperService(), tokenHandle, token, userId);
if (result.authToken == null) {
Slog.w(TAG, "Invalid escrow token supplied");
@@ -2508,8 +2584,9 @@ public class LockSettingsService extends ILockSettings.Stub {
setLockCredentialWithAuthTokenLocked(credential, type, result.authToken,
requestedQuality, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId);
- return true;
}
+ onAuthTokenKnownForUser(userId, result.authToken);
+ return true;
}
@Override
@@ -2529,6 +2606,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
unlockUser(userId, null, authResult.authToken.deriveDiskEncryptionKey());
+ onAuthTokenKnownForUser(userId, authResult.authToken);
}
@Override
@@ -2610,6 +2688,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private class DeviceProvisionedObserver extends ContentObserver {
private final Uri mDeviceProvisionedUri = Settings.Global.getUriFor(
Settings.Global.DEVICE_PROVISIONED);
+ private final Uri mUserSetupCompleteUri = Settings.Secure.getUriFor(
+ Settings.Secure.USER_SETUP_COMPLETE);
private boolean mRegistered;
@@ -2627,6 +2707,8 @@ public class LockSettingsService extends ILockSettings.Stub {
reportDeviceSetupComplete();
clearFrpCredentialIfOwnerNotSecure();
}
+ } else if (mUserSetupCompleteUri.equals(uri)) {
+ cleanSpCache();
}
}
@@ -2678,6 +2760,8 @@ public class LockSettingsService extends ILockSettings.Stub {
if (register) {
mContext.getContentResolver().registerContentObserver(mDeviceProvisionedUri,
false, this);
+ mContext.getContentResolver().registerContentObserver(mUserSetupCompleteUri,
+ false, this, UserHandle.USER_ALL);
} else {
mContext.getContentResolver().unregisterContentObserver(this);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 5fe11b11fa5a..38745f6cfd28 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -16,15 +16,14 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryMetadata.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
+import android.security.keystore.KeychainProtectionParameter;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -251,12 +250,12 @@ public class KeySyncTask implements Runnable {
}
// TODO: store raw data in RecoveryServiceMetadataEntry and generate Parcelables later
// TODO: use Builder.
- RecoveryMetadata metadata = new RecoveryMetadata(
+ KeychainProtectionParameter metadata = new KeychainProtectionParameter(
/*userSecretType=*/ TYPE_LOCKSCREEN,
/*lockScreenUiFormat=*/ getUiFormat(mCredentialType, mCredential),
/*keyDerivationParams=*/ KeyDerivationParams.createSha256Params(salt),
/*secret=*/ new byte[0]);
- ArrayList<RecoveryMetadata> metadataList = new ArrayList<>();
+ ArrayList<KeychainProtectionParameter> metadataList = new ArrayList<>();
metadataList.add(metadata);
int snapshotVersion = incrementSnapshotVersion(recoveryAgentUid);
@@ -265,7 +264,7 @@ public class KeySyncTask implements Runnable {
mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false);
// TODO: use Builder.
- mRecoverySnapshotStorage.put(recoveryAgentUid, new RecoveryData(
+ mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot(
snapshotVersion,
/*recoveryMetadata=*/ metadataList,
/*applicationKeyBlobs=*/ createApplicationKeyEntries(encryptedApplicationKeys),
@@ -308,7 +307,7 @@ public class KeySyncTask implements Runnable {
*/
private boolean shoudCreateSnapshot(int recoveryAgentUid) {
int[] types = mRecoverableKeyStoreDb.getRecoverySecretTypes(mUserId, recoveryAgentUid);
- if (!ArrayUtils.contains(types, RecoveryMetadata.TYPE_LOCKSCREEN)) {
+ if (!ArrayUtils.contains(types, KeychainProtectionParameter.TYPE_LOCKSCREEN)) {
// Only lockscreen type is supported.
// We will need to pass extra argument to KeySyncTask to support custom pass phrase.
return false;
@@ -331,14 +330,14 @@ public class KeySyncTask implements Runnable {
* @return The format - either pattern, pin, or password.
*/
@VisibleForTesting
- @RecoveryMetadata.LockScreenUiFormat static int getUiFormat(
+ @KeychainProtectionParameter.LockScreenUiFormat static int getUiFormat(
int credentialType, String credential) {
if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
- return RecoveryMetadata.TYPE_PATTERN;
+ return KeychainProtectionParameter.TYPE_PATTERN;
} else if (isPin(credential)) {
- return RecoveryMetadata.TYPE_PIN;
+ return KeychainProtectionParameter.TYPE_PIN;
} else {
- return RecoveryMetadata.TYPE_PASSWORD;
+ return KeychainProtectionParameter.TYPE_PASSWORD;
}
}
@@ -401,12 +400,12 @@ public class KeySyncTask implements Runnable {
return keyGenerator.generateKey();
}
- private static List<EntryRecoveryData> createApplicationKeyEntries(
+ private static List<WrappedApplicationKey> createApplicationKeyEntries(
Map<String, byte[]> encryptedApplicationKeys) {
- ArrayList<EntryRecoveryData> keyEntries = new ArrayList<>();
+ ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>();
for (String alias : encryptedApplicationKeys.keySet()) {
keyEntries.add(
- new EntryRecoveryData(
+ new WrappedApplicationKey(
alias,
encryptedApplicationKeys.get(alias)));
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 7658178d43da..f14af4b52a2f 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -34,9 +34,9 @@ import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
+import android.security.keystore.KeychainProtectionParameter;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.security.keystore.RecoveryManager;
import android.util.Log;
@@ -45,7 +45,6 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKe
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
-import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.KeyFactory;
@@ -171,11 +170,12 @@ public class RecoverableKeyStoreManager {
* @return recovery data
* @hide
*/
- public @NonNull RecoveryData getRecoveryData(@NonNull byte[] account)
+ public @NonNull
+ KeychainSnapshot getRecoveryData(@NonNull byte[] account)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
- RecoveryData snapshot = mSnapshotStorage.get(uid);
+ KeychainSnapshot snapshot = mSnapshotStorage.get(uid);
if (snapshot == null) {
throw new ServiceSpecificException(ERROR_NO_SNAPSHOT_PENDING);
}
@@ -257,7 +257,7 @@ public class RecoverableKeyStoreManager {
* @hide
*/
public void setRecoverySecretTypes(
- @NonNull @RecoveryMetadata.UserSecretType int[] secretTypes)
+ @NonNull @KeychainProtectionParameter.UserSecretType int[] secretTypes)
throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
@@ -292,9 +292,9 @@ public class RecoverableKeyStoreManager {
}
public void recoverySecretAvailable(
- @NonNull RecoveryMetadata recoverySecret) throws RemoteException {
+ @NonNull KeychainProtectionParameter recoverySecret) throws RemoteException {
int uid = Binder.getCallingUid();
- if (recoverySecret.getLockScreenUiFormat() == RecoveryMetadata.TYPE_LOCKSCREEN) {
+ if (recoverySecret.getLockScreenUiFormat() == KeychainProtectionParameter.TYPE_LOCKSCREEN) {
throw new SecurityException(
"Caller " + uid + " is not allowed to set lock screen secret");
}
@@ -320,13 +320,13 @@ public class RecoverableKeyStoreManager {
@NonNull byte[] verifierPublicKey,
@NonNull byte[] vaultParams,
@NonNull byte[] vaultChallenge,
- @NonNull List<RecoveryMetadata> secrets)
+ @NonNull List<KeychainProtectionParameter> secrets)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
if (secrets.size() != 1) {
- throw new UnsupportedOperationException("Only a single RecoveryMetadata is supported");
+ throw new UnsupportedOperationException("Only a single KeychainProtectionParameter is supported");
}
PublicKey publicKey;
@@ -384,7 +384,7 @@ public class RecoverableKeyStoreManager {
public Map<String, byte[]> recoverKeys(
@NonNull String sessionId,
@NonNull byte[] encryptedRecoveryKey,
- @NonNull List<EntryRecoveryData> applicationKeys)
+ @NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
@@ -474,9 +474,9 @@ public class RecoverableKeyStoreManager {
*/
private Map<String, byte[]> recoverApplicationKeys(
@NonNull byte[] recoveryKey,
- @NonNull List<EntryRecoveryData> applicationKeys) throws RemoteException {
+ @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>();
- for (EntryRecoveryData applicationKey : applicationKeys) {
+ for (WrappedApplicationKey applicationKey : applicationKeys) {
String alias = applicationKey.getAlias();
byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index eb2da8077b36..8bba2122787d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -404,7 +404,7 @@ public class RecoverableKeyStoreDb {
/**
* Updates the list of user secret types used for end-to-end encryption.
* If no secret types are set, recovery snapshot will not be created.
- * See {@code RecoveryMetadata}
+ * See {@code KeychainProtectionParameter}
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 158b1e34d019..62bb41ec48aa 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,7 +17,7 @@
package com.android.server.locksettings.recoverablekeystore.storage;
import android.annotation.Nullable;
-import android.security.keystore.RecoveryData;
+import android.security.keystore.KeychainSnapshot;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -34,12 +34,12 @@ import com.android.internal.annotations.GuardedBy;
*/
public class RecoverySnapshotStorage {
@GuardedBy("this")
- private final SparseArray<RecoveryData> mSnapshotByUid = new SparseArray<>();
+ private final SparseArray<KeychainSnapshot> mSnapshotByUid = new SparseArray<>();
/**
* Sets the latest {@code snapshot} for the recovery agent {@code uid}.
*/
- public synchronized void put(int uid, RecoveryData snapshot) {
+ public synchronized void put(int uid, KeychainSnapshot snapshot) {
mSnapshotByUid.put(uid, snapshot);
}
@@ -47,7 +47,7 @@ public class RecoverySnapshotStorage {
* Returns the latest snapshot for the recovery agent {@code uid}, or null if none exists.
*/
@Nullable
- public synchronized RecoveryData get(int uid) {
+ public synchronized KeychainSnapshot get(int uid) {
return mSnapshotByUid.get(uid);
}
diff --git a/services/core/java/com/android/server/media/MediaUpdateService.java b/services/core/java/com/android/server/media/MediaUpdateService.java
new file mode 100644
index 000000000000..016d062db726
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaUpdateService.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.IMediaResourceMonitor;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.PatternMatcher;
+import android.os.ServiceManager;
+import android.media.IMediaExtractorUpdateService;
+
+import java.lang.Exception;
+
+/** This class provides a system service that manages media framework updates. */
+public class MediaUpdateService extends SystemService {
+ private static final String TAG = "MediaUpdateService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String MEDIA_UPDATE_PACKAGE_NAME = "com.android.media.update";
+ private static final String EXTRACTOR_UPDATE_SERVICE_NAME = "media.extractor.update";
+
+ private IMediaExtractorUpdateService mMediaExtractorUpdateService;
+
+ public MediaUpdateService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ // TODO: Uncomment below once sepolicy change is landed.
+ /*
+ if ("userdebug".equals(android.os.Build.TYPE) || "eng".equals(android.os.Build.TYPE)) {
+ connect();
+ registerBroadcastReceiver();
+ }
+ */
+ }
+
+ private void connect() {
+ IBinder binder = ServiceManager.getService(EXTRACTOR_UPDATE_SERVICE_NAME);
+ if (binder != null) {
+ try {
+ binder.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Slog.w(TAG, "mediaextractor died; reconnecting");
+ mMediaExtractorUpdateService = null;
+ connect();
+ }
+ }, 0);
+ } catch (Exception e) {
+ binder = null;
+ }
+ }
+ if (binder != null) {
+ mMediaExtractorUpdateService = IMediaExtractorUpdateService.Stub.asInterface(binder);
+ packageStateChanged();
+ } else {
+ Slog.w(TAG, EXTRACTOR_UPDATE_SERVICE_NAME + " not found.");
+ }
+ }
+
+ private void registerBroadcastReceiver() {
+ BroadcastReceiver updateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM)
+ != UserHandle.USER_SYSTEM) {
+ // Ignore broadcast for non system users. We don't want to update system
+ // service multiple times.
+ return;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED:
+ if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
+ // The existing package is updated. Will be handled with the
+ // following ACTION_PACKAGE_ADDED case.
+ return;
+ }
+ packageStateChanged();
+ break;
+ case Intent.ACTION_PACKAGE_CHANGED:
+ packageStateChanged();
+ break;
+ case Intent.ACTION_PACKAGE_ADDED:
+ packageStateChanged();
+ break;
+ }
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(MEDIA_UPDATE_PACKAGE_NAME, PatternMatcher.PATTERN_LITERAL);
+
+ getContext().registerReceiverAsUser(updateReceiver, UserHandle.ALL, filter,
+ null /* broadcast permission */, null /* handler */);
+ }
+
+ private void packageStateChanged() {
+ ApplicationInfo packageInfo = null;
+ boolean pluginsAvailable = false;
+ try {
+ packageInfo = getContext().getPackageManager().getApplicationInfo(
+ MEDIA_UPDATE_PACKAGE_NAME, PackageManager.MATCH_SYSTEM_ONLY);
+ pluginsAvailable = packageInfo.enabled;
+ } catch (Exception e) {
+ Slog.v(TAG, "package '" + MEDIA_UPDATE_PACKAGE_NAME + "' not installed");
+ }
+ loadExtractorPlugins(
+ (packageInfo != null && pluginsAvailable) ? packageInfo.sourceDir : "");
+ }
+
+ private void loadExtractorPlugins(String apkPath) {
+ try {
+ if (mMediaExtractorUpdateService != null) {
+ mMediaExtractorUpdateService.loadPlugins(apkPath);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Error in loadPlugins", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 37e6ae98cb0a..42093e87e455 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -71,6 +71,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -98,6 +99,9 @@ abstract public class ManagedServices {
static final String ATT_APPROVED_LIST = "approved";
static final String ATT_USER_ID = "user";
static final String ATT_IS_PRIMARY = "primary";
+ static final String ATT_VERSION = "version";
+
+ static final int DB_VERSION = 1;
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -295,6 +299,8 @@ abstract public class ManagedServices {
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
out.startTag(null, getConfig().xmlTag);
+ out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+
if (forBackup) {
trimApprovedListsAccordingToInstalledServices();
}
@@ -336,6 +342,14 @@ abstract public class ManagedServices {
public void readXml(XmlPullParser parser)
throws XmlPullParserException, IOException {
+ // upgrade xml
+ int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
+ final List<UserInfo> activeUsers = mUm.getUsers(true);
+ for (UserInfo userInfo : activeUsers) {
+ upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier());
+ }
+
+ // read grants
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
@@ -346,6 +360,7 @@ abstract public class ManagedServices {
if (type == XmlPullParser.START_TAG) {
if (TAG_MANAGED_SERVICES.equals(tag)) {
Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml");
+
final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
final boolean isPrimary =
@@ -360,6 +375,8 @@ abstract public class ManagedServices {
rebindServices(false);
}
+ protected void upgradeXml(final int xmlVersion, final int userId) {}
+
private void loadAllowedComponentsFromSettings() {
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -379,7 +396,7 @@ abstract public class ManagedServices {
Slog.d(TAG, "Done loading approved values from settings");
}
- private void addApprovedList(String approved, int userId, boolean isPrimary) {
+ protected void addApprovedList(String approved, int userId, boolean isPrimary) {
if (TextUtils.isEmpty(approved)) {
approved = "";
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 575c44d15209..2f6618f56a80 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -434,6 +434,7 @@ public class NotificationManagerService extends SystemService {
}
}
}
+
String defaultDndAccess = getContext().getResources().getString(
com.android.internal.R.string.config_defaultDndAccessPackages);
if (defaultListenerAccess != null) {
@@ -446,6 +447,29 @@ public class NotificationManagerService extends SystemService {
}
}
}
+
+ readDefaultAssistant(userId);
+ }
+
+ protected void readDefaultAssistant(int userId) {
+ String defaultAssistantAccess = getContext().getResources().getString(
+ com.android.internal.R.string.config_defaultAssistantAccessPackage);
+ if (defaultAssistantAccess != null) {
+ // Gather all notification assistant components for candidate pkg. There should
+ // only be one
+ Set<ComponentName> approvedAssistants =
+ mAssistants.queryPackageForServices(defaultAssistantAccess,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ for (ComponentName cn : approvedAssistants) {
+ try {
+ getBinderService().setNotificationAssistantAccessGrantedForUser(cn,
+ userId, true);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
void readPolicyXml(InputStream stream, boolean forRestore)
@@ -1155,6 +1179,7 @@ public class NotificationManagerService extends SystemService {
}
}
+ @VisibleForTesting
void clearNotifications() {
mEnqueuedNotifications.clear();
mNotificationList.clear();
@@ -1374,7 +1399,8 @@ public class NotificationManagerService extends SystemService {
AppGlobals.getPackageManager(), getContext().getPackageManager(),
getLocalService(LightsManager.class),
new NotificationListeners(AppGlobals.getPackageManager()),
- new NotificationAssistants(AppGlobals.getPackageManager()),
+ new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
+ AppGlobals.getPackageManager()),
new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
null, snoozeHelper, new NotificationUsageStats(getContext()),
new AtomicFile(new File(systemDir, "notification_policy.xml")),
@@ -2917,12 +2943,34 @@ public class NotificationManagerService extends SystemService {
}
}
+ /**
+ * Sets the notification policy. Apps that target API levels below
+ * {@link android.os.Build.VERSION_CODES#P} cannot make DND silence
+ * {@link Policy#PRIORITY_CATEGORY_ALARMS} or
+ * {@link Policy#PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER}
+ */
@Override
public void setNotificationPolicy(String pkg, Policy policy) {
enforcePolicyAccess(pkg, "setNotificationPolicy");
final long identity = Binder.clearCallingIdentity();
try {
+ final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
+ 0, UserHandle.getUserId(MY_UID));
+
+ if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.O_MR1) {
+ Policy currPolicy = mZenModeHelper.getNotificationPolicy();
+
+ int priorityCategories = policy.priorityCategories
+ | (currPolicy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS)
+ | (currPolicy.priorityCategories &
+ Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER);
+ policy = new Policy(priorityCategories,
+ policy.priorityCallSenders, policy.priorityMessageSenders,
+ policy.suppressedVisualEffects);
+ }
+
mZenModeHelper.setNotificationPolicy(policy);
+ } catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5531,8 +5579,9 @@ public class NotificationManagerService extends SystemService {
public class NotificationAssistants extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
- public NotificationAssistants(IPackageManager pm) {
- super(getContext(), mNotificationLock, mUserProfiles, pm);
+ public NotificationAssistants(Context context, Object lock, UserProfiles up,
+ IPackageManager pm) {
+ super(context, lock, up, pm);
}
@Override
@@ -5637,6 +5686,14 @@ public class NotificationManagerService extends SystemService {
public boolean isEnabled() {
return !getServices().isEmpty();
}
+
+ protected void upgradeXml(final int xmlVersion, final int userId) {
+ if (xmlVersion == 0) {
+ // one time approval of the OOB assistant
+ Slog.d(TAG, "Approving default notification assistant for user " + userId);
+ readDefaultAssistant(userId);
+ }
+ }
}
public class NotificationListeners extends ManagedServices {
@@ -6050,7 +6107,7 @@ public class NotificationManagerService extends SystemService {
public static final String USAGE = "help\n"
+ "allow_listener COMPONENT [user_id]\n"
+ "disallow_listener COMPONENT [user_id]\n"
- + "set_assistant COMPONENT\n"
+ + "allow_assistant COMPONENT\n"
+ "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
+ "disallow_dnd PACKAGE";
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index be66fe22ca1b..4b3758d2e293 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -281,13 +281,14 @@ public class Installer extends SystemService {
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
- @Nullable String seInfo, boolean downgrade)
+ @Nullable String seInfo, boolean downgrade, int targetSdkVersion)
throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
- dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
+ dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
+ targetSdkVersion);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b06b58385cc3..1717b3d1a5f7 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -652,6 +652,7 @@ public class LauncherAppsService extends SystemService {
activityInfo.name.equals(component.getClassName())) {
// Found an activity with category launcher that matches
// this component so ok to launch.
+ launchIntent.setPackage(null);
launchIntent.setComponent(component);
mContext.startActivityAsUser(launchIntent, opts, user);
return;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 03f662a49762..03950119ea13 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -260,12 +260,13 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
public void dexopt(String apkPath, int uid, @Nullable String pkgName,
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
- @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
+ @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
+ int targetSdkVersion)
throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 3.
- builder.append("3 ");
+ // The version. Right now it's 4.
+ builder.append("4 ");
builder.append("dexopt");
@@ -281,6 +282,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
encodeParameter(builder, sharedLibraries);
encodeParameter(builder, seInfo);
encodeParameter(builder, downgrade);
+ encodeParameter(builder, targetSdkVersion);
commands.add(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 730a9fdad420..91df87b713e8 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -274,7 +274,7 @@ public class PackageDexOptimizer {
// primary dex files.
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
- false /* downgrade*/);
+ false /* downgrade*/, pkg.applicationInfo.targetSdkVersion);
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -395,7 +395,7 @@ public class PackageDexOptimizer {
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
- options.isDowngrade());
+ options.isDowngrade(), info.targetSdkVersion);
}
return DEX_OPT_PERFORMED;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1e2f2b2df863..711ea13a7c15 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2697,6 +2697,12 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()
|| disabledPs.pkg == null) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Possibly deleted app: " + ps.dumpState_temp()
+ + "; path: " + (disabledPs.codePath == null ? "<<NULL>>":disabledPs.codePath)
+ + "; pkg: " + (disabledPs.pkg==null?"<<NULL>>":disabledPs.pkg.toString()));
+}
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
@@ -2748,6 +2754,10 @@ public class PackageManagerService extends IPackageManager.Stub
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "remove update; name: " + deletedAppName + ", exists? " + (deletedPkg != null));
+}
final String msg;
if (deletedPkg == null) {
// should have found an update, but, we didn't; remove everything
@@ -8311,6 +8321,8 @@ public class PackageManagerService extends IPackageManager.Stub
return scannedPkg;
}
+ // Temporary to catch potential issues with refactoring
+ private static boolean REFACTOR_DEBUG = true;
/**
* Adds a new package to the internal data structures during platform initialization.
* <p>After adding, the package is known to the system and available for querying.
@@ -8351,6 +8363,10 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mPackages) {
renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
final String realPkgName = getRealPackageName(pkg, renamedPkgName);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Add pkg: " + pkg.packageName + (realPkgName==null?"":", realName: " + realPkgName));
+}
if (realPkgName != null) {
ensurePackageRenamed(pkg, renamedPkgName);
}
@@ -8365,6 +8381,12 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_INSTALL && isSystemPkgUpdated) {
Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
}
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "SSP? " + scanSystemPartition
+ + ", exists? " + pkgAlreadyExists + (pkgAlreadyExists?" "+pkgSetting.toString():"")
+ + ", upgraded? " + isSystemPkgUpdated + (isSystemPkgUpdated?" "+disabledPkgSetting.toString():""));
+}
final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
? mSettings.getSharedUserLPw(pkg.mSharedUserId,
@@ -8376,6 +8398,12 @@ public class PackageManagerService extends IPackageManager.Stub
Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Shared UserID " + pkg.mSharedUserId
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+}
}
if (scanSystemPartition) {
@@ -8384,6 +8412,10 @@ public class PackageManagerService extends IPackageManager.Stub
// version on /data, cycle through all of its children packages and
// remove children that are no longer defined.
if (isSystemPkgUpdated) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Disable child packages");
+}
final int scannedChildCount = (pkg.childPackages != null)
? pkg.childPackages.size() : 0;
final int disabledChildCount = disabledPkgSetting.childPackageNames != null
@@ -8395,11 +8427,19 @@ public class PackageManagerService extends IPackageManager.Stub
for (int j = 0; j < scannedChildCount; j++) {
PackageParser.Package childPkg = pkg.childPackages.get(j);
if (childPkg.packageName.equals(disabledChildPackageName)) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Ignore " + disabledChildPackageName);
+}
disabledPackageAvailable = true;
break;
}
}
if (!disabledPackageAvailable) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Disable " + disabledChildPackageName);
+}
mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
}
}
@@ -8408,17 +8448,44 @@ public class PackageManagerService extends IPackageManager.Stub
disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
null /* originalPkgSetting */, null, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Scan disabled system package");
+Slog.e("TODD",
+ "Pre: " + request.pkgSetting.dumpState_temp());
+}
+final ScanResult result =
scanPackageOnlyLI(request, mFactoryTest, -1L);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Post: " + (result.success?result.pkgSetting.dumpState_temp():"FAILED scan"));
+}
}
}
}
final boolean newPkgChangedPaths =
pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "paths changed? " + newPkgChangedPaths
+ + "; old: " + pkg.codePath
+ + ", new: " + (pkgSetting==null?"<<NULL>>":pkgSetting.codePathString));
+}
final boolean newPkgVersionGreater =
pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode;
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "version greater? " + newPkgVersionGreater
+ + "; old: " + pkg.getLongVersionCode()
+ + ", new: " + (pkgSetting==null?"<<NULL>>":pkgSetting.versionCode));
+}
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
&& newPkgChangedPaths && newPkgVersionGreater;
+if (REFACTOR_DEBUG) {
+ Slog.e("TODD",
+ "system better? " + isSystemPkgBetter);
+}
if (isSystemPkgBetter) {
// The version of the application on /system is greater than the version on
// /data. Switch back to the application on /system.
@@ -8434,6 +8501,13 @@ public class PackageManagerService extends IPackageManager.Stub
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package changed;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+}
final InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
@@ -8445,6 +8519,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "THROW exception; system pkg version not good enough");
+}
// The version of the application on the /system partition is less than or
// equal to the version on the /data partition. Throw an exception and use
// the application already installed on the /data partition.
@@ -8470,6 +8548,11 @@ public class PackageManagerService extends IPackageManager.Stub
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.name);
+}
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
@@ -8484,6 +8567,13 @@ public class PackageManagerService extends IPackageManager.Stub
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package enabled;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+}
InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
@@ -8500,13 +8590,35 @@ public class PackageManagerService extends IPackageManager.Stub
+ " name: " + pkgSetting.name
+ "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
+ "; new: " + pkg.codePath + " @ " + pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package disabled;"
+ + " name: " + pkgSetting.name
+ + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
+ + "; new: " + pkg.codePath + " @ " + pkg.codePath);
+}
}
}
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Scan package");
+Slog.e("TODD",
+ "Pre: " + (pkgSetting==null?"<<NONE>>":pkgSetting.dumpState_temp()));
+}
final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
+if (REFACTOR_DEBUG) {
+pkgSetting = mSettings.getPackageLPr(pkg.packageName);
+Slog.e("TODD",
+ "Post: " + (pkgSetting==null?"<<NONE>>":pkgSetting.dumpState_temp()));
+}
if (shouldHideSystemApp) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Disable package: " + pkg.packageName);
+}
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
@@ -10652,6 +10764,26 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
}
+
+ // Verify that packages sharing a user with a privileged app are marked as privileged.
+ if (!pkg.isPrivileged() && (pkg.mSharedUserId != null)) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, false);
+ } catch (PackageManagerException ignore) {}
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
+ if ((platformPkgSetting.signatures.mSignatures != null) &&
+ (compareSignatures(platformPkgSetting.signatures.mSignatures,
+ pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
+ throw new PackageManagerException("Apps that share a user with a " +
+ "privileged app must themselves be marked as privileged. " +
+ pkg.packageName + " shares privileged user " +
+ pkg.mSharedUserId + ".");
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2b91b7d38b4f..2a2430c07980 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -97,6 +97,35 @@ public final class PackageSetting extends PackageSettingBase {
+ " " + name + "/" + appId + "}";
}
+ // Temporary to catch potential issues with refactoring
+ public String dumpState_temp() {
+ String flags = "";
+ flags += ((pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
+ flags += ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
+ if ("".equals(flags)) {
+ flags = "-";
+ }
+ String privFlags = "";
+ privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
+ privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
+ privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
+ if ("".equals(privFlags)) {
+ privFlags = "-";
+ }
+ return "PackageSetting{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + name + (realName == null ? "" : "("+realName+")") + "/" + appId + (sharedUser==null?"":" u:" + sharedUser.name+"("+sharedUserId+")")
+ + ", ver:" + versionCode
+ + ", path: " + codePath
+ + ", pABI: " + primaryCpuAbiString
+ + ", sABI: " + secondaryCpuAbiString
+ + ", oABI: " + cpuAbiOverrideString
+ + ", flags: " + flags
+ + ", privFlags: " + privFlags
+ + ", pkg: " + (pkg == null ? "<<NULL>>" : pkg.dumpState_temp())
+ + "}";
+ }
+
public void copyFrom(PackageSetting orig) {
super.copyFrom(orig);
doCopy(orig);
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 877da144730f..244613180d00 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.service.pm.PackageServiceDumpProto;
import android.util.ArraySet;
@@ -102,4 +103,8 @@ public final class SharedUserSetting extends SettingBase {
}
return pkgList;
}
+
+ public boolean isPrivileged() {
+ return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 50690cb0f33e..cc07d82554e9 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -25,12 +25,14 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -119,7 +121,8 @@ public class UserRestrictionsUtils {
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_CONFIG_BRIGHTNESS,
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
- UserManager.DISALLOW_AMBIENT_DISPLAY
+ UserManager.DISALLOW_AMBIENT_DISPLAY,
+ UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT
});
/**
@@ -542,6 +545,22 @@ public class UserRestrictionsUtils {
android.provider.Settings.Global.SAFE_BOOT_DISALLOWED,
newValue ? 1 : 0);
break;
+ case UserManager.DISALLOW_AIRPLANE_MODE:
+ if (newValue) {
+ final boolean airplaneMode = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+ if (airplaneMode) {
+ android.provider.Settings.Global.putInt(
+ context.getContentResolver(),
+ android.provider.Settings.Global.AIRPLANE_MODE_ON, 0);
+ // Post the intent.
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", 0);
+ context.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+ break;
}
} finally {
Binder.restoreCallingIdentity(id);
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
index d6281c51b3fa..a517d6d1a99e 100644
--- a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
@@ -111,6 +111,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
final long ident = mInjector.clearCallingIdentity();
try {
+ launchIntent.setPackage(null);
launchIntent.setComponent(component);
mContext.startActivityAsUser(launchIntent,
ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(), user);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0470607e26c9..88e4270ad07e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -48,7 +48,6 @@ import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
@@ -65,6 +64,9 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
@@ -267,6 +269,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
+import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
@@ -4209,20 +4212,23 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
return;
- }
-
- if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
+ } else if (mKeyguardOccluded && mKeyguardDelegate.isShowing()) {
+ mKeyguardDelegate.dismiss(new KeyguardDismissCallback() {
+ @Override
+ public void onDismissSucceeded() throws RemoteException {
+ mHandler.post(() -> {
+ startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
+ });
+ }
+ }, null /* message */);
+ return;
+ } else if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@Override
public void onKeyguardExitResult(boolean success) {
if (success) {
- try {
- ActivityManager.getService().stopAppSwitches();
- } catch (RemoteException e) {
- }
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
}
}
@@ -4232,11 +4238,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
// no keyguard stuff to worry about, just launch home!
- try {
- ActivityManager.getService().stopAppSwitches();
- } catch (RemoteException e) {
- }
if (mRecentsVisible) {
+ try {
+ ActivityManager.getService().stopAppSwitches();
+ } catch (RemoteException e) {}
+
// Hide Recents and notify it to launch Home
if (awakenFromDreams) {
awakenDreams();
@@ -4244,7 +4250,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
hideRecentApps(false, true);
} else {
// Otherwise, just launch Home
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
}
}
@@ -4867,7 +4872,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final int type = attrs.type;
final int fl = PolicyControl.getWindowFlags(win, attrs);
- final long fl2 = attrs.flags2;
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(win, null);
@@ -4889,12 +4893,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
- || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
- || (requestedSysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0;
+ || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
- final boolean layoutInCutout = (fl2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0;
sf.set(displayFrames.mStable);
@@ -5054,9 +5056,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// moving from a window that is not hiding the status bar to one that is.
cf.set(displayFrames.mRestricted);
}
- if (requestedFullscreen && !layoutInCutout) {
- pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
- }
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
@@ -5142,9 +5141,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
of.set(displayFrames.mUnrestricted);
df.set(displayFrames.mUnrestricted);
pf.set(displayFrames.mUnrestricted);
- if (requestedFullscreen && !layoutInCutout) {
- pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
- }
} else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
of.set(displayFrames.mRestricted);
df.set(displayFrames.mRestricted);
@@ -5219,15 +5215,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- // Ensure that windows that did not request to be laid out in the cutout don't get laid
- // out there.
- if (!layoutInCutout) {
+ final int cutoutMode = attrs.layoutInDisplayCutoutMode;
+ // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
+ // the cutout safe zone.
+ if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
final Rect displayCutoutSafeExceptMaybeTop = mTmpRect;
displayCutoutSafeExceptMaybeTop.set(displayFrames.mDisplayCutoutSafe);
- if (layoutInScreen && layoutInsetDecor) {
+ if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
+ && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
// At the top we have the status bar, so apps that are
- // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR already expect that there's an inset
- // there and we don't need to exclude the window from that area.
+ // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
+ // already expect that there's an inset there and we don't need to exclude
+ // the window from that area.
displayCutoutSafeExceptMaybeTop.top = Integer.MIN_VALUE;
}
pf.intersectUnchecked(displayCutoutSafeExceptMaybeTop);
@@ -5656,11 +5655,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public boolean allowAppAnimationsLw() {
- if (mShowingDream) {
- // If keyguard or dreams is currently visible, no reason to animate behind it.
- return false;
- }
- return true;
+ return !mShowingDream;
}
@Override
@@ -7637,6 +7632,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
void startDockOrHome(boolean fromHomeKey, boolean awakenFromDreams) {
+ try {
+ ActivityManager.getService().stopAppSwitches();
+ } catch (RemoteException e) {}
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+
if (awakenFromDreams) {
awakenDreams();
}
@@ -7676,11 +7676,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (false) {
// This code always brings home to the front.
- try {
- ActivityManager.getService().stopAppSwitches();
- } catch (RemoteException e) {
- }
- sendCloseSystemWindows();
startDockOrHome(false /*fromHomeKey*/, true /* awakenFromDreams */);
} else {
// This code brings home to the front or, if it is already
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 64a280c1cbe7..c05dd2af3e5b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -138,11 +138,6 @@ import java.lang.annotation.RetentionPolicy;
* </dl>
*/
public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
- // Navigation bar position values
- int NAV_BAR_LEFT = 1 << 0;
- int NAV_BAR_RIGHT = 1 << 1;
- int NAV_BAR_BOTTOM = 1 << 2;
-
@Retention(SOURCE)
@IntDef({NAV_BAR_LEFT, NAV_BAR_RIGHT, NAV_BAR_BOTTOM})
@interface NavigationBarPosition {}
@@ -1211,13 +1206,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Return true if it is okay to perform animations for an app transition
- * that is about to occur. You may return false for this if, for example,
- * the lock screen is currently displayed so the switch should happen
+ * that is about to occur. You may return false for this if, for example,
+ * the dream window is currently displayed so the switch should happen
* immediately.
*/
public boolean allowAppAnimationsLw();
-
/**
* A new window has been focused.
*/
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 1b5a5216909d..48a196dfcff1 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -32,6 +32,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import java.io.PrintWriter;
+import java.util.List;
/**
* A special helper class used by the WindowManager
@@ -90,7 +91,28 @@ public abstract class WindowOrientationListener {
mHandler = handler;
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
- mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DEVICE_ORIENTATION);
+ List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
+ Sensor wakeUpDeviceOrientationSensor = null;
+ Sensor nonWakeUpDeviceOrientationSensor = null;
+ /**
+ * Prefer the wakeup form of the sensor if implemented.
+ * It's OK to look for just two types of this sensor and use
+ * the last found. Typical devices will only have one sensor of
+ * this type.
+ */
+ for (Sensor s : l) {
+ if (s.isWakeUpSensor()) {
+ wakeUpDeviceOrientationSensor = s;
+ } else {
+ nonWakeUpDeviceOrientationSensor = s;
+ }
+ }
+
+ if (wakeUpDeviceOrientationSensor != null) {
+ mSensor = wakeUpDeviceOrientationSensor;
+ } else {
+ mSensor = nonWakeUpDeviceOrientationSensor;
+ }
if (mSensor != null) {
mOrientationJudge = new OrientationSensorJudge();
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 163b1600e0bd..659253f9d603 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -346,12 +346,12 @@ final class AccessibilityController {
final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
if (magnifying) {
switch (transition) {
- case AppTransition.TRANSIT_ACTIVITY_OPEN:
- case AppTransition.TRANSIT_TASK_OPEN:
- case AppTransition.TRANSIT_TASK_TO_FRONT:
- case AppTransition.TRANSIT_WALLPAPER_OPEN:
- case AppTransition.TRANSIT_WALLPAPER_CLOSE:
- case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ case WindowManager.TRANSIT_ACTIVITY_OPEN:
+ case WindowManager.TRANSIT_TASK_OPEN:
+ case WindowManager.TRANSIT_TASK_TO_FRONT:
+ case WindowManager.TRANSIT_WALLPAPER_OPEN:
+ case WindowManager.TRANSIT_WALLPAPER_CLOSE:
+ case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: {
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d4b437a5a25e..fc7ad09d5182 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -16,6 +16,28 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
@@ -75,9 +97,11 @@ import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
import android.view.DisplayListCanvas;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RemoteAnimationAdapter;
import android.view.RenderNode;
import android.view.ThreadedRenderer;
-import android.view.WindowManager;
+import android.view.WindowManager.TransitionFlags;
+import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -109,63 +133,6 @@ public class AppTransition implements Dump {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransition" : TAG_WM;
private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
- /** Not set up for a transition. */
- public static final int TRANSIT_UNSET = -1;
- /** No animation for transition. */
- public static final int TRANSIT_NONE = 0;
- /** A window in a new activity is being opened on top of an existing one in the same task. */
- public static final int TRANSIT_ACTIVITY_OPEN = 6;
- /** The window in the top-most activity is being closed to reveal the
- * previous activity in the same task. */
- public static final int TRANSIT_ACTIVITY_CLOSE = 7;
- /** A window in a new task is being opened on top of an existing one
- * in another activity's task. */
- public static final int TRANSIT_TASK_OPEN = 8;
- /** A window in the top-most activity is being closed to reveal the
- * previous activity in a different task. */
- public static final int TRANSIT_TASK_CLOSE = 9;
- /** A window in an existing task is being displayed on top of an existing one
- * in another activity's task. */
- public static final int TRANSIT_TASK_TO_FRONT = 10;
- /** A window in an existing task is being put below all other tasks. */
- public static final int TRANSIT_TASK_TO_BACK = 11;
- /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
- * does, effectively closing the wallpaper. */
- public static final int TRANSIT_WALLPAPER_CLOSE = 12;
- /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
- * effectively opening the wallpaper. */
- public static final int TRANSIT_WALLPAPER_OPEN = 13;
- /** A window in a new activity is being opened on top of an existing one, and both are on top
- * of the wallpaper. */
- public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
- /** The window in the top-most activity is being closed to reveal the previous activity, and
- * both are on top of the wallpaper. */
- public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
- /** A window in a new task is being opened behind an existing one in another activity's task.
- * The new window will show briefly and then be gone. */
- public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
- /** A window in a task is being animated in-place. */
- public static final int TRANSIT_TASK_IN_PLACE = 17;
- /** An activity is being relaunched (e.g. due to configuration change). */
- public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
- /** A task is being docked from recents. */
- public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
- /** Keyguard is going away */
- public static final int TRANSIT_KEYGUARD_GOING_AWAY = 20;
- /** Keyguard is going away with showing an activity behind that requests wallpaper */
- public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
- /** Keyguard is being occluded */
- public static final int TRANSIT_KEYGUARD_OCCLUDE = 22;
- /** Keyguard is being unoccluded */
- public static final int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
-
- /** Transition flag: Keyguard is going away, but keeping the notification shade open */
- public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE = 0x1;
- /** Transition flag: Keyguard is going away, but doesn't want an animation for it */
- public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION = 0x2;
- /** Transition flag: Keyguard is going away while it was showing the system wallpaper. */
- public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER = 0x4;
-
/** Fraction of animation at which the recents thumbnail stays completely transparent */
private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
/** Fraction of animation at which the recents thumbnail becomes completely transparent */
@@ -191,8 +158,8 @@ public class AppTransition implements Dump {
private final Context mContext;
private final WindowManagerService mService;
- private int mNextAppTransition = TRANSIT_UNSET;
- private int mNextAppTransitionFlags = 0;
+ private @TransitionType int mNextAppTransition = TRANSIT_UNSET;
+ private @TransitionFlags int mNextAppTransitionFlags = 0;
private int mLastUsedAppTransition = TRANSIT_UNSET;
private String mLastOpeningApp;
private String mLastClosingApp;
@@ -213,6 +180,8 @@ public class AppTransition implements Dump {
* }.
*/
private static final int NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS = 9;
+ private static final int NEXT_TRANSIT_TYPE_REMOTE = 10;
+
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
// These are the possible states for the enter/exit activities during a thumbnail transition
@@ -275,6 +244,8 @@ public class AppTransition implements Dump {
private final boolean mGridLayoutRecentsEnabled;
private final boolean mLowRamRecentsEnabled;
+ private RemoteAnimationController mRemoteAnimationController;
+
AppTransition(Context context, WindowManagerService service) {
mContext = context;
mService = service;
@@ -321,11 +292,11 @@ public class AppTransition implements Dump {
return mNextAppTransition != TRANSIT_UNSET;
}
- boolean isTransitionEqual(int transit) {
+ boolean isTransitionEqual(@TransitionType int transit) {
return mNextAppTransition == transit;
}
- int getAppTransition() {
+ @TransitionType int getAppTransition() {
return mNextAppTransition;
}
@@ -454,6 +425,9 @@ public class AppTransition implements Dump {
app.startDelayingAnimationStart();
}
}
+ if (mRemoteAnimationController != null) {
+ mRemoteAnimationController.goodToGo();
+ }
return redoLayout;
}
@@ -468,6 +442,7 @@ public class AppTransition implements Dump {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
mNextAppTransitionPackage = null;
mNextAppTransitionAnimationsSpecs.clear();
+ mRemoteAnimationController = null;
mNextAppTransitionAnimationsSpecsFuture = null;
mDefaultNextAppTransitionAnimationSpec = null;
mAnimationFinishedCallback = null;
@@ -476,7 +451,7 @@ public class AppTransition implements Dump {
void freeze() {
final int transit = mNextAppTransition;
- setAppTransition(AppTransition.TRANSIT_UNSET, 0 /* flags */);
+ setAppTransition(TRANSIT_UNSET, 0 /* flags */);
clear();
setReady();
notifyAppTransitionCancelledLocked(transit);
@@ -531,7 +506,7 @@ public class AppTransition implements Dump {
return redoLayout;
}
- private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
+ private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
+ (lp != null ? lp.packageName : null)
+ " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
@@ -567,7 +542,7 @@ public class AppTransition implements Dump {
return null;
}
- Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
+ Animation loadAnimationAttr(LayoutParams lp, int animAttr) {
int anim = 0;
Context context = mContext;
if (animAttr >= 0) {
@@ -583,7 +558,7 @@ public class AppTransition implements Dump {
return null;
}
- Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
+ Animation loadAnimationRes(LayoutParams lp, int resId) {
Context context = mContext;
if (resId >= 0) {
AttributeCache.Entry ent = getCachedAnimations(lp);
@@ -1551,6 +1526,10 @@ public class AppTransition implements Dump {
&& mNextAppTransition != TRANSIT_KEYGUARD_GOING_AWAY;
}
+ RemoteAnimationController getRemoteAnimationController() {
+ return mRemoteAnimationController;
+ }
+
/**
*
* @param frame These are the bounds of the window when it finishes the animation. This is where
@@ -1572,7 +1551,7 @@ public class AppTransition implements Dump {
* to the recents thumbnail and hence need to account for the surface being
* bigger.
*/
- Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
+ Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode,
int orientation, Rect frame, Rect displayFrame, Rect insets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
boolean freeform, int taskId) {
@@ -1788,7 +1767,7 @@ public class AppTransition implements Dump {
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
IRemoteCallback startedCallback) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
@@ -1796,14 +1775,12 @@ public class AppTransition implements Dump {
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
@@ -1813,7 +1790,7 @@ public class AppTransition implements Dump {
void overridePendingAppTransitionClipReveal(int startX, int startY,
int startWidth, int startHeight) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
@@ -1823,7 +1800,7 @@ public class AppTransition implements Dump {
void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
@@ -1831,14 +1808,12 @@ public class AppTransition implements Dump {
putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX, int startY,
int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1847,15 +1822,13 @@ public class AppTransition implements Dump {
srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
- public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
+ void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1878,15 +1851,13 @@ public class AppTransition implements Dump {
postAnimationCallback();
mNextAppTransitionCallback = onAnimationStartedCallback;
mAnimationFinishedCallback = onAnimationFinishedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1896,14 +1867,21 @@ public class AppTransition implements Dump {
}
}
- void overrideInPlaceAppTransition(String packageName, int anim) {
+ void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
if (isTransitionSet()) {
clear();
+ mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
+ mRemoteAnimationController = new RemoteAnimationController(mService,
+ remoteAnimationAdapter, mService.mH);
+ }
+ }
+
+ void overrideInPlaceAppTransition(String packageName, int anim) {
+ if (canOverridePendingAppTransition()) {
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
mNextAppTransitionPackage = packageName;
mNextAppTransitionInPlace = anim;
- } else {
- postAnimationCallback();
}
}
@@ -1911,13 +1889,18 @@ public class AppTransition implements Dump {
* @see {@link #NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS}
*/
void overridePendingAppTransitionStartCrossProfileApps() {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS;
postAnimationCallback();
}
}
+ private boolean canOverridePendingAppTransition() {
+ // Remote animations always take precedence
+ return isTransitionSet() && mNextAppTransitionType != NEXT_TRANSIT_TYPE_REMOTE;
+ }
+
/**
* If a future is set for the app transition specs, fetch it in another thread.
*/
@@ -2140,8 +2123,8 @@ public class AppTransition implements Dump {
* @return true if transition is not running and should not be skipped, false if transition is
* already running
*/
- boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent, int flags,
- boolean forceOverride) {
+ boolean prepareAppTransitionLocked(@TransitionType int transit, boolean alwaysKeepCurrent,
+ @TransitionFlags int flags, boolean forceOverride) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
+ " transit=" + appTransitionToString(transit)
+ " " + this
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index ae9f28bcb6a6..e9efd4ec9e3d 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -19,8 +19,8 @@ package com.android.server.wm;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -32,7 +32,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager.TaskSnapshot;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -40,6 +39,8 @@ import android.os.Looper;
import android.os.Message;
import android.util.Slog;
import android.view.IApplicationToken;
+import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.AttributeCache;
@@ -393,7 +394,7 @@ public class AppWindowContainerController
wtoken.mEnteringAnimation = false;
}
if (mService.mAppTransition.getAppTransition()
- == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
+ == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps.
final WindowState win =
mService.getDefaultDisplayContentLocked().findFocusedWindow();
@@ -695,6 +696,17 @@ public class AppWindowContainerController
}
}
+ public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ synchronized (mWindowMap) {
+ if (mContainer == null) {
+ Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
+ + " token: " + mToken);
+ return;
+ }
+ mContainer.registerRemoteAnimations(definition);
+ }
+ }
+
void reportStartingWindowDrawn() {
mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2c84cb1a4d6f..261065f05889 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -34,7 +34,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -91,6 +91,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.IApplicationToken;
+import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
@@ -106,7 +107,6 @@ import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.LinkedList;
class AppTokenList extends ArrayList<AppWindowToken> {
}
@@ -250,6 +250,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
private final Point mTmpPoint = new Point();
private final Rect mTmpRect = new Rect();
+ private RemoteAnimationDefinition mRemoteAnimationDefinition;
AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
@@ -378,7 +379,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
// been set by the app now.
mHiddenSetFromTransferredStartingWindow = false;
- setClientHidden(!visible);
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
@@ -394,7 +394,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
boolean runningAppAnimation = false;
- if (transit != AppTransition.TRANSIT_UNSET) {
+ if (transit != WindowManager.TRANSIT_UNSET) {
if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
delayed = runningAppAnimation = true;
}
@@ -463,6 +463,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
}
+ // Update the client visibility if we are not running an animation. Otherwise, we'll
+ // update client visibility state in onAnimationFinished.
+ if (!visible && !delayed) {
+ setClientHidden(true);
+ }
+
// If we are hidden but there is no delay needed we immediately
// apply the Surface transaction so that the ActivityManager
// can have some guarantee on the Surface state following
@@ -1427,13 +1433,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame);
} else if (mLetterbox != null) {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ // Make sure we have a transaction here, in case we're called outside of a transaction.
+ // This does not use mPendingTransaction, because SurfaceAnimator uses a
+ // global transaction in onAnimationEnd.
SurfaceControl.openTransaction();
try {
mLetterbox.hide(t);
} finally {
- // TODO: This should use pendingTransaction eventually, but right now things
- // happening on the animation finished callback are happening on the global
- // transaction.
SurfaceControl.mergeToGlobalTransaction(t);
SurfaceControl.closeTransaction();
}
@@ -1611,27 +1617,37 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// different animation is running.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
if (okToAnimate()) {
- final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
- if (a != null) {
- final TaskStack stack = getStack();
- mTmpPoint.set(0, 0);
- mTmpRect.setEmpty();
- if (stack != null) {
- stack.getRelativePosition(mTmpPoint);
- stack.getBounds(mTmpRect);
- mTmpRect.offsetTo(0, 0);
- }
- final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- mService.mAppTransition.canSkipFirstFrame(),
- mService.mAppTransition.getAppStackClipMode()),
- mService.mSurfaceAnimationRunner);
- if (a.getZAdjustment() == Animation.ZORDER_TOP) {
- mNeedsZBoost = true;
+ final AnimationAdapter adapter;
+ final TaskStack stack = getStack();
+ mTmpPoint.set(0, 0);
+ mTmpRect.setEmpty();
+ if (stack != null) {
+ stack.getRelativePosition(mTmpPoint);
+ stack.getBounds(mTmpRect);
+ mTmpRect.offsetTo(0, 0);
+ }
+ if (mService.mAppTransition.getRemoteAnimationController() != null) {
+ adapter = mService.mAppTransition.getRemoteAnimationController()
+ .createAnimationAdapter(this, mTmpPoint, mTmpRect);
+ } else {
+ final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
+ if (a != null) {
+ adapter = new LocalAnimationAdapter(
+ new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
+ mService.mAppTransition.canSkipFirstFrame(),
+ mService.mAppTransition.getAppStackClipMode()),
+ mService.mSurfaceAnimationRunner);
+ if (a.getZAdjustment() == Animation.ZORDER_TOP) {
+ mNeedsZBoost = true;
+ }
+ mTransit = transit;
+ mTransitFlags = mService.mAppTransition.getTransitFlags();
+ } else {
+ adapter = null;
}
+ }
+ if (adapter != null) {
startAnimation(getPendingTransaction(), adapter, !isVisible());
- mTransit = transit;
- mTransitFlags = mService.mAppTransition.getTransitFlags();
}
} else {
cancelAnimation();
@@ -1754,6 +1770,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
"AppWindowToken");
clearThumbnail();
+ setClientHidden(isHidden());
if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) {
getDisplayContent().computeImeTarget(true /* updateImeTarget */);
@@ -1884,6 +1901,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
mThumbnail = null;
}
+ void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ mRemoteAnimationDefinition = definition;
+ }
+
+ RemoteAnimationDefinition getRemoteAnimationDefinition() {
+ return mRemoteAnimationDefinition;
+ }
+
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a8e00dd53526..ef486614fd2a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -63,7 +63,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_C
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 03c0768cdec0..0cf8d2f85b62 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -29,7 +29,7 @@ import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 28b4c1dbeb8e..0171b56ffc47 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -136,7 +136,7 @@ class DragDropController {
outSurface.copyFrom(surface);
final IBinder winBinder = window.asBinder();
IBinder token = new Binder();
- mDragState = new DragState(mService, token, surface, flags, winBinder);
+ mDragState = new DragState(mService, this, token, surface, flags, winBinder);
mDragState.mPid = callerPid;
mDragState.mUid = callerUid;
mDragState.mOriginalAlpha = alpha;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index b9f437af20e4..1ac9b88749a7 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -42,6 +42,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.IUserManager;
+import android.os.UserManagerInternal;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
@@ -55,6 +56,7 @@ import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.internal.view.IDragAndDropPermissions;
+import com.android.server.LocalServices;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
@@ -116,10 +118,10 @@ class DragState {
private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
private Point mDisplaySize = new Point();
- DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
- int flags, IBinder localWin) {
+ DragState(WindowManagerService service, DragDropController controller, IBinder token,
+ SurfaceControl surface, int flags, IBinder localWin) {
mService = service;
- mDragDropController = service.mDragDropController;
+ mDragDropController = controller;
mToken = token;
mSurfaceControl = surface;
mFlags = flags;
@@ -318,15 +320,9 @@ class DragState {
mSourceUserId = UserHandle.getUserId(mUid);
- final IUserManager userManager =
- (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
- try {
- mCrossProfileCopyAllowed = !userManager.getUserRestrictions(mSourceUserId).getBoolean(
- UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Remote Exception calling UserManager: " + e);
- mCrossProfileCopyAllowed = false;
- }
+ final UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+ mCrossProfileCopyAllowed = !userManager.getUserRestriction(
+ mSourceUserId, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
@@ -534,7 +530,8 @@ class DragState {
final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
final DragAndDropPermissionsHandler dragAndDropPermissions;
- if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
+ if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0
+ && mData != null) {
dragAndDropPermissions = new DragAndDropPermissionsHandler(
mData,
mUid,
@@ -546,7 +543,9 @@ class DragState {
dragAndDropPermissions = null;
}
if (mSourceUserId != targetUserId){
- mData.fixUris(mSourceUserId);
+ if (mData != null) {
+ mData.fixUris(mSourceUserId);
+ }
}
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 69cbe4607cf1..62519e12d5d9 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -360,14 +360,19 @@ class PinnedStackController {
* Sets the Ime state and height.
*/
void setAdjustedForIme(boolean adjustedForIme, int imeHeight) {
- // Return early if there is no state change
- if (mIsImeShowing == adjustedForIme && mImeHeight == imeHeight) {
+ // Due to the order of callbacks from the system, we may receive an ime height even when
+ // {@param adjustedForIme} is false, and also a zero height when {@param adjustedForIme}
+ // is true. Instead, ensure that the ime state changes with the height and if the ime is
+ // showing, then the height is non-zero.
+ final boolean imeShowing = adjustedForIme && imeHeight > 0;
+ imeHeight = imeShowing ? imeHeight : 0;
+ if (imeShowing == mIsImeShowing && imeHeight == mImeHeight) {
return;
}
- mIsImeShowing = adjustedForIme;
+ mIsImeShowing = imeShowing;
mImeHeight = imeHeight;
- notifyImeVisibilityChanged(adjustedForIme, imeHeight);
+ notifyImeVisibilityChanged(imeShowing, imeHeight);
notifyMovementBoundsChanged(true /* fromImeAdjustment */);
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
new file mode 100644
index 000000000000..688b4ffcee4b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationFinishedCallback.Stub;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import java.util.ArrayList;
+
+/**
+ * Helper class to run app animations in a remote process.
+ */
+class RemoteAnimationController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
+ private static final long TIMEOUT_MS = 2000;
+
+ private final WindowManagerService mService;
+ private final RemoteAnimationAdapter mRemoteAnimationAdapter;
+ private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
+ private final Rect mTmpRect = new Rect();
+ private final Handler mHandler;
+
+ private final IRemoteAnimationFinishedCallback mFinishedCallback = new Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ RemoteAnimationController.this.onAnimationFinished();
+ }
+ };
+
+ private final Runnable mTimeoutRunnable = () -> {
+ onAnimationFinished();
+ invokeAnimationCancelled();
+ };
+
+ RemoteAnimationController(WindowManagerService service,
+ RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
+ mService = service;
+ mRemoteAnimationAdapter = remoteAnimationAdapter;
+ mHandler = handler;
+ }
+
+ /**
+ * Creates an animation for each individual {@link AppWindowToken}.
+ *
+ * @param appWindowToken The app to animate.
+ * @param position The position app bounds, in screen coordinates.
+ * @param stackBounds The stack bounds of the app.
+ * @return The adapter to be run on the app.
+ */
+ AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
+ Rect stackBounds) {
+ final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
+ appWindowToken, position, stackBounds);
+ mPendingAnimations.add(adapter);
+ return adapter;
+ }
+
+ /**
+ * Called when the transition is ready to be started, and all leashes have been set up.
+ */
+ void goodToGo() {
+ mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
+ mFinishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to start remote animation", e);
+ onAnimationFinished();
+ }
+ }
+
+ private RemoteAnimationTarget[] createAnimations() {
+ final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()];
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ result[i] = mPendingAnimations.get(i).createRemoteAppAnimation();
+ }
+ return result;
+ }
+
+ private void onAnimationFinished() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ synchronized (mService.mWindowMap) {
+ mService.openSurfaceTransaction();
+ try {
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
+ adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+ }
+ } finally {
+ mService.closeSurfaceTransaction("RemoteAnimationController#finished");
+ }
+ }
+ }
+
+ private void invokeAnimationCancelled() {
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify cancel", e);
+ }
+ }
+
+ private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
+
+ private final AppWindowToken mAppWindowToken;
+ private SurfaceControl mCapturedLeash;
+ private OnAnimationFinishedCallback mCapturedFinishCallback;
+ private final Point mPosition = new Point();
+ private final Rect mStackBounds = new Rect();
+
+ RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
+ Rect stackBounds) {
+ mAppWindowToken = appWindowToken;
+ mPosition.set(position.x, position.y);
+ mStackBounds.set(stackBounds);
+ }
+
+ RemoteAnimationTarget createRemoteAppAnimation() {
+ return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(),
+ mCapturedLeash, !mAppWindowToken.fillsParent(),
+ mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect,
+ mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
+ }
+
+ private int getMode() {
+ if (mService.mOpeningApps.contains(mAppWindowToken)) {
+ return RemoteAnimationTarget.MODE_OPENING;
+ } else {
+ return RemoteAnimationTarget.MODE_CLOSING;
+ }
+ }
+
+ @Override
+ public boolean getDetachWallpaper() {
+ return false;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback) {
+
+ // Restore z-layering, position and stack crop until client has a chance to modify it.
+ t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
+ t.setPosition(animationLeash, mPosition.x, mPosition.y);
+ mTmpRect.set(mStackBounds);
+ mTmpRect.offsetTo(0, 0);
+ t.setWindowCrop(animationLeash, mTmpRect);
+ mCapturedLeash = animationLeash;
+ mCapturedFinishCallback = finishCallback;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ mPendingAnimations.remove(this);
+ if (mPendingAnimations.isEmpty()) {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ invokeAnimationCancelled();
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mRemoteAnimationAdapter.getDuration();
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis()
+ + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index ac0919d93166..1218d3bc1b9b 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -25,7 +25,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7b3353375914..0fedc795c16c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
@@ -87,7 +88,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -210,17 +210,18 @@ import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.RemoteAnimationAdapter;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowContentFrameStats;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionFlags;
+import android.view.WindowManager.TransitionType;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.view.animation.Animation;
import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
@@ -1540,7 +1541,7 @@ public class WindowManagerService extends IWindowManager.Stub
// We treat this as if this activity was opening, so we can trigger the app transition
// animation and piggy-back on existing transition animation infrastructure.
mOpeningApps.add(atoken);
- prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT);
+ prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT);
mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
frame.width(), frame.height());
executeAppTransition();
@@ -1554,7 +1555,7 @@ public class WindowManagerService extends IWindowManager.Stub
// we don't set up the transition anymore and just let it go.
if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
mOpeningApps.add(atoken);
- prepareAppTransition(AppTransition.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT);
+ prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT);
executeAppTransition();
}
}
@@ -2509,7 +2510,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
+ public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) {
prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
}
@@ -2522,8 +2523,8 @@ public class WindowManagerService extends IWindowManager.Stub
* AppTransition.TRANSIT_FLAG_*.
* @param forceOverride Always override the transit, not matter what was set previously.
*/
- public void prepareAppTransition(int transit, boolean alwaysKeepCurrent, int flags,
- boolean forceOverride) {
+ public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent,
+ @TransitionFlags int flags, boolean forceOverride) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
@@ -2539,7 +2540,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public int getPendingAppTransition() {
+ public @TransitionType int getPendingAppTransition() {
return mAppTransition.getAppTransition();
}
@@ -2624,6 +2625,18 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+ if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "overridePendingAppTransitionRemote()")) {
+ throw new SecurityException(
+ "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
+ }
+ synchronized (mWindowMap) {
+ mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+ }
+ }
+
+ @Override
public void endProlongedAnimations() {
synchronized (mWindowMap) {
for (final WindowState win : mWindowMap.values()) {
@@ -5894,6 +5907,7 @@ public class WindowManagerService extends IWindowManager.Stub
* the screen is.
* @see WindowManagerPolicy#getNavBarPosition()
*/
+ @Override
@WindowManagerPolicy.NavigationBarPosition
public int getNavBarPosition() {
synchronized (mWindowMap) {
@@ -6599,6 +6613,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void onOverlayChanged() {
synchronized (mWindowMap) {
mPolicy.onOverlayChangedLw();
+ mDisplayManagerInternal.onOverlayChanged();
requestTraversal();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5d7423814ca9..db30db074838 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -21,14 +21,12 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.SurfaceControl.Transaction;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
@@ -43,6 +41,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -193,6 +193,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
@@ -2994,16 +2995,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// No cutout, no letterbox.
return false;
}
- if ((mAttrs.flags2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0) {
+ if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
// Layout in cutout, no letterbox.
return false;
}
// TODO: handle dialogs and other non-filling windows
+ if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
+ // Never layout in cutout, always letterbox.
+ return true;
+ }
// Letterbox if any fullscreen mode is set.
final int fl = mAttrs.flags;
final int sysui = mSystemUiVisibility;
- return (fl & FLAG_FULLSCREEN) != 0
- || (sysui & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_FULLSCREEN)) != 0;
+ return (fl & FLAG_FULLSCREEN) != 0 || (sysui & (SYSTEM_UI_FLAG_FULLSCREEN)) != 0;
}
boolean isDragResizeChanged() {
@@ -3949,6 +3953,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return null;
}
+ /**
+ * @return True if we our one of our ancestors has {@link #mAnimatingExit} set to true, false
+ * otherwise.
+ */
+ @VisibleForTesting
+ boolean isSelfOrAncestorWindowAnimatingExit() {
+ WindowState window = this;
+ do {
+ if (window.mAnimatingExit) {
+ return true;
+ }
+ window = window.getParentWindow();
+ } while (window != null);
+ return false;
+ }
+
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
@@ -3984,7 +4004,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
- if (!mAnimatingExit) {
+ if (!isSelfOrAncestorWindowAnimatingExit()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 017b3251696e..7364e87227e4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -23,23 +23,23 @@ import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_OPEN;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -57,14 +57,19 @@ import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionType;
import android.view.animation.Animation;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.Predicate;
/**
* Positions windows and their surfaces.
@@ -247,7 +252,7 @@ class WindowSurfacePlacer {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
int transit = mService.mAppTransition.getAppTransition();
if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
- transit = AppTransition.TRANSIT_UNSET;
+ transit = WindowManager.TRANSIT_UNSET;
}
mService.mSkipAppTransitionAnimation = false;
mService.mNoAnimationNotifyOnTransitionFinished.clear();
@@ -255,17 +260,9 @@ class WindowSurfacePlacer {
mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
- // TODO: Don't believe this is really needed...
- //mService.mWindowsChanged = true;
mService.mRoot.mWallpaperMayChange = false;
- // The top-most window will supply the layout params, and we will determine it below.
- LayoutParams animLp = null;
- int bestAnimLayer = -1;
- boolean fullscreenAnim = false;
- boolean voiceInteraction = false;
-
int i;
for (i = 0; i < appsCount; i++) {
final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -274,7 +271,6 @@ class WindowSurfacePlacer {
// visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
// transition selection depends on wallpaper target visibility.
wtoken.clearAnimatingFlags();
-
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
@@ -283,62 +279,30 @@ class WindowSurfacePlacer {
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent,
mService.mOpeningApps);
- final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
- boolean openingAppHasWallpaper = false;
- boolean closingAppHasWallpaper = false;
-
- // Do a first pass through the tokens for two things:
- // (1) Determine if both the closing and opening app token sets are wallpaper targets, in
- // which case special animations are needed (since the wallpaper needs to stay static behind
- // them).
- // (2) Find the layout params of the top-most application window in the tokens, which is
- // what will control the animation theme.
- final int closingAppsCount = mService.mClosingApps.size();
- appsCount = closingAppsCount + mService.mOpeningApps.size();
- for (i = 0; i < appsCount; i++) {
- final AppWindowToken wtoken;
- if (i < closingAppsCount) {
- wtoken = mService.mClosingApps.valueAt(i);
- if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
- closingAppHasWallpaper = true;
- }
- } else {
- wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount);
- if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
- openingAppHasWallpaper = true;
- }
- }
-
- voiceInteraction |= wtoken.mVoiceInteraction;
-
- if (wtoken.fillsParent()) {
- final WindowState ws = wtoken.findMainWindow();
- if (ws != null) {
- animLp = ws.mAttrs;
- bestAnimLayer = ws.mLayer;
- fullscreenAnim = true;
- }
- } else if (!fullscreenAnim) {
- final WindowState ws = wtoken.findMainWindow();
- if (ws != null) {
- if (ws.mLayer > bestAnimLayer) {
- animLp = ws.mAttrs;
- bestAnimLayer = ws.mLayer;
- }
- }
- }
- }
+ // Determine if closing and opening app token sets are wallpaper targets, in which case
+ // special animations are needed.
+ final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
+ final boolean openingAppHasWallpaper = canBeWallpaperTarget(mService.mOpeningApps)
+ && hasWallpaperTarget;
+ final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps)
+ && hasWallpaperTarget;
transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
closingAppHasWallpaper);
- // If all closing windows are obscured, then there is no need to do an animation. This is
- // the case, for example, when this transition is being done behind the lock screen.
- if (!mService.mPolicy.allowAppAnimationsLw()) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Animations disallowed by keyguard or dream.");
- animLp = null;
- }
+ // Find the layout params of the top-most application window in the tokens, which is
+ // what will control the animation theme. If all closing windows are obscured, then there is
+ // no need to do an animation. This is the case, for example, when this transition is being
+ // done behind a dream window.
+ final AppWindowToken animLpToken = mService.mPolicy.allowAppAnimationsLw()
+ ? findAnimLayoutParamsToken(transit)
+ : null;
+
+ final LayoutParams animLp = getAnimLp(animLpToken);
+ overrideWithRemoteAnimationIfSet(animLpToken, transit);
+
+ final boolean voiceInteraction = containsVoiceInteraction(mService.mOpeningApps)
+ || containsVoiceInteraction(mService.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
@@ -388,6 +352,81 @@ class WindowSurfacePlacer {
return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
+ private static LayoutParams getAnimLp(AppWindowToken wtoken) {
+ final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null;
+ return mainWindow != null ? mainWindow.mAttrs : null;
+ }
+
+ /**
+ * Overrides the pending transition with the remote animation defined for the transition in the
+ * set of defined remote animations in the app window token.
+ */
+ private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit) {
+ if (animLpToken == null) {
+ return;
+ }
+ final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
+ if (definition != null) {
+ final RemoteAnimationAdapter adapter = definition.getAdapter(transit);
+ if (adapter != null) {
+ mService.mAppTransition.overridePendingAppTransitionRemote(adapter);
+ }
+ }
+ }
+
+ /**
+ * @return The window token that determines the animation theme.
+ */
+ private AppWindowToken findAnimLayoutParamsToken(@TransitionType int transit) {
+ AppWindowToken result;
+
+ // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
+ result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
+ w -> w.getRemoteAnimationDefinition() != null
+ && w.getRemoteAnimationDefinition().hasTransition(transit));
+ if (result != null) {
+ return result;
+ }
+ result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
+ w -> w.fillsParent() && w.findMainWindow() != null);
+ if (result != null) {
+ return result;
+ }
+ return lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
+ w -> w.findMainWindow() != null);
+ }
+
+ private AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
+ ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) {
+ final int array1count = array1.size();
+ final int count = array1count + array2.size();
+ int bestPrefixOrderIndex = Integer.MIN_VALUE;
+ AppWindowToken bestToken = null;
+ for (int i = 0; i < count; i++) {
+ final AppWindowToken wtoken;
+ if (i < array1count) {
+ wtoken = array1.valueAt(i);
+ } else {
+ wtoken = array2.valueAt(i - array1count);
+ }
+ final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
+ if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
+ bestPrefixOrderIndex = prefixOrderIndex;
+ bestToken = wtoken;
+ }
+ }
+ return bestToken;
+ }
+
+ private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) {
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ if (apps.valueAt(i).mVoiceInteraction) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
boolean voiceInteraction) {
AppWindowToken topOpeningApp = null;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 36de3d12a743..384b416b0201 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -109,4 +109,21 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
public boolean startUserInBackground(ComponentName who, UserHandle userHandle) {
return false;
}
+
+ @Override
+ public void setStartUserSessionMessage(
+ ComponentName admin, CharSequence startUserSessionMessage) {}
+
+ @Override
+ public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {}
+
+ @Override
+ public String getStartUserSessionMessage(ComponentName admin) {
+ return null;
+ }
+
+ @Override
+ public String getEndUserSessionMessage(ComponentName admin) {
+ return null;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bf2b137f65b8..cb4f5c1177df 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -200,6 +200,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
import com.android.server.pm.UserRestrictionsUtils;
+
import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParser;
@@ -226,6 +227,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -790,6 +792,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
+ private static final String TAG_MANDATORY_BACKUP_TRANSPORT = "mandatory_backup_transport";
+ private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
+ private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
DeviceAdminInfo info;
@@ -906,6 +911,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// The blacklist data is stored in a file whose name is stored in the XML
String passwordBlacklistFile = null;
+ // The component name of the backup transport which has to be used if backups are mandatory
+ // or null if backups are not mandatory.
+ ComponentName mandatoryBackupTransport = null;
+
+ // Message for user switcher
+ String startUserSessionMessage = null;
+ String endUserSessionMessage = null;
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
isParent = parent;
@@ -1169,6 +1182,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled));
out.endTag(null, TAG_IS_LOGOUT_ENABLED);
}
+ if (mandatoryBackupTransport != null) {
+ out.startTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
+ out.attribute(null, ATTR_VALUE, mandatoryBackupTransport.flattenToString());
+ out.endTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
+ }
+ if (startUserSessionMessage != null) {
+ out.startTag(null, TAG_START_USER_SESSION_MESSAGE);
+ out.text(startUserSessionMessage);
+ out.endTag(null, TAG_START_USER_SESSION_MESSAGE);
+ }
+ if (endUserSessionMessage != null) {
+ out.startTag(null, TAG_END_USER_SESSION_MESSAGE);
+ out.text(endUserSessionMessage);
+ out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
+ }
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1347,6 +1375,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
isLogoutEnabled = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_MANDATORY_BACKUP_TRANSPORT.equals(tag)) {
+ mandatoryBackupTransport = ComponentName.unflattenFromString(
+ parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ startUserSessionMessage = parser.getText();
+ } else {
+ Log.w(LOG_TAG, "Missing text when loading start session message");
+ }
+ } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ endUserSessionMessage = parser.getText();
+ } else {
+ Log.w(LOG_TAG, "Missing text when loading end session message");
+ }
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -3201,10 +3246,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
synchronized (this) {
- // push the force-ephemeral-users policy to the user manager.
ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (deviceOwner != null) {
+ // Push the force-ephemeral-users policy to the user manager.
mUserManagerInternal.setForceEphemeralUsers(deviceOwner.forceEphemeralUsers);
+
+ // Update user switcher message to activity manager.
+ ActivityManagerInternal activityManagerInternal =
+ mInjector.getActivityManagerInternal();
+ activityManagerInternal.setSwitchingFromSystemUserMessage(
+ deviceOwner.startUserSessionMessage);
+ activityManagerInternal.setSwitchingToSystemUserMessage(
+ deviceOwner.endUserSessionMessage);
}
}
}
@@ -4503,6 +4556,28 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private boolean canPOorDOCallResetPassword(ActiveAdmin admin, @UserIdInt int userId) {
+ // Only if the admins targets a pre-O SDK
+ return getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.O;
+ }
+
+ /* PO or DO could do an untrusted reset in certain conditions. */
+ private boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) {
+ synchronized (this) {
+ // An active DO or PO might be able to fo an untrusted credential reset
+ for (final ActiveAdmin admin : getUserData(userId).mAdminList) {
+ if (!isActiveAdminWithPolicyForUserLocked(admin,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, userId)) {
+ continue;
+ }
+ if (canPOorDOCallResetPassword(admin, userId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
@Override
public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
final int callingUid = mInjector.binderGetCallingUid();
@@ -4521,12 +4596,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
final boolean preN;
if (admin != null) {
- final int targetSdk = getTargetSdk(admin.info.getPackageName(), userHandle);
- if (targetSdk >= Build.VERSION_CODES.O) {
+ if (!canPOorDOCallResetPassword(admin, userHandle)) {
throw new SecurityException("resetPassword() is deprecated for DPC targeting O"
+ " or later");
}
- preN = targetSdk <= android.os.Build.VERSION_CODES.M;
+ preN = getTargetSdk(admin.info.getPackageName(),
+ userHandle) <= android.os.Build.VERSION_CODES.M;
} else {
// Otherwise, make sure the caller has any active admin with the right policy.
admin = getActiveAdminForCallerLocked(null,
@@ -7319,6 +7394,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
policy.mAffiliationIds.clear();
policy.mLockTaskPackages.clear();
+ updateLockTaskPackagesLocked(policy.mLockTaskPackages, userId);
policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
saveSettingsLocked(userId);
@@ -10096,6 +10172,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updateMaximumTimeToLockLocked(userId);
}
}
+
+ @Override
+ public boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) {
+ return DevicePolicyManagerService.this.canUserHaveUntrustedCredentialReset(userId);
+ }
}
private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
@@ -11337,7 +11418,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkNotNull(admin);
synchronized (this) {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(
+ admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ if (!enabled) {
+ activeAdmin.mandatoryBackupTransport = null;
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
}
final long ident = mInjector.binderClearCallingIdentity();
@@ -11372,6 +11458,50 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public void setMandatoryBackupTransport(
+ ComponentName admin, ComponentName backupTransportComponent) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+ synchronized (this) {
+ ActiveAdmin activeAdmin =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ if (!Objects.equals(backupTransportComponent, activeAdmin.mandatoryBackupTransport)) {
+ activeAdmin.mandatoryBackupTransport = backupTransportComponent;
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
+ }
+ final long identity = mInjector.binderClearCallingIdentity();
+ try {
+ IBackupManager ibm = mInjector.getIBackupManager();
+ if (ibm != null && backupTransportComponent != null) {
+ if (!ibm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
+ ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ }
+ ibm.selectBackupTransportAsync(backupTransportComponent, null);
+ ibm.setBackupEnabled(true);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to set mandatory backup transport.", e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public ComponentName getMandatoryBackupTransport() {
+ if (!mHasFeature) {
+ return null;
+ }
+ synchronized (this) {
+ ActiveAdmin activeAdmin = getDeviceOwnerAdminLocked();
+ return activeAdmin == null ? null : activeAdmin.mandatoryBackupTransport;
+ }
+ }
+
+
+ @Override
public boolean bindDeviceAdminServiceAsUser(
@NonNull ComponentName admin, @NonNull IApplicationThread caller,
@Nullable IBinder activtiyToken, @NonNull Intent serviceIntent,
@@ -11943,15 +12073,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Preconditions.checkNotNull(admin);
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- if (enabled == isLogoutEnabledInternalLocked()) {
- // already in the requested state
- return;
+ synchronized (this) {
+ ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (deviceOwner.isLogoutEnabled == enabled) {
+ // already in the requested state
+ return;
+ }
+ deviceOwner.isLogoutEnabled = enabled;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
- ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- deviceOwner.isLogoutEnabled = enabled;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
@Override
@@ -11960,15 +12093,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
synchronized (this) {
- return isLogoutEnabledInternalLocked();
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ return (deviceOwner != null) && deviceOwner.isLogoutEnabled;
}
}
- private boolean isLogoutEnabledInternalLocked() {
- ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- return (deviceOwner != null) && deviceOwner.isLogoutEnabled;
- }
-
@Override
public List<String> getDisallowedSystemApps(ComponentName admin, int userId,
String provisioningAction) throws RemoteException {
@@ -12005,6 +12134,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
/* throwForMissingPermission= */ true);
checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
+ if (!incomingDeviceInfo.getActivityInfo().metaData
+ .getBoolean(DeviceAdminReceiver.SUPPORT_TRANSFER_OWNERSHIP_META_DATA, false)) {
+ throw new IllegalArgumentException("Provided target does not support "
+ + "ownership transfer.");
+ }
final long id = mInjector.binderClearCallingIdentity();
try {
@@ -12061,4 +12195,83 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return extras;
}
+
+ @Override
+ public void setStartUserSessionMessage(
+ ComponentName admin, CharSequence startUserSessionMessage) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+
+ final String startUserSessionMessageString =
+ startUserSessionMessage != null ? startUserSessionMessage.toString() : null;
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (TextUtils.equals(deviceOwner.startUserSessionMessage, startUserSessionMessage)) {
+ return;
+ }
+ deviceOwner.startUserSessionMessage = startUserSessionMessageString;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+
+ mInjector.getActivityManagerInternal()
+ .setSwitchingFromSystemUserMessage(startUserSessionMessageString);
+ }
+
+ @Override
+ public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+
+ final String endUserSessionMessageString =
+ endUserSessionMessage != null ? endUserSessionMessage.toString() : null;
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (TextUtils.equals(deviceOwner.endUserSessionMessage, endUserSessionMessage)) {
+ return;
+ }
+ deviceOwner.endUserSessionMessage = endUserSessionMessageString;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+
+ mInjector.getActivityManagerInternal()
+ .setSwitchingToSystemUserMessage(endUserSessionMessageString);
+ }
+
+ @Override
+ public String getStartUserSessionMessage(ComponentName admin) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(admin);
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ return deviceOwner.startUserSessionMessage;
+ }
+ }
+
+ @Override
+ public String getEndUserSessionMessage(ComponentName admin) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(admin);
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ return deviceOwner.endUserSessionMessage;
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4310a98d8000..3199bfa49455 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,6 +80,7 @@ import com.android.server.job.JobSchedulerService;
import com.android.server.lights.LightsService;
import com.android.server.media.MediaResourceMonitorService;
import com.android.server.media.MediaRouterService;
+import com.android.server.media.MediaUpdateService;
import com.android.server.media.MediaSessionService;
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
@@ -1442,6 +1443,10 @@ public final class SystemServer {
mSystemServiceManager.startService(MediaSessionService.class);
traceEnd();
+ traceBeginAndSlog("StartMediaUpdateService");
+ mSystemServiceManager.startService(MediaUpdateService.class);
+ traceEnd();
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
traceBeginAndSlog("StartHdmiControlService");
mSystemServiceManager.startService(HdmiControlService.class);
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 71ba685b99c9..cd4e8f977d60 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -57,7 +57,9 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
@@ -670,37 +672,29 @@ public final class PrintManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
if (dumpAsProto) {
- dump(new ProtoOutputStream(fd), userStatesToDump);
+ dump(new DualDumpOutputStream(new ProtoOutputStream(fd), null),
+ userStatesToDump);
} else {
- dump(fd, pw, userStatesToDump);
+ pw.println("PRINT MANAGER STATE (dumpsys print)");
+
+ dump(new DualDumpOutputStream(null, new IndentingPrintWriter(pw, " ")),
+ userStatesToDump);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- private void dump(@NonNull ProtoOutputStream proto,
+ private void dump(@NonNull DualDumpOutputStream dumpStream,
@NonNull ArrayList<UserState> userStatesToDump) {
final int userStateCount = userStatesToDump.size();
for (int i = 0; i < userStateCount; i++) {
- long token = proto.start(PrintServiceDumpProto.USER_STATES);
- userStatesToDump.get(i).dump(proto);
- proto.end(token);
+ long token = dumpStream.start("user_states", PrintServiceDumpProto.USER_STATES);
+ userStatesToDump.get(i).dump(dumpStream);
+ dumpStream.end(token);
}
- proto.flush();
- }
-
- private void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
- @NonNull ArrayList<UserState> userStatesToDump) {
- pw = Preconditions.checkNotNull(pw);
-
- pw.println("PRINT MANAGER STATE (dumpsys print)");
- final int userStateCount = userStatesToDump.size();
- for (int i = 0; i < userStateCount; i++) {
- userStatesToDump.get(i).dump(fd, pw, "");
- pw.println();
- }
+ dumpStream.flush();
}
private void registerContentObservers() {
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 13462cd3e3e4..80b97cf995ae 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -47,11 +47,10 @@ import android.printservice.IPrintService;
import android.printservice.IPrintServiceClient;
import android.service.print.ActivePrintServiceProto;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.print.DualDumpOutputStream;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -532,49 +531,30 @@ final class RemotePrintService implements DeathRecipient {
}
}
- public void dump(@NonNull ProtoOutputStream proto) {
- writeComponentName(proto, ActivePrintServiceProto.COMPONENT_NAME, mComponentName);
+ public void dump(@NonNull DualDumpOutputStream proto) {
+ writeComponentName(proto, "component_name", ActivePrintServiceProto.COMPONENT_NAME,
+ mComponentName);
- proto.write(ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
- proto.write(ActivePrintServiceProto.IS_BOUND, isBound());
- proto.write(ActivePrintServiceProto.HAS_DISCOVERY_SESSION, mHasPrinterDiscoverySession);
- proto.write(ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS, mHasActivePrintJobs);
- proto.write(ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
+ proto.write("is_destroyed", ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
+ proto.write("is_bound", ActivePrintServiceProto.IS_BOUND, isBound());
+ proto.write("has_discovery_session", ActivePrintServiceProto.HAS_DISCOVERY_SESSION,
+ mHasPrinterDiscoverySession);
+ proto.write("has_active_print_jobs", ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS,
+ mHasActivePrintJobs);
+ proto.write("is_discovering_printers", ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
mDiscoveryPriorityList != null);
synchronized (mLock) {
if (mTrackedPrinterList != null) {
int numTrackedPrinters = mTrackedPrinterList.size();
for (int i = 0; i < numTrackedPrinters; i++) {
- writePrinterId(proto, ActivePrintServiceProto.TRACKED_PRINTERS,
- mTrackedPrinterList.get(i));
+ writePrinterId(proto, "tracked_printers",
+ ActivePrintServiceProto.TRACKED_PRINTERS, mTrackedPrinterList.get(i));
}
}
}
}
- public void dump(PrintWriter pw, String prefix) {
- String tab = " ";
- pw.append(prefix).append("service:").println();
- pw.append(prefix).append(tab).append("componentName=")
- .append(mComponentName.flattenToString()).println();
- pw.append(prefix).append(tab).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
- pw.append(prefix).append(tab).append("bound=")
- .append(String.valueOf(isBound())).println();
- pw.append(prefix).append(tab).append("hasDicoverySession=")
- .append(String.valueOf(mHasPrinterDiscoverySession)).println();
- pw.append(prefix).append(tab).append("hasActivePrintJobs=")
- .append(String.valueOf(mHasActivePrintJobs)).println();
- pw.append(prefix).append(tab).append("isDiscoveringPrinters=")
- .append(String.valueOf(mDiscoveryPriorityList != null)).println();
-
- synchronized (mLock) {
- pw.append(prefix).append(tab).append("trackedPrinters=").append(
- (mTrackedPrinterList != null) ? mTrackedPrinterList.toString() : "null");
- }
- }
-
private boolean isBound() {
return mPrintService != null;
}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index f654fcb60750..a69baa110f5a 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -43,16 +43,14 @@ import android.printservice.PrintService;
import android.service.print.PrintSpoolerStateProto;
import android.util.Slog;
import android.util.TimedRemoteCaller;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.TransferPipe;
+import com.android.internal.print.DualDumpOutputStream;
import libcore.io.IoUtils;
-import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -558,37 +556,25 @@ final class RemotePrintSpooler {
}
}
- public void dump(@NonNull ProtoOutputStream proto) {
+ public void dump(@NonNull DualDumpOutputStream dumpStream) {
synchronized (mLock) {
- proto.write(PrintSpoolerStateProto.IS_DESTROYED, mDestroyed);
- proto.write(PrintSpoolerStateProto.IS_BOUND, mRemoteInstance != null);
+ dumpStream.write("is_destroyed", PrintSpoolerStateProto.IS_DESTROYED, mDestroyed);
+ dumpStream.write("is_bound", PrintSpoolerStateProto.IS_BOUND, mRemoteInstance != null);
}
try {
- proto.write(PrintSpoolerStateProto.INTERNAL_STATE,
- TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), "--proto"));
+ if (dumpStream.isProto()) {
+ dumpStream.write(null, PrintSpoolerStateProto.INTERNAL_STATE,
+ TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), "--proto"));
+ } else {
+ dumpStream.writeNested("internal_state", TransferPipe.dumpAsync(
+ getRemoteInstanceLazy().asBinder()));
+ }
} catch (IOException | TimeoutException | RemoteException | InterruptedException e) {
Slog.e(LOG_TAG, "Failed to dump remote instance", e);
}
}
- public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
- synchronized (mLock) {
- pw.append(prefix).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
- pw.append(prefix).append("bound=")
- .append((mRemoteInstance != null) ? "true" : "false").println();
-
- pw.flush();
- try {
- TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), fd,
- new String[] { prefix });
- } catch (IOException | TimeoutException | RemoteException | InterruptedException e) {
- pw.println("Failed to dump remote instance: " + e);
- }
- }
- }
-
private void onAllPrintJobsHandled() {
synchronized (mLock) {
throwIfDestroyedLocked();
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 364bbc035a29..e2808e8245e5 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -76,19 +76,17 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
import com.android.server.print.RemotePrintServiceRecommendationService
.RemotePrintServiceRecommendationServiceCallbacks;
import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -817,112 +815,63 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
mDestroyed = true;
}
- public void dump(@NonNull ProtoOutputStream proto) {
+ public void dump(@NonNull DualDumpOutputStream dumpStream) {
synchronized (mLock) {
- proto.write(PrintUserStateProto.USER_ID, mUserId);
+ dumpStream.write("user_id", PrintUserStateProto.USER_ID, mUserId);
final int installedServiceCount = mInstalledServices.size();
for (int i = 0; i < installedServiceCount; i++) {
- long token = proto.start(PrintUserStateProto.INSTALLED_SERVICES);
+ long token = dumpStream.start("installed_services",
+ PrintUserStateProto.INSTALLED_SERVICES);
PrintServiceInfo installedService = mInstalledServices.get(i);
ResolveInfo resolveInfo = installedService.getResolveInfo();
- writeComponentName(proto, InstalledPrintServiceProto.COMPONENT_NAME,
+ writeComponentName(dumpStream, "component_name",
+ InstalledPrintServiceProto.COMPONENT_NAME,
new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name));
- writeStringIfNotNull(proto, InstalledPrintServiceProto.SETTINGS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "settings_activity",
+ InstalledPrintServiceProto.SETTINGS_ACTIVITY,
installedService.getSettingsActivityName());
- writeStringIfNotNull(proto, InstalledPrintServiceProto.ADD_PRINTERS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "add_printers_activity",
+ InstalledPrintServiceProto.ADD_PRINTERS_ACTIVITY,
installedService.getAddPrintersActivityName());
- writeStringIfNotNull(proto, InstalledPrintServiceProto.ADVANCED_OPTIONS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "advanced_options_activity",
+ InstalledPrintServiceProto.ADVANCED_OPTIONS_ACTIVITY,
installedService.getAdvancedOptionsActivityName());
- proto.end(token);
+ dumpStream.end(token);
}
for (ComponentName disabledService : mDisabledServices) {
- writeComponentName(proto, PrintUserStateProto.DISABLED_SERVICES, disabledService);
+ writeComponentName(dumpStream, "disabled_services",
+ PrintUserStateProto.DISABLED_SERVICES, disabledService);
}
final int activeServiceCount = mActiveServices.size();
for (int i = 0; i < activeServiceCount; i++) {
- long token = proto.start(PrintUserStateProto.ACTIVE_SERVICES);
- mActiveServices.valueAt(i).dump(proto);
- proto.end(token);
+ long token = dumpStream.start("actives_services",
+ PrintUserStateProto.ACTIVE_SERVICES);
+ mActiveServices.valueAt(i).dump(dumpStream);
+ dumpStream.end(token);
}
- mPrintJobForAppCache.dumpLocked(proto);
+ mPrintJobForAppCache.dumpLocked(dumpStream);
if (mPrinterDiscoverySession != null) {
- long token = proto.start(PrintUserStateProto.DISCOVERY_SESSIONS);
- mPrinterDiscoverySession.dumpLocked(proto);
- proto.end(token);
+ long token = dumpStream.start("discovery_service",
+ PrintUserStateProto.DISCOVERY_SESSIONS);
+ mPrinterDiscoverySession.dumpLocked(dumpStream);
+ dumpStream.end(token);
}
}
- long token = proto.start(PrintUserStateProto.PRINT_SPOOLER_STATE);
- mSpooler.dump(proto);
- proto.end(token);
- }
-
- public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) {
- pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":");
- pw.println();
-
- String tab = " ";
-
- synchronized (mLock) {
- pw.append(prefix).append(tab).append("installed services:").println();
- final int installedServiceCount = mInstalledServices.size();
- for (int i = 0; i < installedServiceCount; i++) {
- PrintServiceInfo installedService = mInstalledServices.get(i);
- String installedServicePrefix = prefix + tab + tab;
- pw.append(installedServicePrefix).append("service:").println();
- ResolveInfo resolveInfo = installedService.getResolveInfo();
- ComponentName componentName = new ComponentName(
- resolveInfo.serviceInfo.packageName,
- resolveInfo.serviceInfo.name);
- pw.append(installedServicePrefix).append(tab).append("componentName=")
- .append(componentName.flattenToString()).println();
- pw.append(installedServicePrefix).append(tab).append("settingsActivity=")
- .append(installedService.getSettingsActivityName()).println();
- pw.append(installedServicePrefix).append(tab).append("addPrintersActivity=")
- .append(installedService.getAddPrintersActivityName()).println();
- pw.append(installedServicePrefix).append(tab).append("avancedOptionsActivity=")
- .append(installedService.getAdvancedOptionsActivityName()).println();
- }
-
- pw.append(prefix).append(tab).append("disabled services:").println();
- for (ComponentName disabledService : mDisabledServices) {
- String disabledServicePrefix = prefix + tab + tab;
- pw.append(disabledServicePrefix).append("service:").println();
- pw.append(disabledServicePrefix).append(tab).append("componentName=")
- .append(disabledService.flattenToString());
- pw.println();
- }
-
- pw.append(prefix).append(tab).append("active services:").println();
- final int activeServiceCount = mActiveServices.size();
- for (int i = 0; i < activeServiceCount; i++) {
- RemotePrintService activeService = mActiveServices.valueAt(i);
- activeService.dump(pw, prefix + tab + tab);
- pw.println();
- }
-
- pw.append(prefix).append(tab).append("cached print jobs:").println();
- mPrintJobForAppCache.dumpLocked(pw, prefix + tab + tab);
-
- pw.append(prefix).append(tab).append("discovery mediator:").println();
- if (mPrinterDiscoverySession != null) {
- mPrinterDiscoverySession.dumpLocked(pw, prefix + tab + tab);
- }
- }
-
- pw.append(prefix).append(tab).append("print spooler:").println();
- mSpooler.dump(fd, pw, prefix + tab + tab);
- pw.println();
+ long token = dumpStream.start("print_spooler_state",
+ PrintUserStateProto.PRINT_SPOOLER_STATE);
+ mSpooler.dump(dumpStream);
+ dumpStream.end(token);
}
private void readConfigurationLocked() {
@@ -1650,15 +1599,17 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
}
}
- public void dumpLocked(@NonNull ProtoOutputStream proto) {
- proto.write(PrinterDiscoverySessionProto.IS_DESTROYED, mDestroyed);
- proto.write(PrinterDiscoverySessionProto.IS_PRINTER_DISCOVERY_IN_PROGRESS,
+ public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
+ dumpStream.write("is_destroyed", PrinterDiscoverySessionProto.IS_DESTROYED, mDestroyed);
+ dumpStream.write("is_printer_discovery_in_progress",
+ PrinterDiscoverySessionProto.IS_PRINTER_DISCOVERY_IN_PROGRESS,
!mStartedPrinterDiscoveryTokens.isEmpty());
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- proto.write(PrinterDiscoverySessionProto.PRINTER_DISCOVERY_OBSERVERS,
+ dumpStream.write("printer_discovery_observers",
+ PrinterDiscoverySessionProto.PRINTER_DISCOVERY_OBSERVERS,
observer.toString());
}
mDiscoveryObservers.finishBroadcast();
@@ -1666,61 +1617,22 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
for (int i = 0; i < tokenCount; i++) {
IBinder token = mStartedPrinterDiscoveryTokens.get(i);
- proto.write(PrinterDiscoverySessionProto.DISCOVERY_REQUESTS, token.toString());
+ dumpStream.write("discovery_requests",
+ PrinterDiscoverySessionProto.DISCOVERY_REQUESTS, token.toString());
}
final int trackedPrinters = mStateTrackedPrinters.size();
for (int i = 0; i < trackedPrinters; i++) {
PrinterId printer = mStateTrackedPrinters.get(i);
- writePrinterId(proto, PrinterDiscoverySessionProto.TRACKED_PRINTER_REQUESTS,
- printer);
+ writePrinterId(dumpStream, "tracked_printer_requests",
+ PrinterDiscoverySessionProto.TRACKED_PRINTER_REQUESTS, printer);
}
final int printerCount = mPrinters.size();
for (int i = 0; i < printerCount; i++) {
PrinterInfo printer = mPrinters.valueAt(i);
- writePrinterInfo(mContext, proto, PrinterDiscoverySessionProto.PRINTER, printer);
- }
- }
-
- public void dumpLocked(PrintWriter pw, String prefix) {
- pw.append(prefix).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
-
- pw.append(prefix).append("printDiscoveryInProgress=")
- .append(String.valueOf(!mStartedPrinterDiscoveryTokens.isEmpty())).println();
-
- String tab = " ";
-
- pw.append(prefix).append(tab).append("printer discovery observers:").println();
- final int observerCount = mDiscoveryObservers.beginBroadcast();
- for (int i = 0; i < observerCount; i++) {
- IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- pw.append(prefix).append(prefix).append(observer.toString());
- pw.println();
- }
- mDiscoveryObservers.finishBroadcast();
-
- pw.append(prefix).append(tab).append("start discovery requests:").println();
- final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
- for (int i = 0; i < tokenCount; i++) {
- IBinder token = mStartedPrinterDiscoveryTokens.get(i);
- pw.append(prefix).append(tab).append(tab).append(token.toString()).println();
- }
-
- pw.append(prefix).append(tab).append("tracked printer requests:").println();
- final int trackedPrinters = mStateTrackedPrinters.size();
- for (int i = 0; i < trackedPrinters; i++) {
- PrinterId printer = mStateTrackedPrinters.get(i);
- pw.append(prefix).append(tab).append(tab).append(printer.toString()).println();
- }
-
- pw.append(prefix).append(tab).append("printers:").println();
- final int pritnerCount = mPrinters.size();
- for (int i = 0; i < pritnerCount; i++) {
- PrinterInfo printer = mPrinters.valueAt(i);
- pw.append(prefix).append(tab).append(tab).append(
- printer.toString()).println();
+ writePrinterInfo(mContext, dumpStream, "printer",
+ PrinterDiscoverySessionProto.PRINTER, printer);
}
}
@@ -1933,36 +1845,22 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
}
}
- public void dumpLocked(PrintWriter pw, String prefix) {
- String tab = " ";
- final int bucketCount = mPrintJobsForRunningApp.size();
- for (int i = 0; i < bucketCount; i++) {
- final int appId = mPrintJobsForRunningApp.keyAt(i);
- pw.append(prefix).append("appId=" + appId).append(':').println();
- List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
- final int printJobCount = bucket.size();
- for (int j = 0; j < printJobCount; j++) {
- PrintJobInfo printJob = bucket.get(j);
- pw.append(prefix).append(tab).append(printJob.toString()).println();
- }
- }
- }
-
- public void dumpLocked(@NonNull ProtoOutputStream proto) {
+ public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
final int bucketCount = mPrintJobsForRunningApp.size();
for (int i = 0; i < bucketCount; i++) {
final int appId = mPrintJobsForRunningApp.keyAt(i);
List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
final int printJobCount = bucket.size();
for (int j = 0; j < printJobCount; j++) {
- long token = proto.start(PrintUserStateProto.CACHED_PRINT_JOBS);
+ long token = dumpStream.start("cached_print_jobs",
+ PrintUserStateProto.CACHED_PRINT_JOBS);
- proto.write(CachedPrintJobProto.APP_ID, appId);
+ dumpStream.write("app_id", CachedPrintJobProto.APP_ID, appId);
- writePrintJobInfo(mContext, proto, CachedPrintJobProto.PRINT_JOB,
- bucket.get(j));
+ writePrintJobInfo(mContext, dumpStream, "print_job",
+ CachedPrintJobProto.PRINT_JOB, bucket.get(j));
- proto.end(token);
+ dumpStream.end(token);
}
}
}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index f9ebd28418cd..03d28b79b5dd 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -16,14 +16,17 @@
package com.android.server.backup;
-import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAMES;
-
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
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 static org.robolectric.Shadows.shadowOf;
@@ -34,17 +37,21 @@ import android.app.backup.ISelectBackupTransportCallback;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.os.HandlerThread;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
-
import com.android.server.backup.testing.ShadowAppBackupUtils;
-import com.android.server.backup.testing.TransportTestUtils;
-import com.android.server.backup.testing.TransportTestUtils.TransportData;
+import com.android.server.backup.testing.ShadowBackupPolicyEnforcer;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderClasses;
-
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -56,39 +63,38 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContextWrapper;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowSettings;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import org.robolectric.shadows.ShadowSystemClock;
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
sdk = 26,
- shadows = {ShadowAppBackupUtils.class}
+ shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class}
)
@SystemLoaderClasses({RefactoredBackupManagerService.class, TransportManager.class})
@Presubmit
public class BackupManagerServiceRoboTest {
private static final String TAG = "BMSTest";
- private static final String TRANSPORT_NAME =
- "com.google.android.gms/.backup.BackupTransportService";
@Mock private TransportManager mTransportManager;
private HandlerThread mBackupThread;
private ShadowLooper mShadowBackupLooper;
private File mBaseStateDir;
private File mDataDir;
- private RefactoredBackupManagerService mBackupManagerService;
private ShadowContextWrapper mShadowContext;
private Context mContext;
+ private TransportData mTransport;
+ private String mTransportName;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mTransport = backupTransport();
+ mTransportName = mTransport.transportName;
+
mBackupThread = new HandlerThread("backup-test");
mBackupThread.setUncaughtExceptionHandler(
(t, e) -> ShadowLog.e(TAG, "Uncaught exception in test thread " + t.getName(), e));
@@ -103,20 +109,14 @@ public class BackupManagerServiceRoboTest {
mBaseStateDir = new File(cacheDir, "base_state_dir");
mDataDir = new File(cacheDir, "data_dir");
- mBackupManagerService =
- new RefactoredBackupManagerService(
- mContext,
- new Trampoline(mContext),
- mBackupThread,
- mBaseStateDir,
- mDataDir,
- mTransportManager);
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
@After
public void tearDown() throws Exception {
mBackupThread.quit();
ShadowAppBackupUtils.reset();
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
/* Tests for destination string */
@@ -124,10 +124,12 @@ public class BackupManagerServiceRoboTest {
@Test
public void testDestinationString() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME)))
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
.thenReturn("destinationString");
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
- String destination = mBackupManagerService.getDestinationString(TRANSPORT_NAME);
+ String destination = backupManagerService.getDestinationString(mTransportName);
assertThat(destination).isEqualTo("destinationString");
}
@@ -135,10 +137,12 @@ public class BackupManagerServiceRoboTest {
@Test
public void testDestinationString_whenTransportNotRegistered() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME)))
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
.thenThrow(TransportNotRegisteredException.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
- String destination = mBackupManagerService.getDestinationString(TRANSPORT_NAME);
+ String destination = backupManagerService.getDestinationString(mTransportName);
assertThat(destination).isNull();
}
@@ -146,12 +150,14 @@ public class BackupManagerServiceRoboTest {
@Test
public void testDestinationString_withoutPermission() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME)))
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
.thenThrow(TransportNotRegisteredException.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
expectThrows(
SecurityException.class,
- () -> mBackupManagerService.getDestinationString(TRANSPORT_NAME));
+ () -> backupManagerService.getDestinationString(mTransportName));
}
/* Tests for app eligibility */
@@ -159,24 +165,28 @@ public class BackupManagerServiceRoboTest {
@Test
public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportData transport =
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> true;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
- boolean result = mBackupManagerService.isAppEligibleForBackup("app.package");
+ boolean result = backupManagerService.isAppEligibleForBackup("app.package");
assertThat(result).isTrue();
+
verify(mTransportManager)
- .disposeOfTransportClient(eq(transport.transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
}
@Test
public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
+ setUpCurrentTransport(mTransportManager, mTransport);
ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
- boolean result = mBackupManagerService.isAppEligibleForBackup("app.package");
+ boolean result = backupManagerService.isAppEligibleForBackup("app.package");
assertThat(result).isFalse();
}
@@ -184,38 +194,43 @@ public class BackupManagerServiceRoboTest {
@Test
public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
expectThrows(
SecurityException.class,
- () -> mBackupManagerService.isAppEligibleForBackup("app.package"));
+ () -> backupManagerService.isAppEligibleForBackup("app.package"));
}
@Test
public void testFilterAppsEligibleForBackup() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportData transport =
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
Map<String, Boolean> packagesMap = new HashMap<>();
packagesMap.put("package.a", true);
packagesMap.put("package.b", false);
ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = packagesMap::get;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
String[] packages = packagesMap.keySet().toArray(new String[packagesMap.size()]);
- String[] filtered = mBackupManagerService.filterAppsEligibleForBackup(packages);
+ String[] filtered = backupManagerService.filterAppsEligibleForBackup(packages);
assertThat(filtered).asList().containsExactly("package.a");
verify(mTransportManager)
- .disposeOfTransportClient(eq(transport.transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
}
@Test
public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
String[] filtered =
- mBackupManagerService.filterAppsEligibleForBackup(
+ backupManagerService.filterAppsEligibleForBackup(
new String[] {"package.a", "package.b"});
assertThat(filtered).isEmpty();
@@ -224,28 +239,35 @@ public class BackupManagerServiceRoboTest {
@Test
public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
expectThrows(
SecurityException.class,
() ->
- mBackupManagerService.filterAppsEligibleForBackup(
+ backupManagerService.filterAppsEligibleForBackup(
new String[] {"package.a", "package.b"}));
}
/* Tests for select transport */
+ private ComponentName mNewTransportComponent;
private TransportData mNewTransport;
+ private TransportMock mNewTransportMock;
+ private ComponentName mOldTransportComponent;
private TransportData mOldTransport;
- private ComponentName mNewTransportComponent;
- private ISelectBackupTransportCallback mCallback;
+ private TransportMock mOldTransportMock;
private void setUpForSelectTransport() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(mTransportManager, TRANSPORT_NAMES);
- mNewTransport = transports.get(0);
- mNewTransportComponent = mNewTransport.transportClientMock.getTransportComponent();
- mOldTransport = transports.get(1);
+ mNewTransport = backupTransport();
+ mNewTransportComponent = mNewTransport.getTransportComponent();
+ mOldTransport = d2dTransport();
+ mOldTransportComponent = mOldTransport.getTransportComponent();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+ mNewTransportMock = transportMocks.get(0);
+ mOldTransportMock = transportMocks.get(1);
when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
.thenReturn(mOldTransport.transportName);
}
@@ -254,22 +276,28 @@ public class BackupManagerServiceRoboTest {
public void testSelectBackupTransport() throws Exception {
setUpForSelectTransport();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
String oldTransport =
- mBackupManagerService.selectBackupTransport(mNewTransport.transportName);
+ backupManagerService.selectBackupTransport(mNewTransport.transportName);
assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
}
@Test
public void testSelectBackupTransport_withoutPermission() throws Exception {
setUpForSelectTransport();
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
expectThrows(
SecurityException.class,
- () -> mBackupManagerService.selectBackupTransport(mNewTransport.transportName));
+ () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
}
@Test
@@ -278,13 +306,55 @@ public class BackupManagerServiceRoboTest {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
.thenReturn(BackupManager.SUCCESS);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync_whenMandatoryTransport() throws Exception {
+ setUpForSelectTransport();
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mNewTransportComponent);
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
- mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
mShadowBackupLooper.runToEndOfTasks();
assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync_whenOtherThanMandatoryTransport() throws Exception {
+ setUpForSelectTransport();
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mOldTransportComponent);
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+ verify(callback).onFailure(eq(BackupManager.ERROR_BACKUP_NOT_ALLOWED));
}
@Test
@@ -293,9 +363,11 @@ public class BackupManagerServiceRoboTest {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
.thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
- mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
mShadowBackupLooper.runToEndOfTasks();
assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
@@ -304,19 +376,19 @@ public class BackupManagerServiceRoboTest {
@Test
public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, null, null));
- ComponentName newTransportComponent =
- TransportTestUtils.transportComponentName(TRANSPORT_NAME);
+ setUpTransports(mTransportManager, mTransport.unregistered());
+ ComponentName newTransportComponent = mTransport.getTransportComponent();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
.thenReturn(BackupManager.SUCCESS);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
- mBackupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
+ backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(TRANSPORT_NAME);
+ assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
verify(callback).onFailure(anyInt());
}
@@ -324,13 +396,14 @@ public class BackupManagerServiceRoboTest {
public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
setUpForSelectTransport();
mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- ComponentName newTransportComponent =
- mNewTransport.transportClientMock.getTransportComponent();
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+ ComponentName newTransportComponent = mNewTransport.getTransportComponent();
expectThrows(
SecurityException.class,
() ->
- mBackupManagerService.selectBackupTransportAsync(
+ backupManagerService.selectBackupTransportAsync(
newTransportComponent, mock(ISelectBackupTransportCallback.class)));
}
@@ -338,4 +411,269 @@ public class BackupManagerServiceRoboTest {
return ShadowSettings.ShadowSecure.getString(
mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
}
+
+ /* Tests for updating transport attributes */
+
+ private static final int PACKAGE_UID = 10;
+ private ComponentName mTransportComponent;
+ private int mTransportUid;
+
+ private void setUpForUpdateTransportAttributes() throws Exception {
+ mTransportComponent = mTransport.getTransportComponent();
+ String transportPackage = mTransportComponent.getPackageName();
+
+ ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
+ shadowPackageManager.addPackage(transportPackage);
+ shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+
+ mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
+ }
+
+ @Test
+ public void
+ testUpdateTransportAttributes_whenTransportUidEqualsToCallingUid_callsThroughToTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid + 1,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ null,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ null,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ null,
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void
+ testUpdateTransportAttributes_whenDataManagementArgumentsNullityDontMatch_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ null,
+ "dataManagementLabel"));
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ null));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /* Miscellaneous tests */
+
+ @Test
+ public void testConstructor_postRegisterTransports() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+ createBackupManagerService();
+
+ mShadowBackupLooper.runToEndOfTasks();
+ verify(mTransportManager).registerTransports();
+ }
+
+ @Test
+ public void testConstructor_doesNotRegisterTransportsSynchronously() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+ createBackupManagerService();
+
+ // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
+ verify(mTransportManager, never()).registerTransports();
+ }
+
+ private RefactoredBackupManagerService createBackupManagerService() {
+ return new RefactoredBackupManagerService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+ }
+
+ private RefactoredBackupManagerService createInitializedBackupManagerService() {
+ RefactoredBackupManagerService backupManagerService =
+ new RefactoredBackupManagerService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+ mShadowBackupLooper.runToEndOfTasks();
+ // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
+ // above does NOT advance the handlers' clock, hence whenever a handler post messages with
+ // specific time to the looper the time of those messages will be before the looper's time.
+ // To fix this we advance SystemClock as well since that is from where the handlers read
+ // time.
+ ShadowSystemClock.setCurrentTimeMillis(mShadowBackupLooper.getScheduler().getCurrentTime());
+ return backupManagerService;
+ }
}
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index acd670f6748c..cf0bc235dc10 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -16,108 +16,97 @@
package com.android.server.backup;
+import static com.android.server.backup.testing.TransportData.genericTransport;
+import static com.android.server.backup.testing.TransportTestUtils.mockTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager;
+
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.shadow.api.Shadow.extract;
import static org.testng.Assert.expectThrows;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.Stream.concat;
+
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.testing.ShadowBackupTransportStub;
import com.android.server.backup.testing.ShadowContextImplForBackup;
-import com.android.server.backup.testing.ShadowPackageManagerForBackup;
-import com.android.server.backup.testing.TransportBoundListenerStub;
+import com.android.server.testing.shadows.FrameworkShadowPackageManager;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportClientManager;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.shadows.FrameworkShadowContextImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowLog;
-import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
- manifest = Config.NONE,
- sdk = 26,
- shadows = {
- ShadowContextImplForBackup.class,
- ShadowBackupTransportStub.class,
- ShadowPackageManagerForBackup.class
- }
+ manifest = Config.NONE,
+ sdk = 26,
+ shadows = {FrameworkShadowPackageManager.class, FrameworkShadowContextImpl.class}
)
@SystemLoaderClasses({TransportManager.class})
@Presubmit
public class TransportManagerTest {
- private static final String PACKAGE_NAME = "some.package.name";
- private static final String ANOTHER_PACKAGE_NAME = "another.package.name";
+ private static final String PACKAGE_A = "some.package.a";
+ private static final String PACKAGE_B = "some.package.b";
- private TransportInfo mTransport1;
- private TransportInfo mTransport2;
+ @Mock private OnTransportRegisteredListener mListener;
+ @Mock private TransportClientManager mTransportClientManager;
+ private TransportData mTransportA1;
+ private TransportData mTransportA2;
+ private TransportData mTransportB1;
- private ShadowPackageManager mPackageManagerShadow;
-
- private final TransportBoundListenerStub mTransportBoundListenerStub =
- new TransportBoundListenerStub(true);
+ private ShadowPackageManager mShadowPackageManager;
+ private Context mContext;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- ShadowLog.stream = System.out;
-
- mPackageManagerShadow =
- (ShadowPackageManagerForBackup)
+ mShadowPackageManager =
+ (FrameworkShadowPackageManager)
extract(RuntimeEnvironment.application.getPackageManager());
+ mContext = RuntimeEnvironment.application.getApplicationContext();
- mTransport1 = new TransportInfo(
- PACKAGE_NAME,
- "transport1.name",
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel");
- mTransport2 = new TransportInfo(
- PACKAGE_NAME,
- "transport2.name",
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel");
-
- ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName,
- mTransport1.binder);
- ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName,
- mTransport2.binder);
- ShadowBackupTransportStub.sBinderTransportMap.put(
- mTransport1.binder, mTransport1.binderInterface);
- ShadowBackupTransportStub.sBinderTransportMap.put(
- mTransport2.binder, mTransport2.binderInterface);
+ mTransportA1 = genericTransport(PACKAGE_A, "TransportFoo");
+ mTransportA2 = genericTransport(PACKAGE_A, "TransportBar");
+ mTransportB1 = genericTransport(PACKAGE_B, "TransportBaz");
}
@After
@@ -126,560 +115,441 @@ public class TransportManagerTest {
}
@Test
- public void onPackageAdded_bindsToAllTransports() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(
- mTransport1.componentName, mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isTrue();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
+ public void testRegisterTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2, mTransportB1);
+ TransportManager transportManager =
+ createTransportManager(mTransportA1, mTransportA2, mTransportB1);
- @Test
- public void onPackageAdded_oneTransportUnavailable_bindsToOnlyOneTransport() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- ShadowContextImplForBackup.sUnbindableComponents.add(mTransport1.componentName);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(
- mTransport1.componentName, mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
+ transportManager.registerTransports();
+
+ assertRegisteredTransports(
+ transportManager, asList(mTransportA1, mTransportA2, mTransportB1));
+
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(mTransportB1.transportName, mTransportB1.transportDirName);
}
@Test
- public void onPackageAdded_whitelistIsNull_doesNotBindToTransports() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- null /* whitelist */,
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).isEmpty();
- assertThat(transportManager.getBoundTransportNames()).isEmpty();
- assertThat(mTransportBoundListenerStub.isCalled()).isFalse();
+ public void
+ testRegisterTransports_whenOneTransportUnavailable_doesNotRegisterUnavailableTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ TransportData transport1 = mTransportA1.unavailable();
+ TransportData transport2 = mTransportA2;
+ setUpTransports(transport1, transport2);
+ TransportManager transportManager = createTransportManager(transport1, transport2);
+
+ transportManager.registerTransports();
+
+ assertRegisteredTransports(transportManager, singletonList(transport2));
+ verify(mListener, never())
+ .onTransportRegistered(transport1.transportName, transport1.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(transport2.transportName, transport2.transportDirName);
}
@Test
- public void onPackageAdded_onlyOneTransportWhitelisted_onlyConnectsToWhitelistedTransport()
+ public void testRegisterTransports_whenWhitelistIsEmpty_doesNotRegisterTransports()
throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Collections.singleton(mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(null);
- @Test
- public void onPackageAdded_appIsNotPrivileged_doesNotBindToTransports() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), 0);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(
- mTransport1.componentName, mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).isEmpty();
- assertThat(transportManager.getBoundTransportNames()).isEmpty();
- assertThat(mTransportBoundListenerStub.isCalled()).isFalse();
+ transportManager.registerTransports();
+
+ assertRegisteredTransports(transportManager, emptyList());
+ verify(mListener, never()).onTransportRegistered(any(), any());
}
@Test
- public void onPackageRemoved_transportsUnbound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void
+ testRegisterTransports_whenOnlyOneTransportWhitelisted_onlyRegistersWhitelistedTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(null, mTransportA1);
- transportManager.onPackageRemoved(PACKAGE_NAME);
+ transportManager.registerTransports();
- assertThat(transportManager.getAllTransportComponents()).isEmpty();
- assertThat(transportManager.getBoundTransportNames()).isEmpty();
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener, never())
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
}
@Test
- public void onPackageRemoved_incorrectPackageName_nothingHappens() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void testRegisterTransports_whenAppIsNotPrivileged_doesNotRegisterTransports()
+ throws Exception {
+ // Note ApplicationInfo.PRIVATE_FLAG_PRIVILEGED is missing from flags
+ setUpPackage(PACKAGE_A, 0);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager =
+ createTransportManager(null, mTransportA1, mTransportA2);
- transportManager.onPackageRemoved(ANOTHER_PACKAGE_NAME);
+ transportManager.registerTransports();
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
+ assertRegisteredTransports(transportManager, emptyList());
+ verify(mListener, never()).onTransportRegistered(any(), any());
}
@Test
- public void onPackageChanged_oneComponentChanged_onlyOneTransportRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name});
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
+ public void testOnPackageAdded_registerTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
- @Test
- public void onPackageChanged_nothingChanged_noTransportsRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[0]);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isFalse();
- }
+ transportManager.onPackageAdded(PACKAGE_A);
- @Test
- public void onPackageChanged_unexpectedComponentChanged_noTransportsRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[]{"unexpected.component"});
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isFalse();
+ assertRegisteredTransports(transportManager, asList(mTransportA1));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
}
@Test
- public void onPackageChanged_transportsRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name});
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
+ public void testOnPackageRemoved_unregisterTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportB1);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportB1);
+ transportManager.registerTransports();
- @Test
- public void getTransportBinder_returnsCorrectBinder() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo(
- mTransport1.binderInterface);
- assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
- mTransport2.binderInterface);
+ transportManager.onPackageRemoved(PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
}
@Test
- public void getTransportBinder_incorrectTransportName_returnsNull() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void testOnPackageRemoved_whenUnknownPackage_nothingHappens() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
+
+ transportManager.onPackageRemoved(PACKAGE_A + "unknown");
- assertThat(transportManager.getTransportBinder("incorrect.transport")).isNull();
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
}
@Test
- public void getTransportBinder_oneTransportUnavailable_returnsCorrectBinder() throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
- Collections.singletonList(mTransport1), mTransport1.name);
+ public void testOnPackageChanged_whenOneComponentChanged_onlyOneTransportReRegistered()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+ // Reset listener to verify calls after registerTransports() above
+ reset(mListener);
+
+ transportManager.onPackageChanged(
+ PACKAGE_A, mTransportA1.getTransportComponent().getClassName());
- assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull();
- assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
- mTransport2.binderInterface);
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportA2));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener, never())
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
}
@Test
- public void getCurrentTransport_selectTransportNotCalled_returnsDefaultTransport()
+ public void testOnPackageChanged_whenNoComponentsChanged_doesNotRegisterTransports()
throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
+ reset(mListener);
- assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name);
+ transportManager.onPackageChanged(PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
}
@Test
- public void getCurrentTransport_selectTransportCalled_returnsCorrectTransport()
+ public void testOnPackageChanged_whenUnknownComponentChanged_noTransportsRegistered()
throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
+ reset(mListener);
- transportManager.selectTransport(mTransport2.name);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A + ".UnknownComponent");
- assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport2.name);
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
}
@Test
- public void getCurrentTransportBinder_returnsCorrectBinder() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void testOnPackageChanged_reRegisterTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+ reset(mListener);
- assertThat(transportManager.getCurrentTransportBinder())
- .isEqualTo(mTransport1.binderInterface);
+ transportManager.onPackageChanged(
+ PACKAGE_A,
+ mTransportA1.getTransportComponent().getClassName(),
+ mTransportA2.getTransportComponent().getClassName());
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportA2));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
}
@Test
- public void getCurrentTransportBinder_transportNotBound_returnsNull() throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
- Collections.singletonList(mTransport1), mTransport2.name);
+ public void testGetCurrentTransportName_whenSelectTransportNotCalled_returnsDefaultTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
- transportManager.selectTransport(mTransport1.name);
+ String currentTransportName = transportManager.getCurrentTransportName();
- assertThat(transportManager.getCurrentTransportBinder()).isNull();
+ assertThat(currentTransportName).isEqualTo(mTransportA1.transportName);
}
@Test
- public void getTransportName_returnsCorrectTransportName() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- assertThat(transportManager.getTransportName(mTransport1.binderInterface))
- .isEqualTo(mTransport1.name);
- assertThat(transportManager.getTransportName(mTransport2.binderInterface))
- .isEqualTo(mTransport2.name);
- }
+ public void testGetCurrentTransport_whenSelectTransportCalled_returnsSelectedTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+ transportManager.selectTransport(mTransportA2.transportName);
- @Test
- public void getTransportName_transportNotBound_returnsNull() throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
- Collections.singletonList(mTransport1), mTransport1.name);
+ String currentTransportName = transportManager.getCurrentTransportName();
- assertThat(transportManager.getTransportName(mTransport1.binderInterface)).isNull();
- assertThat(transportManager.getTransportName(mTransport2.binderInterface))
- .isEqualTo(mTransport2.name);
+ assertThat(currentTransportName).isEqualTo(mTransportA2.transportName);
}
@Test
- public void getTransportWhitelist_returnsCorrectWhiteList() throws Exception {
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(mTransport1.componentName, mTransport2.componentName)),
- mTransport1.name,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
-
- assertThat(transportManager.getTransportWhitelist()).containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- }
+ public void testGetTransportWhitelist() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
- @Test
- public void getTransportWhitelist_whiteListIsNull_returnsEmptyArray() throws Exception {
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- null /* whitelist */,
- mTransport1.name,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
-
- assertThat(transportManager.getTransportWhitelist()).isEmpty();
+ Set<ComponentName> transportWhitelist = transportManager.getTransportWhitelist();
+
+ assertThat(transportWhitelist)
+ .containsExactlyElementsIn(
+ asList(
+ mTransportA1.getTransportComponent(),
+ mTransportA2.getTransportComponent()));
}
@Test
- public void selectTransport_setsTransportCorrectlyAndReturnsPreviousTransport()
- throws Exception {
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- null /* whitelist */,
- mTransport1.name,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
-
- assertThat(transportManager.selectTransport(mTransport2.name)).isEqualTo(mTransport1.name);
- assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name);
+ public void testSelectTransport() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager =
+ createTransportManager(null, mTransportA1, mTransportA2);
+
+ String transport1 = transportManager.selectTransport(mTransportA1.transportName);
+ String transport2 = transportManager.selectTransport(mTransportA2.transportName);
+
+ assertThat(transport1).isNull();
+ assertThat(transport2).isEqualTo(mTransportA1.transportName);
}
@Test
- public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void testGetTransportClient_forRegisteredTransport() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
TransportClient transportClient =
- transportManager.getTransportClient(mTransport1.name, "caller");
+ transportManager.getTransportClient(mTransportA1.transportName, "caller");
- assertThat(transportClient.getTransportComponent()).isEqualTo(mTransport1.componentName);
+ assertThat(transportClient.getTransportComponent())
+ .isEqualTo(mTransportA1.getTransportComponent());
}
@Test
- public void getTransportClient_forOldNameOfTransportThatChangedName_returnsNull()
+ public void testGetTransportClient_forOldNameOfTransportThatChangedName_returnsNull()
throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
transportManager.updateTransportAttributes(
- mTransport1.componentName, "newName", null, "destinationString", null, null);
+ mTransportA1.getTransportComponent(),
+ "newName",
+ null,
+ "destinationString",
+ null,
+ null);
TransportClient transportClient =
- transportManager.getTransportClient(mTransport1.name, "caller");
+ transportManager.getTransportClient(mTransportA1.transportName, "caller");
assertThat(transportClient).isNull();
}
@Test
- public void getTransportClient_forNewNameOfTransportThatChangedName_returnsCorrectly()
+ public void testGetTransportClient_forNewNameOfTransportThatChangedName_returnsCorrectly()
throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
transportManager.updateTransportAttributes(
- mTransport1.componentName, "newName", null, "destinationString", null, null);
+ mTransportA1.getTransportComponent(),
+ "newName",
+ null,
+ "destinationString",
+ null,
+ null);
- TransportClient transportClient =
- transportManager.getTransportClient("newName", "caller");
+ TransportClient transportClient = transportManager.getTransportClient("newName", "caller");
- assertThat(transportClient.getTransportComponent()).isEqualTo(mTransport1.componentName);
+ assertThat(transportClient.getTransportComponent())
+ .isEqualTo(mTransportA1.getTransportComponent());
}
@Test
- public void getTransportName_forTransportThatChangedName_returnsNewName()
- throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void testGetTransportName_forTransportThatChangedName_returnsNewName() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
transportManager.updateTransportAttributes(
- mTransport1.componentName, "newName", null, "destinationString", null, null);
+ mTransportA1.getTransportComponent(),
+ "newName",
+ null,
+ "destinationString",
+ null,
+ null);
- String transportName = transportManager.getTransportName(mTransport1.componentName);
+ String transportName =
+ transportManager.getTransportName(mTransportA1.getTransportComponent());
assertThat(transportName).isEqualTo("newName");
}
@Test
- public void isTransportRegistered_returnsCorrectly() throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Collections.singletonList(mTransport1),
- Collections.singletonList(mTransport2),
- mTransport1.name);
+ public void testIsTransportRegistered() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+
+ boolean isTransportA1Registered =
+ transportManager.isTransportRegistered(mTransportA1.transportName);
+ boolean isTransportA2Registered =
+ transportManager.isTransportRegistered(mTransportA2.transportName);
- assertThat(transportManager.isTransportRegistered(mTransport1.name)).isTrue();
- assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse();
+ assertThat(isTransportA1Registered).isTrue();
+ assertThat(isTransportA2Registered).isFalse();
}
@Test
- public void getTransportAttributes_forRegisteredTransport_returnsCorrectValues()
+ public void testGetTransportAttributes_forRegisteredTransport_returnsCorrectValues()
throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Collections.singletonList(mTransport1),
- mTransport1.name);
-
- assertThat(transportManager.getTransportConfigurationIntent(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.configurationIntent());
- assertThat(transportManager.getTransportDataManagementIntent(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.dataManagementIntent());
- assertThat(transportManager.getTransportDataManagementLabel(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.dataManagementLabel());
- assertThat(transportManager.getTransportDirName(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.transportDirName());
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
+
+ Intent configurationIntent =
+ transportManager.getTransportConfigurationIntent(mTransportA1.transportName);
+ Intent dataManagementIntent =
+ transportManager.getTransportDataManagementIntent(mTransportA1.transportName);
+ String dataManagementLabel =
+ transportManager.getTransportDataManagementLabel(mTransportA1.transportName);
+ String transportDirName = transportManager.getTransportDirName(mTransportA1.transportName);
+
+ assertThat(configurationIntent).isEqualTo(mTransportA1.configurationIntent);
+ assertThat(dataManagementIntent).isEqualTo(mTransportA1.dataManagementIntent);
+ assertThat(dataManagementLabel).isEqualTo(mTransportA1.dataManagementLabel);
+ assertThat(transportDirName).isEqualTo(mTransportA1.transportDirName);
}
@Test
- public void getTransportAttributes_forUnregisteredTransport_throws()
- throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Collections.singletonList(mTransport1),
- Collections.singletonList(mTransport2),
- mTransport1.name);
+ public void testGetTransportAttributes_forUnregisteredTransport_throws() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportConfigurationIntent(mTransport2.name));
+ () -> transportManager.getTransportConfigurationIntent(mTransportA2.transportName));
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportDataManagementIntent(
- mTransport2.name));
+ () ->
+ transportManager.getTransportDataManagementIntent(
+ mTransportA2.transportName));
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportDataManagementLabel(mTransport2.name));
+ () -> transportManager.getTransportDataManagementLabel(mTransportA2.transportName));
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportDirName(mTransport2.name));
+ () -> transportManager.getTransportDirName(mTransportA2.transportName));
+ }
+
+ private List<TransportMock> setUpTransports(TransportData... transports) throws Exception {
+ setUpTransportsForTransportManager(mShadowPackageManager, transports);
+ List<TransportMock> transportMocks = new ArrayList<>(transports.length);
+ for (TransportData transport : transports) {
+ TransportMock transportMock = mockTransport(transport);
+ when(mTransportClientManager.getTransportClient(
+ eq(transport.getTransportComponent()), any()))
+ .thenReturn(transportMock.transportClient);
+ transportMocks.add(transportMock);
+ }
+ return transportMocks;
}
- private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports,
- int flags) throws Exception {
+ private void setUpPackage(String packageName, int flags) {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = packageName;
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.privateFlags = flags;
-
- mPackageManagerShadow.addPackage(packageInfo);
-
- List<ResolveInfo> transportsInfo = new ArrayList<>();
- for (TransportInfo transport : transports) {
- ResolveInfo info = new ResolveInfo();
- info.serviceInfo = new ServiceInfo();
- info.serviceInfo.packageName = packageName;
- info.serviceInfo.name = transport.name;
- transportsInfo.add(info);
- }
-
- Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
- intent.setPackage(packageName);
-
- mPackageManagerShadow.addResolveInfoForIntent(intent, transportsInfo);
- }
-
- private TransportManager createTransportManagerAndSetUpTransports(
- List<TransportInfo> availableTransports, String defaultTransportName) throws Exception {
- return createTransportManagerAndSetUpTransports(availableTransports,
- Collections.<TransportInfo>emptyList(), defaultTransportName);
+ mShadowPackageManager.addPackage(packageInfo);
}
- private TransportManager createTransportManagerAndSetUpTransports(
- List<TransportInfo> availableTransports, List<TransportInfo> unavailableTransports,
- String defaultTransportName)
- throws Exception {
- List<String> availableTransportsNames = new ArrayList<>();
- List<ComponentName> availableTransportsComponentNames = new ArrayList<>();
- for (TransportInfo transport : availableTransports) {
- availableTransportsNames.add(transport.name);
- availableTransportsComponentNames.add(transport.componentName);
- }
-
- List<ComponentName> allTransportsComponentNames = new ArrayList<>();
- allTransportsComponentNames.addAll(availableTransportsComponentNames);
- for (TransportInfo transport : unavailableTransports) {
- allTransportsComponentNames.add(transport.componentName);
- }
-
- for (TransportInfo transport : unavailableTransports) {
- ShadowContextImplForBackup.sUnbindableComponents.add(transport.componentName);
- }
-
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(allTransportsComponentNames),
- defaultTransportName,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- availableTransportsComponentNames);
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- availableTransportsNames);
- for (TransportInfo transport : availableTransports) {
- assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
- .isTrue();
- }
- for (TransportInfo transport : unavailableTransports) {
- assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
- .isFalse();
- }
-
- mTransportBoundListenerStub.resetState();
-
+ private TransportManager createTransportManager(
+ @Nullable TransportData selectedTransport, TransportData... transports) {
+ Set<ComponentName> whitelist =
+ concat(Stream.of(selectedTransport), Stream.of(transports))
+ .filter(Objects::nonNull)
+ .map(TransportData::getTransportComponent)
+ .collect(toSet());
+ TransportManager transportManager =
+ new TransportManager(
+ mContext,
+ whitelist,
+ selectedTransport != null ? selectedTransport.transportName : null,
+ mTransportClientManager);
+ transportManager.setOnTransportRegisteredListener(mListener);
return transportManager;
}
- private static class TransportInfo {
- public final String packageName;
- public final String name;
- public final ComponentName componentName;
- public final IBackupTransport binderInterface;
- public final IBinder binder;
-
- TransportInfo(
- String packageName,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- String dataManagementLabel) {
- this.packageName = packageName;
- this.name = name;
- this.componentName = new ComponentName(packageName, name);
- this.binder = mock(IBinder.class);
- IBackupTransport transport = mock(IBackupTransport.class);
- try {
- when(transport.name()).thenReturn(name);
- when(transport.configurationIntent()).thenReturn(configurationIntent);
- when(transport.currentDestinationString()).thenReturn(currentDestinationString);
- when(transport.dataManagementIntent()).thenReturn(dataManagementIntent);
- when(transport.dataManagementLabel()).thenReturn(dataManagementLabel);
- } catch (RemoteException e) {
- // Only here to mock methods that throw RemoteException
- }
- this.binderInterface = transport;
- }
+ private void assertRegisteredTransports(
+ TransportManager transportManager, List<TransportData> transports) {
+ assertThat(transportManager.getRegisteredTransportComponents())
+ .asList()
+ .containsExactlyElementsIn(
+ transports
+ .stream()
+ .map(TransportData::getTransportComponent)
+ .collect(toList()));
+ assertThat(transportManager.getRegisteredTransportNames())
+ .asList()
+ .containsExactlyElementsIn(
+ transports.stream().map(t -> t.transportName).collect(toList()));
}
-
}
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index dfca9010130f..ace0441c8a4a 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -19,8 +19,10 @@ package com.android.server.backup.internal;
import static android.app.backup.BackupTransport.TRANSPORT_ERROR;
import static android.app.backup.BackupTransport.TRANSPORT_OK;
-import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAME;
-import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAMES;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
import static com.google.common.truth.Truth.assertThat;
@@ -28,7 +30,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,7 +45,8 @@ import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.RefactoredBackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.testing.TransportTestUtils;
-import com.android.server.backup.testing.TransportTestUtils.TransportData;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportClient;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderClasses;
@@ -58,7 +60,10 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.io.File;
+import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
+import java.util.stream.Stream;
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 26)
@@ -68,16 +73,21 @@ public class PerformInitializeTaskTest {
@Mock private RefactoredBackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@Mock private OnTaskFinishedListener mListener;
- @Mock private IBackupTransport mTransport;
+ @Mock private IBackupTransport mTransportBinder;
@Mock private IBackupObserver mObserver;
@Mock private AlarmManager mAlarmManager;
@Mock private PendingIntent mRunInitIntent;
private File mBaseStateDir;
+ private TransportData mTransport;
+ private String mTransportName;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mTransport = backupTransport();
+ mTransportName = mTransport.transportName;
+
Application context = RuntimeEnvironment.application;
mBaseStateDir = new File(context.getCacheDir(), "base_state_dir");
assertThat(mBaseStateDir.mkdir()).isTrue();
@@ -88,82 +98,76 @@ public class PerformInitializeTaskTest {
@Test
public void testRun_callsTransportCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransport).initializeDevice();
- verify(mTransport).finishBackup();
+ verify(mTransportBinder).initializeDevice();
+ verify(mTransportBinder).finishBackup();
}
@Test
public void testRun_callsBackupManagerCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
verify(mBackupManagerService)
- .recordInitPending(
- false, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME));
+ .recordInitPending(false, mTransportName, mTransport.transportDirName);
verify(mBackupManagerService)
- .resetBackupState(
- eq(
- new File(
- mBaseStateDir,
- TransportTestUtils.transportDirName(TRANSPORT_NAME))));
+ .resetBackupState(eq(new File(mBaseStateDir, mTransport.transportDirName)));
}
@Test
public void testRun_callsObserverAndListenerCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_OK));
+ verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_OK));
verify(mObserver).backupFinished(eq(TRANSPORT_OK));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenInitializeDeviceFails() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_ERROR, 0);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransport).initializeDevice();
- verify(mTransport, never()).finishBackup();
+ verify(mTransportBinder).initializeDevice();
+ verify(mTransportBinder, never()).finishBackup();
verify(mBackupManagerService)
- .recordInitPending(
- true, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME));
+ .recordInitPending(true, mTransportName, mTransport.transportDirName);
}
@Test
public void testRun_whenInitializeDeviceFails_callsObserverAndListenerCorrectly()
throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_ERROR, 0);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR));
+ verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR));
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenInitializeDeviceFails_schedulesAlarm() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_ERROR, 0);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -172,37 +176,36 @@ public class PerformInitializeTaskTest {
@Test
public void testRun_whenFinishBackupFails() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransport).initializeDevice();
- verify(mTransport).finishBackup();
+ verify(mTransportBinder).initializeDevice();
+ verify(mTransportBinder).finishBackup();
verify(mBackupManagerService)
- .recordInitPending(
- true, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME));
+ .recordInitPending(true, mTransportName, mTransport.transportDirName);
}
@Test
public void testRun_whenFinishBackupFails_callsObserverAndListenerCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR));
+ verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR));
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenFinishBackupFails_schedulesAlarm() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -211,64 +214,76 @@ public class PerformInitializeTaskTest {
@Test
public void testRun_whenOnlyOneTransportFails() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(
- mTransportManager, TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
- configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0);
- configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ TransportData transport1 = backupTransport();
+ TransportData transport2 = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, transport1, transport2);
+ configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0);
+ configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
PerformInitializeTask performInitializeTask =
- createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ createPerformInitializeTask(transport1.transportName, transport2.transportName);
performInitializeTask.run();
- verify(transports.get(1).transportMock).initializeDevice();
- verify(mObserver).onResult(eq(TRANSPORT_NAMES[0]), eq(TRANSPORT_ERROR));
- verify(mObserver).onResult(eq(TRANSPORT_NAMES[1]), eq(TRANSPORT_OK));
+ verify(transportMocks.get(1).transport).initializeDevice();
+ verify(mObserver).onResult(eq(transport1.transportName), eq(TRANSPORT_ERROR));
+ verify(mObserver).onResult(eq(transport2.transportName), eq(TRANSPORT_OK));
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
}
@Test
public void testRun_withMultipleTransports() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(mTransportManager, TRANSPORT_NAMES);
- configureTransport(transports.get(0).transportMock, TRANSPORT_OK, TRANSPORT_OK);
- configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
- configureTransport(transports.get(2).transportMock, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAMES);
+ List<TransportMock> transportMocks =
+ setUpTransports(
+ mTransportManager, backupTransport(), d2dTransport(), localTransport());
+ configureTransport(transportMocks.get(0).transport, TRANSPORT_OK, TRANSPORT_OK);
+ configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
+ configureTransport(transportMocks.get(2).transport, TRANSPORT_OK, TRANSPORT_OK);
+ String[] transportNames =
+ Stream.of(new TransportData[] {backupTransport(), d2dTransport(), localTransport()})
+ .map(t -> t.transportName)
+ .toArray(String[]::new);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(transportNames);
performInitializeTask.run();
- for (TransportData transport : transports) {
+ Iterator<TransportData> transportsIterator =
+ Arrays.asList(
+ new TransportData[] {
+ backupTransport(), d2dTransport(), localTransport()
+ })
+ .iterator();
+ for (TransportMock transportMock : transportMocks) {
+ TransportData transport = transportsIterator.next();
verify(mTransportManager).getTransportClient(eq(transport.transportName), any());
verify(mTransportManager)
- .disposeOfTransportClient(eq(transport.transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
}
}
@Test
public void testRun_whenOnlyOneTransportFails_disposesAllTransports() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(
- mTransportManager, TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
- configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0);
- configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ TransportData transport1 = backupTransport();
+ TransportData transport2 = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, transport1, transport2);
+ configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0);
+ configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
PerformInitializeTask performInitializeTask =
- createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ createPerformInitializeTask(transport1.transportName, transport2.transportName);
performInitializeTask.run();
verify(mTransportManager)
- .disposeOfTransportClient(eq(transports.get(0).transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMocks.get(0).transportClient), any());
verify(mTransportManager)
- .disposeOfTransportClient(eq(transports.get(1).transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMocks.get(1).transportClient), any());
}
@Test
public void testRun_whenTransportNotRegistered() throws Exception {
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, null, null));
-
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransports(mTransportManager, mTransport.unregistered());
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -279,16 +294,15 @@ public class PerformInitializeTaskTest {
@Test
public void testRun_whenOnlyOneTransportNotRegistered() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(
- mTransportManager,
- new TransportData(TRANSPORT_NAMES[0], null, null),
- new TransportData(TRANSPORT_NAMES[1]));
- String registeredTransportName = transports.get(1).transportName;
- IBackupTransport registeredTransport = transports.get(1).transportMock;
- TransportClient registeredTransportClient = transports.get(1).transportClientMock;
+ TransportData transport1 = backupTransport().unregistered();
+ TransportData transport2 = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, transport1, transport2);
+ String registeredTransportName = transport2.transportName;
+ IBackupTransport registeredTransport = transportMocks.get(1).transport;
+ TransportClient registeredTransportClient = transportMocks.get(1).transportClient;
PerformInitializeTask performInitializeTask =
- createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ createPerformInitializeTask(transport1.transportName, transport2.transportName);
performInitializeTask.run();
@@ -299,25 +313,24 @@ public class PerformInitializeTaskTest {
@Test
public void testRun_whenTransportNotAvailable() throws Exception {
- TransportClient transportClient = mock(TransportClient.class);
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, null, transportClient));
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ TransportMock transportMock = setUpTransport(mTransport.unavailable());
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransportManager).disposeOfTransportClient(eq(transportClient), any());
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenTransportThrowsDeadObjectException() throws Exception {
- TransportClient transportClient = mock(TransportClient.class);
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, mTransport, transportClient));
- when(mTransport.initializeDevice()).thenThrow(DeadObjectException.class);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ TransportMock transportMock = setUpTransport(mTransport);
+ IBackupTransport transport = transportMock.transport;
+ TransportClient transportClient = transportMock.transportClient;
+ when(transport.initializeDevice()).thenThrow(DeadObjectException.class);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -343,9 +356,10 @@ public class PerformInitializeTaskTest {
when(transportMock.finishBackup()).thenReturn(finishBackupStatus);
}
- private void setUpTransport(String transportName) throws Exception {
- TransportTestUtils.setUpTransport(
- mTransportManager,
- new TransportData(transportName, mTransport, mock(TransportClient.class)));
+ private TransportMock setUpTransport(TransportData transport) throws Exception {
+ TransportMock transportMock =
+ TransportTestUtils.setUpTransport(mTransportManager, transport);
+ mTransportBinder = transportMock.transport;
+ return transportMock;
}
}
diff --git a/services/robotests/src/com/android/server/backup/testing/ShadowBackupPolicyEnforcer.java b/services/robotests/src/com/android/server/backup/testing/ShadowBackupPolicyEnforcer.java
new file mode 100644
index 000000000000..88b30da46433
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/ShadowBackupPolicyEnforcer.java
@@ -0,0 +1,24 @@
+package com.android.server.backup.testing;
+
+import android.content.ComponentName;
+
+import com.android.server.backup.BackupPolicyEnforcer;
+import com.android.server.backup.RefactoredBackupManagerService;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(BackupPolicyEnforcer.class)
+public class ShadowBackupPolicyEnforcer {
+
+ private static ComponentName sMandatoryBackupTransport;
+
+ public static void setMandatoryBackupTransport(ComponentName backupTransportComponent) {
+ sMandatoryBackupTransport = backupTransportComponent;
+ }
+
+ @Implementation
+ public ComponentName getMandatoryBackupTransport() {
+ return sMandatoryBackupTransport;
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
new file mode 100644
index 000000000000..1be298d2c8c5
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.testing;
+
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+
+import java.util.concurrent.Callable;
+
+public class TestUtils {
+ /**
+ * Calls {@link Runnable#run()} and returns if no exception is thrown. Otherwise, if the
+ * exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException} and
+ * throw.
+ *
+ * <p><b>Warning:</b>DON'T use this outside tests. A wrapped checked exception is just a failure
+ * in a test.
+ */
+ public static void uncheck(ThrowingRunnable runnable) {
+ try {
+ runnable.runOrThrow();
+ } catch (Exception e) {
+ throw wrapIfChecked(e);
+ }
+ }
+
+ /**
+ * Calls {@link Callable#call()} and returns the value if no exception is thrown. Otherwise, if
+ * the exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException}
+ * and throw.
+ *
+ * <p><b>Warning:</b>DON'T use this outside tests. A wrapped checked exception is just a failure
+ * in a test.
+ */
+ public static <T> T uncheck(Callable<T> callable) {
+ try {
+ return callable.call();
+ } catch (Exception e) {
+ throw wrapIfChecked(e);
+ }
+ }
+
+ /**
+ * Wrap {@code e} in a {@link RuntimeException} only if it's not one already, in which case it's
+ * returned.
+ */
+ public static RuntimeException wrapIfChecked(Exception e) {
+ if (e instanceof RuntimeException) {
+ return (RuntimeException) e;
+ }
+ return new RuntimeException(e);
+ }
+
+ private TestUtils() {}
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java b/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java
deleted file mode 100644
index 84ac2c212854..000000000000
--- a/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.backup.testing;
-
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.TransportManager;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Stub implementation of TransportBoundListener, which returns given result and can tell whether
- * it was called for given transport.
- */
-public class TransportBoundListenerStub implements
- TransportManager.TransportBoundListener {
- private boolean mAlwaysReturnSuccess;
- private Set<IBackupTransport> mTransportsCalledFor = new HashSet<>();
-
- public TransportBoundListenerStub(boolean alwaysReturnSuccess) {
- this.mAlwaysReturnSuccess = alwaysReturnSuccess;
- }
-
- @Override
- public boolean onTransportBound(IBackupTransport binder) {
- mTransportsCalledFor.add(binder);
- return mAlwaysReturnSuccess;
- }
-
- /**
- * Returns whether the listener was called for the specified transport at least once.
- */
- public boolean isCalledForTransport(IBackupTransport binder) {
- return mTransportsCalledFor.contains(binder);
- }
-
- /**
- * Returns whether the listener was called at least once.
- */
- public boolean isCalled() {
- return !mTransportsCalledFor.isEmpty();
- }
-
- /**
- * Resets listener calls.
- */
- public void resetState() {
- mTransportsCalledFor.clear();
- }
-}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/src/com/android/server/backup/testing/TransportData.java
new file mode 100644
index 000000000000..9feaa8efcf3d
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/TransportData.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.testing;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+
+public class TransportData {
+ // No constants since new Intent() can't be called in static context because of Robolectric
+ public static TransportData backupTransport() {
+ return new TransportData(
+ "com.google.android.gms/.backup.BackupTransportService",
+ "com.google.android.gms/.backup.BackupTransportService",
+ "com.google.android.gms.backup.BackupTransportService",
+ new Intent(),
+ "user@gmail.com",
+ new Intent(),
+ "Google Account");
+ }
+
+ public static TransportData d2dTransport() {
+ return new TransportData(
+ "com.google.android.gms/.backup.migrate.service.D2dTransport",
+ "com.google.android.gms/.backup.component.D2dTransportService",
+ "d2dMigrateTransport",
+ null,
+ "Moving data to new device",
+ null,
+ "");
+ }
+
+ public static TransportData localTransport() {
+ return new TransportData(
+ "android/com.android.internal.backup.LocalTransport",
+ "android/com.android.internal.backup.LocalTransportService",
+ "com.android.internal.backup.LocalTransport",
+ null,
+ "Backing up to debug-only private cache",
+ null,
+ "");
+ }
+
+ public static TransportData genericTransport(String packageName, String className) {
+ return new TransportData(
+ packageName + "/." + className,
+ packageName + "/." + className + "Service",
+ packageName + "." + className,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel");
+ }
+
+ @TransportTestUtils.TransportStatus
+ public int transportStatus;
+ public final String transportName;
+ private final String transportComponentShort;
+ @Nullable
+ public String transportDirName;
+ @Nullable public Intent configurationIntent;
+ @Nullable public String currentDestinationString;
+ @Nullable public Intent dataManagementIntent;
+ @Nullable public String dataManagementLabel;
+
+ private TransportData(
+ @TransportTestUtils.TransportStatus int transportStatus,
+ String transportName,
+ String transportComponentShort,
+ String transportDirName,
+ Intent configurationIntent,
+ String currentDestinationString,
+ Intent dataManagementIntent,
+ String dataManagementLabel) {
+ this.transportStatus = transportStatus;
+ this.transportName = transportName;
+ this.transportComponentShort = transportComponentShort;
+ this.transportDirName = transportDirName;
+ this.configurationIntent = configurationIntent;
+ this.currentDestinationString = currentDestinationString;
+ this.dataManagementIntent = dataManagementIntent;
+ this.dataManagementLabel = dataManagementLabel;
+ }
+
+ public TransportData(
+ String transportName,
+ String transportComponentShort,
+ String transportDirName,
+ Intent configurationIntent,
+ String currentDestinationString,
+ Intent dataManagementIntent,
+ String dataManagementLabel) {
+ this(
+ TransportTestUtils.TransportStatus.REGISTERED_AVAILABLE,
+ transportName,
+ transportComponentShort,
+ transportDirName,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+
+ /**
+ * Not field because otherwise we'd have to call ComponentName::new in static context and
+ * Robolectric does not like this.
+ */
+ public ComponentName getTransportComponent() {
+ return ComponentName.unflattenFromString(transportComponentShort);
+ }
+
+ public TransportData unavailable() {
+ return new TransportData(
+ TransportTestUtils.TransportStatus.REGISTERED_UNAVAILABLE,
+ transportName,
+ transportComponentShort,
+ transportDirName,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+
+ public TransportData unregistered() {
+ return new TransportData(
+ TransportTestUtils.TransportStatus.UNREGISTERED,
+ transportName,
+ transportComponentShort,
+ transportDirName,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
index 9770e407ec72..e1dc7b5e151e 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
@@ -16,13 +16,23 @@
package com.android.server.backup.testing;
+import static com.android.server.backup.testing.TestUtils.uncheck;
+
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static java.util.stream.Collectors.toList;
+
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.support.annotation.IntDef;
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.TransportManager;
@@ -30,85 +40,82 @@ import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
-import java.util.Arrays;
+import org.robolectric.shadows.ShadowPackageManager;
+
import java.util.List;
+import java.util.stream.Stream;
public class TransportTestUtils {
- public static final String[] TRANSPORT_NAMES = {
- "android/com.android.internal.backup.LocalTransport",
- "com.google.android.gms/.backup.migrate.service.D2dTransport",
- "com.google.android.gms/.backup.BackupTransportService"
- };
+ /**
+ * Differently from {@link #setUpTransports(TransportManager, TransportData...)}, which
+ * configures {@link TransportManager}, this is meant to mock the environment for a real
+ * TransportManager.
+ */
+ public static void setUpTransportsForTransportManager(
+ ShadowPackageManager shadowPackageManager, TransportData... transports)
+ throws Exception {
+ for (TransportData transport : transports) {
+ ComponentName transportComponent = transport.getTransportComponent();
+ String packageName = transportComponent.getPackageName();
+ ResolveInfo resolveInfo = resolveInfo(transportComponent);
+ shadowPackageManager.addResolveInfoForIntent(transportIntent(), resolveInfo);
+ shadowPackageManager.addResolveInfoForIntent(
+ transportIntent().setPackage(packageName), resolveInfo);
+ }
+ }
- public static final String TRANSPORT_NAME = TRANSPORT_NAMES[0];
+ private static Intent transportIntent() {
+ return new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
+ }
- /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */
- public static TransportData setUpCurrentTransport(
- TransportManager transportManager, String transportName) throws Exception {
- TransportData transport = setUpTransports(transportManager, transportName).get(0);
- when(transportManager.getCurrentTransportClient(any()))
- .thenReturn(transport.transportClientMock);
- return transport;
+ private static ResolveInfo resolveInfo(ComponentName transportComponent) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = new ServiceInfo();
+ resolveInfo.serviceInfo.packageName = transportComponent.getPackageName();
+ resolveInfo.serviceInfo.name = transportComponent.getClassName();
+ return resolveInfo;
}
/** {@code transportName} has to be in the {@link ComponentName} format (with '/') */
- public static List<TransportData> setUpTransports(
- TransportManager transportManager, String... transportNames) throws Exception {
- return setUpTransports(
- transportManager,
- Arrays.stream(transportNames)
- .map(TransportData::new)
- .toArray(TransportData[]::new));
+ public static TransportMock setUpCurrentTransport(
+ TransportManager transportManager, TransportData transport) throws Exception {
+ TransportMock transportMock = setUpTransports(transportManager, transport).get(0);
+ if (transportMock.transportClient != null) {
+ when(transportManager.getCurrentTransportClient(any()))
+ .thenReturn(transportMock.transportClient);
+ }
+ return transportMock;
}
/** @see #setUpTransport(TransportManager, TransportData) */
- public static List<TransportData> setUpTransports(
+ public static List<TransportMock> setUpTransports(
TransportManager transportManager, TransportData... transports) throws Exception {
- for (TransportData transport : transports) {
- setUpTransport(transportManager, transport);
- }
- return Arrays.asList(transports);
+ return Stream.of(transports)
+ .map(transport -> uncheck(() -> setUpTransport(transportManager, transport)))
+ .collect(toList());
}
- /**
- * Configures transport according to {@link TransportData}:
- *
- * <ul>
- * <li>{@link TransportData#transportMock} {@code null} means transport not available.
- * <li>{@link TransportData#transportClientMock} {@code null} means transport not registered.
- * </ul>
- */
- public static void setUpTransport(TransportManager transportManager, TransportData transport)
- throws Exception {
+ public static TransportMock setUpTransport(
+ TransportManager transportManager, TransportData transport) throws Exception {
+ int status = transport.transportStatus;
String transportName = transport.transportName;
- String transportDirName = transportDirName(transportName);
- ComponentName transportComponent = transportComponentName(transportName);
- IBackupTransport transportMock = transport.transportMock;
- TransportClient transportClientMock = transport.transportClientMock;
+ ComponentName transportComponent = transport.getTransportComponent();
+ String transportDirName = transport.transportDirName;
- if (transportClientMock != null) {
+ TransportMock transportMock = mockTransport(transport);
+ if (status == TransportStatus.REGISTERED_AVAILABLE
+ || status == TransportStatus.REGISTERED_UNAVAILABLE) {
// Transport registered
when(transportManager.getTransportClient(eq(transportName), any()))
- .thenReturn(transportClientMock);
+ .thenReturn(transportMock.transportClient);
when(transportManager.getTransportClientOrThrow(eq(transportName), any()))
- .thenReturn(transportClientMock);
+ .thenReturn(transportMock.transportClient);
when(transportManager.getTransportName(transportComponent)).thenReturn(transportName);
when(transportManager.getTransportDirName(eq(transportName)))
.thenReturn(transportDirName);
when(transportManager.getTransportDirName(eq(transportComponent)))
.thenReturn(transportDirName);
- when(transportClientMock.getTransportComponent()).thenReturn(transportComponent);
-
- if (transportMock != null) {
- // Transport registered and available
- when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock);
- when(transportMock.name()).thenReturn(transportName);
- when(transportMock.transportDirName()).thenReturn(transportDirName);
- } else {
- // Transport registered but unavailable
- when(transportClientMock.connectOrThrow(any()))
- .thenThrow(TransportNotAvailableException.class);
- }
+ // TODO: Mock rest of description methods
} else {
// Transport not registered
when(transportManager.getTransportClient(eq(transportName), any())).thenReturn(null);
@@ -121,35 +128,74 @@ public class TransportTestUtils {
when(transportManager.getTransportDirName(eq(transportComponent)))
.thenThrow(TransportNotRegisteredException.class);
}
+ return transportMock;
}
- /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */
- public static ComponentName transportComponentName(String transportName) {
- return ComponentName.unflattenFromString(transportName);
- }
+ public static TransportMock mockTransport(TransportData transport) throws Exception {
+ final TransportClient transportClientMock;
+ int status = transport.transportStatus;
+ ComponentName transportComponent = transport.getTransportComponent();
+ if (status == TransportStatus.REGISTERED_AVAILABLE
+ || status == TransportStatus.REGISTERED_UNAVAILABLE) {
+ // Transport registered
+ transportClientMock = mock(TransportClient.class);
+ when(transportClientMock.getTransportComponent()).thenReturn(transportComponent);
+ if (status == TransportStatus.REGISTERED_AVAILABLE) {
+ // Transport registered and available
+ IBackupTransport transportMock = mockTransportBinder(transport);
+ when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock);
+
+ return new TransportMock(transportClientMock, transportMock);
+ } else {
+ // Transport registered but unavailable
+ when(transportClientMock.connectOrThrow(any()))
+ .thenThrow(TransportNotAvailableException.class);
- public static String transportDirName(String transportName) {
- return transportName + "_dir_name";
+ return new TransportMock(transportClientMock, null);
+ }
+ } else {
+ // Transport not registered
+ return new TransportMock(null, null);
+ }
}
- public static class TransportData {
- public final String transportName;
- @Nullable public final IBackupTransport transportMock;
- @Nullable public final TransportClient transportClientMock;
-
- public TransportData(
- String transportName,
- @Nullable IBackupTransport transportMock,
- @Nullable TransportClient transportClientMock) {
- this.transportName = transportName;
- this.transportMock = transportMock;
- this.transportClientMock = transportClientMock;
+ private static IBackupTransport mockTransportBinder(TransportData transport) throws Exception {
+ IBackupTransport transportBinder = mock(IBackupTransport.class);
+ try {
+ when(transportBinder.name()).thenReturn(transport.transportName);
+ when(transportBinder.transportDirName()).thenReturn(transport.transportDirName);
+ when(transportBinder.configurationIntent()).thenReturn(transport.configurationIntent);
+ when(transportBinder.currentDestinationString())
+ .thenReturn(transport.currentDestinationString);
+ when(transportBinder.dataManagementIntent()).thenReturn(transport.dataManagementIntent);
+ when(transportBinder.dataManagementLabel()).thenReturn(transport.dataManagementLabel);
+ } catch (RemoteException e) {
+ fail("RemoteException?");
}
+ return transportBinder;
+ }
+
+ public static class TransportMock {
+ @Nullable public final TransportClient transportClient;
+ @Nullable public final IBackupTransport transport;
- public TransportData(String transportName) {
- this(transportName, mock(IBackupTransport.class), mock(TransportClient.class));
+ private TransportMock(
+ @Nullable TransportClient transportClient, @Nullable IBackupTransport transport) {
+ this.transportClient = transportClient;
+ this.transport = transport;
}
}
+ @IntDef({
+ TransportStatus.REGISTERED_AVAILABLE,
+ TransportStatus.REGISTERED_UNAVAILABLE,
+ TransportStatus.UNREGISTERED
+ })
+ public @interface TransportStatus {
+ int REGISTERED_AVAILABLE = 0;
+ int REGISTERED_UNAVAILABLE = 1;
+ int UNREGISTERED = 2;
+ }
+
private TransportTestUtils() {}
}
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index 9b4dec6019f4..10442b7e467d 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -35,8 +35,10 @@ import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import com.android.internal.backup.IBackupTransport;
+import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.ShadowEventLog;
import com.android.server.testing.SystemLoaderClasses;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +50,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
@RunWith(FrameworkRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, sdk = 26)
+@Config(manifest = Config.NONE, sdk = 26, shadows = {ShadowEventLog.class})
@SystemLoaderClasses({TransportManager.class, TransportClient.class})
@Presubmit
public class TransportClientTest {
@@ -60,6 +62,7 @@ public class TransportClientTest {
@Mock private IBackupTransport.Stub mIBackupTransport;
private TransportClient mTransportClient;
private ComponentName mTransportComponent;
+ private String mTransportString;
private Intent mBindIntent;
private ShadowLooper mShadowLooper;
@@ -71,6 +74,7 @@ public class TransportClientTest {
mShadowLooper = shadowOf(mainLooper);
mTransportComponent =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
+ mTransportString = mTransportComponent.flattenToShortString();
mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
mTransportClient =
new TransportClient(
@@ -161,7 +165,7 @@ public class TransportClientTest {
}
@Test
- public void testConnectAsync_whenFrameworkDoesntBind_releasesConnection() throws Exception {
+ public void testConnectAsync_whenFrameworkDoesNotBind_releasesConnection() throws Exception {
when(mContext.bindServiceAsUser(
eq(mBindIntent),
any(ServiceConnection.class),
@@ -234,6 +238,82 @@ public class TransportClientTest {
.onTransportConnectionResult(isNull(), eq(mTransportClient));
}
+ @Test
+ public void testConnectAsync_beforeFrameworkCall_logsBoundTransition() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ }
+
+ @Test
+ public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 1);
+ }
+
+ @Test
+ public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onBindingDied(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.unbind("caller1");
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testOnServiceDisconnected_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ connection.onServiceDisconnected(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testOnBindingDied_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ connection.onBindingDied(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ private void assertEventLogged(int tag, Object... values) {
+ assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue();
+ }
+
private ServiceConnection verifyBindServiceAsUserAndCaptureServiceConnection(Context context) {
ArgumentCaptor<ServiceConnection> connectionCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
diff --git a/services/robotests/src/com/android/server/testing/ShadowEventLog.java b/services/robotests/src/com/android/server/testing/ShadowEventLog.java
new file mode 100644
index 000000000000..b8059f4fde87
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/ShadowEventLog.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing;
+
+import android.util.EventLog;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+@Implements(EventLog.class)
+public class ShadowEventLog {
+ private final static LinkedHashSet<Entry> ENTRIES = new LinkedHashSet<>();
+
+ @Implementation
+ public static int writeEvent(int tag, Object... values) {
+ ENTRIES.add(new Entry(tag, Arrays.asList(values)));
+ // Currently we don't care about the return value, if we do, estimate it correctly
+ return 0;
+ }
+
+ public static boolean hasEvent(int tag, Object... values) {
+ return ENTRIES.contains(new Entry(tag, Arrays.asList(values)));
+ }
+
+ public static void clearEvents() {
+ ENTRIES.clear();
+ }
+
+ public static class Entry {
+ public final int tag;
+ public final List<Object> values;
+
+ public Entry(int tag, List<Object> values) {
+ this.tag = tag;
+ this.values = values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Entry entry = (Entry) o;
+ return tag == entry.tag && values.equals(entry.values);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = tag;
+ result = 31 * result + values.hashCode();
+ return result;
+ }
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java
new file mode 100644
index 000000000000..6d220737f2fb
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing.shadows;
+
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.UserHandle;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowContextImpl;
+
+@Implements(className = ShadowContextImpl.CLASS_NAME, inheritImplementationMethods = true)
+public class FrameworkShadowContextImpl extends ShadowContextImpl {
+ @Implementation
+ public boolean bindServiceAsUser(
+ Intent service,
+ ServiceConnection connection,
+ int flags,
+ UserHandle user) {
+ return bindService(service, connection, flags);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java
index b64b59d238d1..5cdbe7ff09ba 100644
--- a/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java
@@ -14,22 +14,18 @@
* limitations under the License
*/
-package com.android.server.backup.testing;
+package com.android.server.testing.shadows;
import android.app.ApplicationPackageManager;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-
+import java.util.List;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowApplicationPackageManager;
-import java.util.List;
-
-/**
- * Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser().
- */
+/** Extension of ShadowApplicationPackageManager */
@Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
-public class ShadowPackageManagerForBackup extends ShadowApplicationPackageManager {
+public class FrameworkShadowPackageManager extends ShadowApplicationPackageManager {
@Override
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
return queryIntentServices(intent, flags);
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index 66d0da13fff1..6a21931e0418 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -42,6 +42,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager.ServiceType;
@@ -50,13 +51,17 @@ import android.os.PowerSaveState;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.ForceAppStandbyTracker.Listener;
import org.junit.Before;
@@ -102,6 +107,9 @@ public class ForceAppStandbyTrackerTest {
PowerManagerInternal injectPowerManagerInternal() {
return mMockPowerManagerInternal;
}
+
+ @Override
+ boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
}
private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -137,7 +145,11 @@ public class ForceAppStandbyTrackerTest {
private Consumer<PowerSaveState> mPowerSaveObserver;
private BroadcastReceiver mReceiver;
+ private MockContentResolver mMockContentResolver;
+ private FakeSettingsProvider mFakeSettingsProvider;
+
private boolean mPowerSaveMode;
+ private boolean mIsSmallBatteryDevice;
private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
@@ -174,13 +186,17 @@ public class ForceAppStandbyTrackerTest {
}
private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException {
-
// Set up functions that start() calls.
when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY)))
.thenAnswer(inv -> getPowerSaveState());
when(mMockAppOpsManager.getPackagesForOps(
any(int[].class)
- )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
+ )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
+
+ mMockContentResolver = new MockContentResolver();
+ mFakeSettingsProvider = new FakeSettingsProvider();
+ when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+ mMockContentResolver.addProvider(Settings.AUTHORITY, mFakeSettingsProvider);
// Call start.
instance.start();
@@ -208,7 +224,6 @@ public class ForceAppStandbyTrackerTest {
verify(mMockPowerManagerInternal).registerLowPowerModeObserver(
eq(ServiceType.FORCE_ALL_APPS_STANDBY),
powerSaveObserverCaptor.capture());
-
verify(mMockContext).registerReceiver(
receiverCaptor.capture(), any(IntentFilter.class));
@@ -221,6 +236,7 @@ public class ForceAppStandbyTrackerTest {
assertNotNull(mAppOpsCallback);
assertNotNull(mPowerSaveObserver);
assertNotNull(mReceiver);
+ assertNotNull(instance.mFlagsObserver);
}
private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -822,6 +838,33 @@ public class ForceAppStandbyTrackerTest {
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
}
+ @Test
+ public void testSmallBatteryAndCharging() throws Exception {
+ // This is a small battery device
+ mIsSmallBatteryDevice = true;
+
+ final ForceAppStandbyTrackerTestable instance = newInstance();
+ callStart(instance);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // Setting/experiment for all app standby for small battery is enabled
+ Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1);
+ instance.mFlagsObserver.onChange(true,
+ Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED));
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery is charging, force app standby is disabled
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ mReceiver.onReceive(mMockContext, intent);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery stops charging, force app standby is enabled
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING);
+ mReceiver.onReceive(mMockContext, intent);
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+ }
+
static int[] array(int... appIds) {
Arrays.sort(appIds);
return appIds;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index b38a4136c94a..9923fa86758b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -22,6 +22,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
@@ -122,7 +123,15 @@ public class ActivityRecordTests extends ActivityTestsBase {
mActivity.makeVisibleIfNeeded(null /* starting */);
assertEquals(mActivity.state, PAUSING);
+
assertTrue(pauseFound.value);
+
+ // Make sure that the state does not change for current non-stopping states.
+ mActivity.state = INITIALIZING;
+
+ mActivity.makeVisibleIfNeeded(null /* starting */);
+
+ assertEquals(mActivity.state, INITIALIZING);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index 766d30d9f9a5..7b4441ae30e6 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -96,7 +96,7 @@ public class BackupManagerServiceTest {
createBackupManagerService();
verify(mTransportManager)
- .setTransportBoundListener(any(TransportManager.TransportBoundListener.class));
+ .setOnTransportRegisteredListener(any());
}
@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 ccf2aaffb9b7..272b5d899d4b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.pm.UserInfo;
@@ -41,6 +42,7 @@ import android.test.AndroidTestCase;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -75,6 +77,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
FakeStorageManager mStorageManager;
IActivityManager mActivityManager;
DevicePolicyManager mDevicePolicyManager;
+ DevicePolicyManagerInternal mDevicePolicyManagerInternal;
KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
@@ -88,6 +91,10 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
mStorageManager = new FakeStorageManager();
mActivityManager = mock(IActivityManager.class);
mDevicePolicyManager = mock(DevicePolicyManager.class);
+ mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class);
+
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.addService(DevicePolicyManagerInternal.class, mDevicePolicyManagerInternal);
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
new file mode 100644
index 000000000000..4ad9f19735c4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.locksettings;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
+
+/**
+ * Run the synthetic password tests with caching enabled.
+ *
+ * By default, those tests run without caching. Untrusted credential reset depends on caching so
+ * this class included those tests.
+ */
+public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ enableSpCaching(true);
+ }
+
+ private void enableSpCaching(boolean enable) {
+ when(mDevicePolicyManagerInternal
+ .canUserHaveUntrustedCredentialReset(anyInt())).thenReturn(enable);
+ }
+
+ public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
+ final String PASSWORD = "testSyntheticPasswordClearCredential-password";
+ final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // clear password
+ mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
+ PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // set a new password
+ mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+ }
+
+ public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
+ final String PASSWORD = "testSyntheticPasswordClearCredential-password";
+ final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // Untrusted change password
+ mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+ assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // Verify the password
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ }
+
+ public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {
+ final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password";
+ final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword";
+
+ // Disable caching for this test
+ enableSpCaching(false);
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // Untrusted change password
+ assertExpectException(IllegalStateException.class, /* messageRegex= */ null,
+ () -> mService.setLockCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID));
+ assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // Verify the new password doesn't work but the old one still does
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ }
+
+}
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 2e4c74f19f9c..b07d6ac5dc04 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -112,7 +112,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
- private void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
+ protected void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
enableSyntheticPassword();
int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
: PASSWORD_QUALITY_UNSPECIFIED;
@@ -129,7 +129,6 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- mGateKeeperService.clearSecureUserId(PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
@@ -170,44 +169,6 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
- public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
-
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- // clear password
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
- assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
-
- // set a new password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- }
-
- public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
-
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- // Untrusted change password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
-
- // Verify the password
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
- }
-
public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
disableSyntheticPassword();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 9eb42e9db425..c1789ba0b15a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -16,11 +16,11 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryMetadata.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN;
-import static android.security.keystore.RecoveryMetadata.TYPE_PASSWORD;
-import static android.security.keystore.RecoveryMetadata.TYPE_PATTERN;
-import static android.security.keystore.RecoveryMetadata.TYPE_PIN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PASSWORD;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PATTERN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
@@ -41,8 +41,8 @@ import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -283,9 +283,9 @@ public class KeySyncTaskTest {
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
KeyDerivationParams KeyDerivationParams =
- recoveryData.getRecoveryMetadata().get(0).getKeyDerivationParams();
+ keychainSnapshot.getKeychainProtectionParams().get(0).getKeyDerivationParams();
assertThat(KeyDerivationParams.getAlgorithm()).isEqualTo(
KeyDerivationParams.ALGORITHM_SHA256);
verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
@@ -296,15 +296,15 @@ public class KeySyncTaskTest {
assertThat(counterId).isNotNull();
byte[] recoveryKey = decryptThmEncryptedKey(
lockScreenHash,
- recoveryData.getEncryptedRecoveryKeyBlob(),
+ keychainSnapshot.getEncryptedRecoveryKeyBlob(),
/*vaultParams=*/ KeySyncUtils.packVaultParams(
mKeyPair.getPublic(),
counterId,
TEST_DEVICE_ID,
/*maxAttempts=*/ 10));
- List<EntryRecoveryData> applicationKeys = recoveryData.getEntryRecoveryData();
+ List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys();
assertThat(applicationKeys).hasSize(1);
- EntryRecoveryData keyData = applicationKeys.get(0);
+ WrappedApplicationKey keyData = applicationKeys.get(0);
assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
byte[] appKey = KeySyncUtils.decryptApplicationKey(
@@ -322,14 +322,14 @@ public class KeySyncTaskTest {
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getSnapshotVersion()).isEqualTo(1); // default value;
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true);
mKeySyncTask.run();
- recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getSnapshotVersion()).isEqualTo(2); // Updated
+ keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
}
@Test
@@ -352,9 +352,9 @@ public class KeySyncTaskTest {
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
- assertThat(recoveryData.getRecoveryMetadata().get(0).getLockScreenUiFormat()).
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PASSWORD);
}
@@ -378,10 +378,10 @@ public class KeySyncTaskTest {
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
// Password with only digits is changed to pin.
- assertThat(recoveryData.getRecoveryMetadata().get(0).getLockScreenUiFormat()).
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PIN);
}
@@ -405,9 +405,9 @@ public class KeySyncTaskTest {
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
- assertThat(recoveryData.getRecoveryMetadata().get(0).getLockScreenUiFormat()).
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PATTERN);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 1bdcf478ce68..37157428683a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -16,8 +16,8 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryMetadata.TYPE_LOCKSCREEN;
-import static android.security.keystore.RecoveryMetadata.TYPE_PASSWORD;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PASSWORD;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
@@ -43,9 +43,8 @@ import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryMetadata;
-import android.security.keystore.RecoveryManager;
+import android.security.keystore.KeychainProtectionParameter;
+import android.security.keystore.WrappedApplicationKey;
import android.support.test.filters.SmallTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
@@ -251,7 +250,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -270,7 +269,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -295,7 +294,7 @@ public class RecoverableKeyStoreManagerTest {
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).startsWith(
- "Only a single RecoveryMetadata is supported");
+ "Only a single KeychainProtectionParameter is supported");
}
}
@@ -308,7 +307,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -330,7 +329,7 @@ public class RecoverableKeyStoreManagerTest {
vaultParams,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -348,7 +347,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_SESSION_ID,
/*recoveryKeyBlob=*/ randomBytes(32),
/*applicationKeys=*/ ImmutableList.of(
- new EntryRecoveryData("alias", randomBytes(32))
+ new WrappedApplicationKey("alias", randomBytes(32))
));
fail("should have thrown");
} catch (ServiceSpecificException e) {
@@ -363,7 +362,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new RecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -387,7 +386,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new RecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -397,7 +396,7 @@ public class RecoverableKeyStoreManagerTest {
SecretKey recoveryKey = randomRecoveryKey();
byte[] encryptedClaimResponse = encryptClaimResponse(
keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
- EntryRecoveryData badApplicationKey = new EntryRecoveryData(
+ WrappedApplicationKey badApplicationKey = new WrappedApplicationKey(
TEST_ALIAS,
randomBytes(32));
@@ -419,7 +418,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new RecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -430,7 +429,7 @@ public class RecoverableKeyStoreManagerTest {
byte[] encryptedClaimResponse = encryptClaimResponse(
keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
byte[] applicationKeyBytes = randomBytes(32);
- EntryRecoveryData applicationKey = new EntryRecoveryData(
+ WrappedApplicationKey applicationKey = new WrappedApplicationKey(
TEST_ALIAS,
encryptedApplicationKey(recoveryKey, applicationKeyBytes));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index 6308f74ff907..56b44e2fa17c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -3,7 +3,7 @@ package com.android.server.locksettings.recoverablekeystore.storage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import android.security.keystore.RecoveryData;
+import android.security.keystore.KeychainSnapshot;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -26,25 +26,25 @@ public class RecoverySnapshotStorageTest {
@Test
public void get_returnsSetSnapshot() {
int userId = 1000;
- RecoveryData recoveryData = new RecoveryData(
+ KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
/*snapshotVersion=*/ 1,
new ArrayList<>(),
new ArrayList<>(),
new byte[0]);
- mRecoverySnapshotStorage.put(userId, recoveryData);
+ mRecoverySnapshotStorage.put(userId, keychainSnapshot);
- assertEquals(recoveryData, mRecoverySnapshotStorage.get(userId));
+ assertEquals(keychainSnapshot, mRecoverySnapshotStorage.get(userId));
}
@Test
public void remove_removesSnapshots() {
int userId = 1000;
- RecoveryData recoveryData = new RecoveryData(
+ KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
/*snapshotVersion=*/ 1,
new ArrayList<>(),
new ArrayList<>(),
new byte[0]);
- mRecoverySnapshotStorage.put(userId, recoveryData);
+ mRecoverySnapshotStorage.put(userId, keychainSnapshot);
mRecoverySnapshotStorage.remove(userId);
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index 9a6da0e791b5..b6c370eb6b08 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -16,15 +16,16 @@
package com.android.server.policy;
-import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -34,7 +35,6 @@ import android.graphics.PixelFormat;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Surface;
import android.view.WindowManager;
import org.junit.Before;
@@ -128,7 +128,23 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
}
@Test
- public void layoutWindowLw_withDisplayCutout_fullscreen() {
+ public void layoutWindowLw_withhDisplayCutout_never() {
+ addDisplayCutout();
+
+ mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ mPolicy.addWindow(mAppWindow);
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+ assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
addDisplayCutout();
mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
@@ -137,6 +153,22 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+ assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
+ assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreen() {
+ addDisplayCutout();
+
+ mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mPolicy.addWindow(mAppWindow);
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
@@ -147,8 +179,8 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
addDisplayCutout();
- mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+ mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mPolicy.addWindow(mAppWindow);
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -217,7 +249,7 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
setRotation(ROTATION_90);
mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+ mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mPolicy.addWindow(mAppWindow);
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
index 77f96ca37e36..e7e55cd4404e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static org.junit.Assert.assertEquals;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
index ce76a223ef20..ac291632c877 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
@@ -16,27 +16,38 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.content.ClipData;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.InputChannel;
import android.view.Surface;
import android.view.SurfaceSession;
+import android.view.View;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+
/**
* Tests for the {@link DragDropController} class.
*
@@ -46,36 +57,92 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@Presubmit
public class DragDropControllerTests extends WindowTestsBase {
- private static final int TIMEOUT_MS = 1000;
- private DragDropController mTarget;
+ private static final int TIMEOUT_MS = 3000;
+ private TestDragDropController mTarget;
private WindowState mWindow;
private IBinder mToken;
+ static class TestDragDropController extends DragDropController {
+ @GuardedBy("sWm.mWindowMap")
+ private Runnable mCloseCallback;
+
+ TestDragDropController(WindowManagerService service, Looper looper) {
+ super(service, looper);
+ }
+
+ void setOnClosedCallbackLocked(Runnable runnable) {
+ assertTrue(dragDropActiveLocked());
+ mCloseCallback = runnable;
+ }
+
+ @Override
+ void onDragStateClosedLocked(DragState dragState) {
+ super.onDragStateClosedLocked(dragState);
+ if (mCloseCallback != null) {
+ mCloseCallback.run();
+ mCloseCallback = null;
+ }
+ }
+ }
+
+ /**
+ * Creates a window state which can be used as a drop target.
+ */
+ private WindowState createDropTargetWindow(String name, int ownerId) {
+ final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(
+ mDisplayContent);
+ final TaskStack stack = createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+ final Task task = createTaskInStack(stack, ownerId);
+ task.addChild(token, 0);
+
+ final WindowState window = createWindow(
+ null, TYPE_BASE_APPLICATION, token, name, ownerId, false);
+ window.mInputChannel = new InputChannel();
+ window.mHasSurface = true;
+ return window;
+ }
+
@Before
public void setUp() throws Exception {
+ final UserManagerInternal userManager = mock(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, userManager);
+
super.setUp();
- assertNotNull(sWm.mDragDropController);
- mTarget = sWm.mDragDropController;
- mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
+
+ mTarget = new TestDragDropController(sWm, sWm.mH.getLooper());
+ mDisplayContent = spy(mDisplayContent);
+ mWindow = createDropTargetWindow("Drag test window", 0);
+ when(mDisplayContent.getTouchableWinAtPointLocked(0, 0)).thenReturn(mWindow);
+ when(sWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
+
synchronized (sWm.mWindowMap) {
- // Because sWm is a static object, the previous operation may remain.
- assertFalse(mTarget.dragDropActiveLocked());
+ sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
}
@After
- public void tearDown() {
- if (mToken != null) {
- mTarget.cancelDragAndDrop(mToken);
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ final CountDownLatch latch;
+ synchronized (sWm.mWindowMap) {
+ if (!mTarget.dragDropActiveLocked()) {
+ return;
+ }
+ if (mToken != null) {
+ mTarget.cancelDragAndDrop(mToken);
+ }
+ latch = new CountDownLatch(1);
+ mTarget.setOnClosedCallbackLocked(() -> {
+ latch.countDown();
+ });
}
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
- public void testPrepareDrag() throws Exception {
- final Surface surface = new Surface();
- mToken = mTarget.prepareDrag(
- new SurfaceSession(), 0, 0, mWindow.mClient, 0, 100, 100, surface);
- assertNotNull(mToken);
+ public void testDragFlow() throws Exception {
+ dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0);
}
@Test
@@ -85,4 +152,33 @@ public class DragDropControllerTests extends WindowTestsBase {
new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0, surface);
assertNull(mToken);
}
+
+ @Test
+ public void testPerformDrag_NullDataWithGrantUri() throws Exception {
+ dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
+ }
+
+ @Test
+ public void testPerformDrag_NullDataToOtherUser() throws Exception {
+ final WindowState otherUsersWindow =
+ createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE);
+ when(mDisplayContent.getTouchableWinAtPointLocked(10, 10))
+ .thenReturn(otherUsersWindow);
+
+ dragFlow(0, null, 10, 10);
+ }
+
+ private void dragFlow(int flag, ClipData data, float dropX, float dropY) {
+ final Surface surface = new Surface();
+ mToken = mTarget.prepareDrag(
+ new SurfaceSession(), 0, 0, mWindow.mClient, flag, 100, 100, surface);
+ assertNotNull(mToken);
+
+ assertTrue(sWm.mInputManager.transferTouchFocus(null, null));
+ assertTrue(mTarget.performDrag(
+ mWindow.mClient, mToken, 0, 0, 0, 0, 0, data));
+
+ mTarget.handleMotionEvent(false, dropX, dropY);
+ mToken = mWindow.mClient.asBinder();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
new file mode 100644
index 000000000000..897be34e36fe
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Postsubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
+ */
+@SmallTest
+@FlakyTest(detail = "Promote to presubmit if non-flakyness is established")
+@RunWith(AndroidJUnit4.class)
+public class RemoteAnimationControllerTest extends WindowTestsBase {
+
+ @Mock SurfaceControl mMockLeash;
+ @Mock Transaction mMockTransaction;
+ @Mock OnAnimationFinishedCallback mFinishedCallback;
+ @Mock IRemoteAnimationRunner mMockRunner;
+ private RemoteAnimationAdapter mAdapter;
+ private RemoteAnimationController mController;
+ private final OffsettableClock mClock = new OffsettableClock.Stopped();
+ private TestHandler mHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+ sWm.mH.runWithScissors(() -> {
+ mHandler = new TestHandler(null, mClock);
+ }, 0);
+ mController = new RemoteAnimationController(sWm, mAdapter, mHandler);
+ }
+
+ @Test
+ public void testRun() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ sWm.mOpeningApps.add(win.mAppToken);
+ try {
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(new Point(50, 100), app.position);
+ assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
+ assertEquals(win.mAppToken.getPrefixOrderIndex(), app.prefixOrderIndex);
+ assertEquals(win.mAppToken.getTask().mTaskId, app.taskId);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setLayer(mMockLeash, app.prefixOrderIndex);
+ verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
+ verify(mMockTransaction).setWindowCrop(mMockLeash, new Rect(0, 0, 100, 50));
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ } finally {
+ sWm.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testCancel() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ adapter.onAnimationCancelled(mMockLeash);
+ verify(mMockRunner).onAnimationCancelled();
+ }
+
+ @Test
+ public void testTimeout() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ mClock.fastForward(2500);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner).onAnimationCancelled();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index f253632a1765..920796ed6a30 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -18,7 +18,7 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_UNSET;
import static com.android.server.wm.TaskSnapshotController.*;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 7be203a99391..6a4710bb06a4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -223,6 +223,19 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(app.canAffectSystemUiFlags());
}
+ @Test
+ public void testIsSelfOrAncestorWindowAnimating() throws Exception {
+ final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
+ final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
+ final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
+ assertFalse(child2.isSelfOrAncestorWindowAnimatingExit());
+ child2.mAnimatingExit = true;
+ assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
+ child2.mAnimatingExit = false;
+ root.mAnimatingExit = true;
+ assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
+ }
+
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index c699a94db279..69b13787ef93 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -230,20 +230,22 @@ class WindowTestsBase {
boolean ownerCanAddInternalSystemWindow) {
final WindowToken token = createWindowToken(
dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
- return createWindow(parent, type, token, name, ownerCanAddInternalSystemWindow);
+ return createWindow(parent, type, token, name, 0 /* ownerId */,
+ ownerCanAddInternalSystemWindow);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
- return createWindow(parent, type, token, name, false /* ownerCanAddInternalSystemWindow */);
+ return createWindow(parent, type, token, name, 0 /* ownerId */,
+ false /* ownerCanAddInternalSystemWindow */);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
- boolean ownerCanAddInternalSystemWindow) {
+ int ownerId, boolean ownerCanAddInternalSystemWindow) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
attrs.setTitle(name);
final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
- 0, attrs, VISIBLE, 0, ownerCanAddInternalSystemWindow);
+ 0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
token.addWindow(w);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
new file mode 100644
index 000000000000..689c2ce4c873
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
+import com.android.server.notification.NotificationManagerService.NotificationAssistants;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class NotificationAssistantsTest extends UiServiceTestCase {
+
+ @Mock
+ private PackageManager mPm;
+ @Mock
+ private IPackageManager miPm;
+ @Mock
+ private UserManager mUm;
+ @Mock
+ NotificationManagerService mNm;
+
+ NotificationAssistants mAssistants;
+
+ @Mock
+ private ManagedServices.UserProfiles mUserProfiles;
+
+ Object mLock = new Object();
+
+ UserInfo mZero = new UserInfo(0, "zero", 0);
+ UserInfo mTen = new UserInfo(10, "ten", 0);
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ getContext().setMockPackageManager(mPm);
+ getContext().addMockSystemService(Context.USER_SERVICE, mUm);
+ mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));
+
+ List<ResolveInfo> approved = new ArrayList<>();
+ ResolveInfo resolve = new ResolveInfo();
+ approved.add(resolve);
+ ServiceInfo info = new ServiceInfo();
+ info.packageName = "a";
+ info.name="a";
+ resolve.serviceInfo = info;
+ when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(approved);
+
+ List<UserInfo> users = new ArrayList<>();
+ users.add(mZero);
+ users.add(mTen);
+ users.add(new UserInfo(11, "11", 0));
+ users.add(new UserInfo(12, "12", 0));
+ for (UserInfo user : users) {
+ when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
+ }
+ when(mUm.getUsers()).thenReturn(users);
+ when(mUm.getUsers(anyBoolean())).thenReturn(users);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ }
+
+ @Test
+ public void testXmlUpgrade() throws Exception {
+ String xml = "<enabled_assistants/>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ //once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+ }
+
+ @Test
+ public void testXmlUpgradeExistingApprovedComponents() throws Exception {
+ String xml = "<enabled_assistants>"
+ + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
+ + "</enabled_assistants>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ // once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+ verify(mAssistants, times(1)).addApprovedList(
+ new ComponentName("b", "b").flattenToString(),10, true);
+ }
+
+ @Test
+ public void testXmlUpgradeOnce() throws Exception {
+ String xml = "<enabled_assistants/>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mAssistants.writeXml(serializer, true);
+ serializer.endDocument();
+ serializer.flush();
+
+ //once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+
+ Mockito.reset(mNm);
+
+ parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ //once per user
+ verify(mNm, never()).readDefaultAssistant(anyInt());
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 9564ab9bdfee..36136a8932c9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -48,7 +48,6 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleCalendar = new ScheduleCalendar();
mScheduleInfo = new ZenModeConfig.ScheduleInfo();
mScheduleInfo.days = new int[] {1, 2, 3, 4, 5};
- mScheduleCalendar.setSchedule(mScheduleInfo);
}
@Test
@@ -100,6 +99,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -126,6 +126,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -153,6 +154,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -171,6 +173,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testShouldExitForAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(1000));
}
@@ -179,6 +182,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testShouldExitForAlarm_beforeAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@@ -187,6 +191,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testShouldExitForAlarm_noAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@@ -195,6 +200,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testShouldExitForAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.shouldExitForAlarm(1000));
}
@@ -203,6 +209,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testMaybeSetNextAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
@@ -213,6 +220,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testMaybeSetNextAlarm_settingOn() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
@@ -223,6 +231,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testMaybeSetNextAlarm_alarmCanceled() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 10000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 0);
@@ -233,6 +242,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testMaybeSetNextAlarm_earlierAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 2000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 1500);
@@ -242,6 +252,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
@Test
public void testMaybeSetNextAlarm_laterAlarm() {
mScheduleInfo.exitAtAlarm = true;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleInfo.nextAlarm = 2000;
mScheduleCalendar.maybeSetNextAlarm(1000, 3000);
@@ -253,6 +264,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
public void testMaybeSetNextAlarm_expiredAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 998;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 999);
@@ -272,6 +284,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -289,6 +302,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -306,6 +320,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.startMinute = 16;
mScheduleInfo.endHour = 15;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -322,6 +337,7 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index e0b6f610ab55..e633053800bc 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2564,6 +2564,35 @@ public final class Telephony {
public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
/**
+ * The {@code content://} style URL to be called from DevicePolicyManagerService,
+ * can manage DPC-owned APNs.
+ * @hide
+ */
+ public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
+
+ /**
+ * The {@code content://} style URL to be called from Telephony to query APNs.
+ * When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only
+ * non-DPC-owned APNs are returned.
+ * @hide
+ */
+ public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered");
+
+ /**
+ * The {@code content://} style URL to be called from DevicePolicyManagerService
+ * or Telephony to manage whether DPC-owned APNs are enforced.
+ * @hide
+ */
+ public static final Uri ENFORCE_MANAGED_URI = Uri.parse(
+ "content://telephony/carriers/enforce_managed");
+
+ /**
+ * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
+ * @hide
+ */
+ public static final String ENFORCE_KEY = "enforced";
+
+ /**
* The default sort order for this table.
*/
public static final String DEFAULT_SORT_ORDER = "name ASC";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5a1a3e353d70..ce0b551311b0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -960,8 +960,9 @@ public class CarrierConfigManager {
public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
/**
- * String to identify carrier name in CarrierConfig app. This string is used only if
- * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true
+ * String to identify carrier name in CarrierConfig app. This string overrides SPN if
+ * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true; otherwise, it will be used if its value is provided
+ * and SPN is unavailable
* @hide
*/
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f411ef71c94b..8edc8b17c9e0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -53,6 +53,7 @@ import android.util.Log;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.ITelecomService;
@@ -2514,6 +2515,33 @@ public class TelephonyManager {
}
}
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * @hide
+ */
+ public void resetCarrierKeysForImsiEncryption() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null) {
+ throw new RuntimeException("IMSI error: Subscriber Info is null");
+ }
+ int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+ info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
+ throw new RuntimeException("IMSI error: Remote Exception");
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
+ throw new RuntimeException("IMSI error: Null Pointer exception");
+ }
+ }
+
/**
* @param keyAvailability bitmask that defines the availabilty of keys for a type.
* @param keyType the key type which is being checked. (WLAN, EPDG)
@@ -2549,7 +2577,7 @@ public class TelephonyManager {
* device keystore.
* <p>
* Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* @param imsiEncryptionInfo which includes the Key Type, the Public Key
* (java.security.PublicKey) and the Key Identifier.and the Key Identifier.
* The keyIdentifier Attribute value pair that helps a server locate
@@ -4932,6 +4960,25 @@ public class TelephonyManager {
}
/**
+ * @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
+ * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
+ * @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
+ * Corresponds to features defined in ImsFeature.
+ * @hide
+ */
+ public @Nullable IImsRegistration getImsRegistration(int slotIndex, int feature) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getImsRegistration(slotIndex, feature);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
* Set IMS registration state
*
* @param Registration state
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
new file mode 100644
index 000000000000..ea0817551369
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.CallSuper;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of data service. Services that extend DataService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow
+ * the following format:
+ * ...
+ * <service android:name=".xxxDataService"
+ * android:permission="android.permission.BIND_DATA_SERVICE" >
+ * <intent-filter>
+ * <action android:name="android.telephony.data.DataService" />
+ * </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class DataService extends Service {
+ private static final String TAG = DataService.class.getSimpleName();
+
+ public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+ public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+
+ private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE = 1;
+ private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 2;
+ private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 3;
+ private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 4;
+ private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 5;
+ private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 6;
+ private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 7;
+ private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 8;
+ private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 9;
+
+ private final HandlerThread mHandlerThread;
+
+ private final DataServiceHandler mHandler;
+
+ private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
+
+ private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>();
+
+ /**
+ * The abstract class of the actual data service implementation. The data service provider
+ * must extend this class to support data connection. Note that each instance of data service
+ * provider is associated with one physical SIM slot.
+ */
+ public class DataServiceProvider {
+
+ private final int mSlotId;
+
+ private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
+
+ /**
+ * Constructor
+ * @param slotId SIM slot id the data service provider associated with.
+ */
+ public DataServiceProvider(int slotId) {
+ mSlotId = slotId;
+ }
+
+ /**
+ * @return SIM slot id the data service provider associated with.
+ */
+ public final int getSlotId() {
+ return mSlotId;
+ }
+
+ /**
+ * Setup a data connection. The data service provider must implement this method to support
+ * establishing a packet data connection. When completed or error, the service must invoke
+ * the provided callback to notify the platform.
+ *
+ * @param accessNetworkType Access network type that the data call will be established on.
+ * Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
+ * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
+ * @param isRoaming True if the device is data roaming.
+ * @param allowRoaming True if data roaming is allowed by the user.
+ * @param isHandover True if the request is for IWLAN handover.
+ * @param linkProperties If {@code isHandover} is true, this is the link properties of the
+ * existing data connection, otherwise null.
+ * @param callback The result callback for this request.
+ */
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, boolean isHandover,
+ LinkProperties linkProperties, DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ /**
+ * Deactivate a data connection. The data service provider must implement this method to
+ * support data connection tear down. When completed or error, the service must invoke the
+ * provided callback to notify the platform.
+ *
+ * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
+ * int, DataProfile, boolean, boolean, boolean, LinkProperties, DataServiceCallback)}.
+ * @param reasonRadioShutDown True if the deactivate request reason is device shut down.
+ * @param isHandover True if the request is for IWLAN handover.
+ * @param callback The result callback for this request.
+ */
+ public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Set an APN to initial attach network.
+ *
+ * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
+ * @param isRoaming True if the device is data roaming.
+ * @param callback The result callback for this request.
+ */
+ public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Send current carrier's data profiles to the data service for data call setup. This is
+ * only for CDMA carrier that can change the profile through OTA. The data service should
+ * always uses the latest data profile sent by the framework.
+ *
+ * @param dps A list of data profiles.
+ * @param isRoaming True if the device is data roaming.
+ * @param callback The result callback for this request.
+ */
+ public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+ DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Get the active data call list.
+ *
+ * @param callback The result callback for this request.
+ */
+ public void getDataCallList(DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ private void registerForDataCallListChanged(IDataServiceCallback callback) {
+ synchronized (mDataCallListChangedCallbacks) {
+ mDataCallListChangedCallbacks.add(callback);
+ }
+ }
+
+ private void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+ synchronized (mDataCallListChangedCallbacks) {
+ mDataCallListChangedCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Notify the system that current data call list changed. Data service must invoke this
+ * method whenever there is any data call status changed.
+ *
+ * @param dataCallList List of the current active data call.
+ */
+ public final void notifyDataCallListChanged(List<DataCallResponse> dataCallList) {
+ synchronized (mDataCallListChangedCallbacks) {
+ for (IDataServiceCallback callback : mDataCallListChangedCallbacks) {
+ mHandler.obtainMessage(DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED, mSlotId,
+ 0, new DataCallListChangedIndication(dataCallList, callback))
+ .sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
+ */
+ @CallSuper
+ protected void onDestroy() {
+ mDataCallListChangedCallbacks.clear();
+ }
+ }
+
+ private static final class SetupDataCallRequest {
+ public final int accessNetworkType;
+ public final DataProfile dataProfile;
+ public final boolean isRoaming;
+ public final boolean allowRoaming;
+ public final boolean isHandover;
+ public final LinkProperties linkProperties;
+ public final IDataServiceCallback callback;
+ SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, boolean isHandover,
+ LinkProperties linkProperties, IDataServiceCallback callback) {
+ this.accessNetworkType = accessNetworkType;
+ this.dataProfile = dataProfile;
+ this.isRoaming = isRoaming;
+ this.allowRoaming = allowRoaming;
+ this.linkProperties = linkProperties;
+ this.isHandover = isHandover;
+ this.callback = callback;
+ }
+ }
+
+ private static final class DeactivateDataCallRequest {
+ public final int cid;
+ public final boolean reasonRadioShutDown;
+ public final boolean isHandover;
+ public final IDataServiceCallback callback;
+ DeactivateDataCallRequest(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ IDataServiceCallback callback) {
+ this.cid = cid;
+ this.reasonRadioShutDown = reasonRadioShutDown;
+ this.isHandover = isHandover;
+ this.callback = callback;
+ }
+ }
+
+ private static final class SetInitialAttachApnRequest {
+ public final DataProfile dataProfile;
+ public final boolean isRoaming;
+ public final IDataServiceCallback callback;
+ SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback) {
+ this.dataProfile = dataProfile;
+ this.isRoaming = isRoaming;
+ this.callback = callback;
+ }
+ }
+
+ private static final class SetDataProfileRequest {
+ public final List<DataProfile> dps;
+ public final boolean isRoaming;
+ public final IDataServiceCallback callback;
+ SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback) {
+ this.dps = dps;
+ this.isRoaming = isRoaming;
+ this.callback = callback;
+ }
+ }
+
+ private static final class DataCallListChangedIndication {
+ public final List<DataCallResponse> dataCallList;
+ public final IDataServiceCallback callback;
+ DataCallListChangedIndication(List<DataCallResponse> dataCallList,
+ IDataServiceCallback callback) {
+ this.dataCallList = dataCallList;
+ this.callback = callback;
+ }
+ }
+
+ private class DataServiceHandler extends Handler {
+
+ DataServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ IDataServiceCallback callback;
+ final int slotId = message.arg1;
+ DataServiceProvider service;
+
+ synchronized (mServiceMap) {
+ service = mServiceMap.get(slotId);
+ }
+
+ switch (message.what) {
+ case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
+ service = createDataServiceProvider(message.arg1);
+ if (service != null) {
+ mServiceMap.put(slotId, service);
+ }
+ break;
+ case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
+ if (service == null) break;
+ SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
+ service.setupDataCall(setupDataCallRequest.accessNetworkType,
+ setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
+ setupDataCallRequest.allowRoaming, setupDataCallRequest.isHandover,
+ setupDataCallRequest.linkProperties,
+ new DataServiceCallback(setupDataCallRequest.callback));
+
+ break;
+ case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
+ if (service == null) break;
+ DeactivateDataCallRequest deactivateDataCallRequest =
+ (DeactivateDataCallRequest) message.obj;
+ service.deactivateDataCall(deactivateDataCallRequest.cid,
+ deactivateDataCallRequest.reasonRadioShutDown,
+ deactivateDataCallRequest.isHandover,
+ new DataServiceCallback(deactivateDataCallRequest.callback));
+ break;
+ case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
+ if (service == null) break;
+ SetInitialAttachApnRequest setInitialAttachApnRequest =
+ (SetInitialAttachApnRequest) message.obj;
+ service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+ setInitialAttachApnRequest.isRoaming,
+ new DataServiceCallback(setInitialAttachApnRequest.callback));
+ break;
+ case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
+ if (service == null) break;
+ SetDataProfileRequest setDataProfileRequest =
+ (SetDataProfileRequest) message.obj;
+ service.setDataProfile(setDataProfileRequest.dps,
+ setDataProfileRequest.isRoaming,
+ new DataServiceCallback(setDataProfileRequest.callback));
+ break;
+ case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
+ if (service == null) break;
+
+ service.getDataCallList(new DataServiceCallback(
+ (IDataServiceCallback) message.obj));
+ break;
+ case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
+ if (service == null) break;
+ service.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+ break;
+ case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
+ if (service == null) break;
+ callback = (IDataServiceCallback) message.obj;
+ service.unregisterForDataCallListChanged(callback);
+ break;
+ case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
+ if (service == null) break;
+ DataCallListChangedIndication indication =
+ (DataCallListChangedIndication) message.obj;
+ try {
+ indication.callback.onDataCallListChanged(indication.dataCallList);
+ } catch (RemoteException e) {
+ loge("Failed to call onDataCallListChanged. " + e);
+ }
+ break;
+ }
+ }
+ }
+
+ private DataService() {
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+
+ mHandler = new DataServiceHandler(mHandlerThread.getLooper());
+ log("Data service created");
+ }
+
+ /**
+ * Create the instance of {@link DataServiceProvider}. Data service provider must override
+ * this method to facilitate the creation of {@link DataServiceProvider} instances. The system
+ * will call this method after binding the data service for each active SIM slot id.
+ *
+ * @param slotId SIM slot id the data service associated with.
+ * @return Data service object
+ */
+ public abstract DataServiceProvider createDataServiceProvider(int slotId);
+
+ /** @hide */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent == null || !DATA_SERVICE_INTERFACE.equals(intent.getAction())) {
+ loge("Unexpected intent " + intent);
+ return null;
+ }
+
+ int slotId = intent.getIntExtra(
+ DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+
+ if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+ loge("Invalid slot id " + slotId);
+ return null;
+ }
+
+ log("onBind: slot id=" + slotId);
+
+ IDataServiceWrapper binder = mBinderMap.get(slotId);
+ if (binder == null) {
+ Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
+ msg.arg1 = slotId;
+ msg.sendToTarget();
+
+ binder = new IDataServiceWrapper(slotId);
+ mBinderMap.put(slotId, binder);
+ }
+
+ return binder;
+ }
+
+ /** @hide */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ if (mBinderMap.get(slotId) != null) {
+ DataServiceProvider serviceImpl;
+ synchronized (mServiceMap) {
+ serviceImpl = mServiceMap.get(slotId);
+ }
+ if (serviceImpl != null) {
+ serviceImpl.onDestroy();
+ }
+ mBinderMap.remove(slotId);
+ }
+
+ // If all clients unbinds, quit the handler thread
+ if (mBinderMap.size() == 0) {
+ mHandlerThread.quit();
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ @Override
+ public void onDestroy() {
+ synchronized (mServiceMap) {
+ for (int i = 0; i < mServiceMap.size(); i++) {
+ DataServiceProvider serviceImpl = mServiceMap.get(i);
+ if (serviceImpl != null) {
+ serviceImpl.onDestroy();
+ }
+ }
+ mServiceMap.clear();
+ }
+
+ mHandlerThread.quit();
+ }
+
+ /**
+ * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
+ */
+ private class IDataServiceWrapper extends IDataService.Stub {
+
+ private final int mSlotId;
+
+ IDataServiceWrapper(int slotId) {
+ mSlotId = slotId;
+ }
+
+ @Override
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+ boolean isRoaming, boolean allowRoaming, boolean isHandover,
+ LinkProperties linkProperties, IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0,
+ new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
+ allowRoaming, isHandover, linkProperties, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0,
+ new DeactivateDataCallRequest(cid, reasonRadioShutDown, isHandover, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0,
+ new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0,
+ new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
+ }
+
+ @Override
+ public void getDataCallList(IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0,
+ callback).sendToTarget();
+ }
+
+ @Override
+ public void registerForDataCallListChanged(IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("Callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+ 0, callback).sendToTarget();
+ }
+
+ @Override
+ public void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("Callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+ 0, callback).sendToTarget();
+ }
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
new file mode 100644
index 000000000000..b6a81f94028b
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+import android.telephony.data.DataService.DataServiceProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * Data service callback, which is for bound data service to invoke for solicited and unsolicited
+ * response. The caller is responsible to create a callback object for each single asynchronous
+ * request.
+ *
+ * @hide
+ */
+@SystemApi
+public class DataServiceCallback {
+
+ private static final String mTag = DataServiceCallback.class.getSimpleName();
+
+ /**
+ * Result of data requests
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+ RESULT_ERROR_ILLEGAL_STATE})
+ public @interface Result {}
+
+ /** Request is completed successfully */
+ public static final int RESULT_SUCCESS = 0;
+ /** Request is not support */
+ public static final int RESULT_ERROR_UNSUPPORTED = 1;
+ /** Request contains invalid arguments */
+ public static final int RESULT_ERROR_INVALID_ARG = 2;
+ /** Service is busy */
+ public static final int RESULT_ERROR_BUSY = 3;
+ /** Request sent in illegal state */
+ public static final int RESULT_ERROR_ILLEGAL_STATE = 4;
+
+ private final WeakReference<IDataServiceCallback> mCallback;
+
+ /** @hide */
+ public DataServiceCallback(IDataServiceCallback callback) {
+ mCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
+ * DataProfile, boolean, boolean, boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ * @param response Setup data call response.
+ */
+ public void onSetupDataCallComplete(@Result int result, DataCallResponse response) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetupDataCallComplete(result, response);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onSetupDataCallComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
+ * boolean, boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ */
+ public void onDeactivateDataCallComplete(@Result int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onDeactivateDataCallComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onDeactivateDataCallComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
+ * DataProfile, boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ */
+ public void onSetInitialAttachApnComplete(@Result int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetInitialAttachApnComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onSetInitialAttachApnComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
+ * boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ */
+ @SystemApi
+ public void onSetDataProfileComplete(@Result int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetDataProfileComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onSetDataProfileComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#getDataCallList(
+ * DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ * @param dataCallList List of the current active data connection.
+ */
+ public void onGetDataCallListComplete(@Result int result, List<DataCallResponse> dataCallList) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onGetDataCallListComplete(result, dataCallList);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onGetDataCallListComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate that data connection list changed.
+ *
+ * @param dataCallList List of the current active data connection.
+ */
+ public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onDataCallListChanged(dataCallList);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onDataCallListChanged on the remote");
+ }
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
new file mode 100644
index 000000000000..4eaaa252da02
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.net.LinkProperties;
+import android.telephony.data.DataProfile;
+import android.telephony.data.IDataServiceCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface IDataService
+{
+ void setupDataCall(int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, boolean isHandover, in LinkProperties linkProperties,
+ IDataServiceCallback callback);
+ void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ IDataServiceCallback callback);
+ void setInitialAttachApn(in DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback);
+ void setDataProfile(in List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback);
+ void getDataCallList(IDataServiceCallback callback);
+ void registerForDataCallListChanged(IDataServiceCallback callback);
+ void unregisterForDataCallListChanged(IDataServiceCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
new file mode 100644
index 000000000000..856185b2974f
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.telephony.data.DataCallResponse;
+
+/**
+ * The call back interface
+ * @hide
+ */
+oneway interface IDataServiceCallback
+{
+ void onSetupDataCallComplete(int result, in DataCallResponse dataCallResponse);
+ void onDeactivateDataCallComplete(int result);
+ void onSetInitialAttachApnComplete(int result);
+ void onSetDataProfileComplete(int result);
+ void onGetDataCallListComplete(int result, in List<DataCallResponse> dataCallList);
+ void onDataCallListChanged(in List<DataCallResponse> dataCallList);
+}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 29849c1f9ccd..6975354c8af1 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -15,14 +15,31 @@
*/
package android.telephony.euicc;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.euicc.EuiccProfileInfo;
import android.util.Log;
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
import com.android.internal.telephony.euicc.IEuiccCardController;
import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* EuiccCardManager is the application interface to an eSIM card.
@@ -34,6 +51,35 @@ import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
public class EuiccCardManager {
private static final String TAG = "EuiccCardManager";
+ /** Reason for canceling a profile download session */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CANCEL_REASON_" }, value = {
+ CANCEL_REASON_END_USER_REJECTED,
+ CANCEL_REASON_POSTPONED,
+ CANCEL_REASON_TIMEOUT,
+ CANCEL_REASON_PPR_NOT_ALLOWED
+ })
+ public @interface CancelReason {}
+
+ /**
+ * The end user has rejected the download. The profile will be put into the error state and
+ * cannot be downloaded again without the operator's change.
+ */
+ public static final int CANCEL_REASON_END_USER_REJECTED = 0;
+
+ /** The download has been postponed and can be restarted later. */
+ public static final int CANCEL_REASON_POSTPONED = 1;
+
+ /** The download has been timed out and can be restarted later. */
+ public static final int CANCEL_REASON_TIMEOUT = 2;
+
+ /**
+ * The profile to be downloaded cannot be installed due to its policy rule is not allowed by
+ * the RAT (Rules Authorisation Table) on the eUICC or by other installed profiles. The
+ * download can be restarted later.
+ */
+ public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3;
+
/** Result code of execution with no error. */
public static final int RESULT_OK = 0;
@@ -85,4 +131,298 @@ public class EuiccCardManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Gets Rules Authorisation Table.
+ *
+ * @param callback the callback to get the result code and the rule authorisation table.
+ */
+ public void getRulesAuthTable(ResultCallback<EuiccRulesAuthTable> callback) {
+ try {
+ getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(),
+ new IGetRulesAuthTableCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
+ callback.onComplete(resultCode, rat);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getRulesAuthTable", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the eUICC challenge for new profile downloading.
+ *
+ * @param callback the callback to get the result code and the challenge.
+ */
+ public void getEuiccChallenge(ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(),
+ new IGetEuiccChallengeCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] challenge) {
+ callback.onComplete(resultCode, challenge);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccChallenge", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+ *
+ * @param callback the callback to get the result code and the info1.
+ */
+ public void getEuiccInfo1(ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(),
+ new IGetEuiccInfo1Callback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] info) {
+ callback.onComplete(resultCode, info);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccInfo1", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
+ *
+ * @param callback the callback to get the result code and the info2.
+ */
+ public void getEuiccInfo2(ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(),
+ new IGetEuiccInfo2Callback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] info) {
+ callback.onComplete(resultCode, info);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccInfo2", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Authenticates the SM-DP+ server by the eUICC.
+ *
+ * @param matchingId the activation code token defined in GSMA RSP v2.0+ or empty when it is not
+ * required.
+ * @param serverSigned1 ASN.1 data in byte array signed and returned by the SM-DP+ server.
+ * @param serverSignature1 ASN.1 data in byte array indicating a SM-DP+ signature which is
+ * returned by SM-DP+ server.
+ * @param euiccCiPkIdToBeUsed ASN.1 data in byte array indicating CI Public Key Identifier to be
+ * used by the eUICC for signature which is returned by SM-DP+ server. This is defined in
+ * GSMA RSP v2.0+.
+ * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
+ * SM-DP+ server.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
+ */
+ public void authenticateServer(String matchingId, byte[] serverSigned1,
+ byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().authenticateServer(
+ mContext.getOpPackageName(),
+ matchingId,
+ serverSigned1,
+ serverSignature1,
+ euiccCiPkIdToBeUsed,
+ serverCertificate,
+ new IAuthenticateServerCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling authenticateServer", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Prepares the profile download request sent to SM-DP+.
+ *
+ * @param hashCc the hash of confirmation code. It can be null if there is no confirmation code
+ * required.
+ * @param smdpSigned2 ASN.1 data in byte array indicating the data to be signed by the SM-DP+
+ * returned by SM-DP+ server.
+ * @param smdpSignature2 ASN.1 data in byte array indicating the SM-DP+ signature returned by
+ * SM-DP+ server.
+ * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
+ * by SM-DP+ server.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
+ */
+ public void prepareDownload(@Nullable byte[] hashCc, byte[] smdpSigned2,
+ byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().prepareDownload(
+ mContext.getOpPackageName(),
+ hashCc,
+ smdpSigned2,
+ smdpSignature2,
+ smdpCertificate,
+ new IPrepareDownloadCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling prepareDownload", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Loads a downloaded bound profile package onto the eUICC.
+ *
+ * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
+ */
+ public void loadBoundProfilePackage(byte[] boundProfilePackage,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().loadBoundProfilePackage(
+ mContext.getOpPackageName(),
+ boundProfilePackage,
+ new ILoadBoundProfilePackageCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling loadBoundProfilePackage", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Cancels the current profile download session.
+ *
+ * @param transactionId the transaction ID returned by SM-DP+ server.
+ * @param reason the cancel reason.
+ * @param callback the callback to get the result code and an byte[] which represents a
+ * {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
+ */
+ public void cancelSession(byte[] transactionId, @CancelReason int reason,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().cancelSession(
+ mContext.getOpPackageName(),
+ transactionId,
+ reason,
+ new ICancelSessionCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling cancelSession", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Lists all notifications of the given {@code notificationEvents}.
+ *
+ * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param callback the callback to get the result code and the list of notifications.
+ */
+ public void listNotifications(@EuiccNotification.Event int events,
+ ResultCallback<EuiccNotification[]> callback) {
+ try {
+ getIEuiccCardController().listNotifications(mContext.getOpPackageName(), events,
+ new IListNotificationsCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification[] notifications) {
+ callback.onComplete(resultCode, notifications);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling listNotifications", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves contents of all notification of the given {@code events}.
+ *
+ * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param callback the callback to get the result code and the list of notifications.
+ */
+ public void retrieveNotificationList(@EuiccNotification.Event int events,
+ ResultCallback<EuiccNotification[]> callback) {
+ try {
+ getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), events,
+ new IRetrieveNotificationListCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification[] notifications) {
+ callback.onComplete(resultCode, notifications);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling retrieveNotificationList", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves the content of a notification of the given {@code seqNumber}.
+ *
+ * @param seqNumber the sequence number of the notification.
+ * @param callback the callback to get the result code and the notification.
+ */
+ public void retrieveNotification(int seqNumber, ResultCallback<EuiccNotification> callback) {
+ try {
+ getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), seqNumber,
+ new IRetrieveNotificationCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification notification) {
+ callback.onComplete(resultCode, notification);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling retrieveNotification", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a notification from eUICC.
+ *
+ * @param seqNumber the sequence number of the notification.
+ * @param callback the callback to get the result code.
+ */
+ public void removeNotificationFromList(int seqNumber, ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().removeNotificationFromList(
+ mContext.getOpPackageName(),
+ seqNumber,
+ new IRemoveNotificationFromListCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ callback.onComplete(resultCode, null);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling removeNotificationFromList", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.aidl b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
new file mode 100644
index 000000000000..dad770da260d
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.euicc;
+
+parcelable EuiccNotification;
diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
new file mode 100644
index 000000000000..9785a4533c5b
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.euicc;
+
+parcelable EuiccRulesAuthTable; \ No newline at end of file
diff --git a/telephony/java/android/telephony/euicc/EuiccRat.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
index 6a56503ac380..7efe04364280 100644
--- a/telephony/java/android/telephony/euicc/EuiccRat.java
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -35,7 +35,7 @@ import java.util.Arrays;
*
* TODO(b/35851809): Make this a @SystemApi.
*/
-public final class EuiccRat implements Parcelable {
+public final class EuiccRulesAuthTable implements Parcelable {
/** Profile policy rule flags */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
@@ -50,7 +50,7 @@ public final class EuiccRat implements Parcelable {
private final CarrierIdentifier[][] mCarrierIds;
private final int[] mPolicyRuleFlags;
- /** This is used to build new {@link EuiccRat} instance. */
+ /** This is used to build new {@link EuiccRulesAuthTable} instance. */
public static final class Builder {
private int[] mPolicyRules;
private CarrierIdentifier[][] mCarrierIds;
@@ -72,7 +72,7 @@ public final class EuiccRat implements Parcelable {
* Builds the RAT instance. This builder should not be used anymore after this method is
* called, otherwise {@link NullPointerException} will be thrown.
*/
- public EuiccRat build() {
+ public EuiccRulesAuthTable build() {
if (mPosition != mPolicyRules.length) {
throw new IllegalStateException(
"Not enough rules are added, expected: "
@@ -80,7 +80,7 @@ public final class EuiccRat implements Parcelable {
+ ", added: "
+ mPosition);
}
- return new EuiccRat(mPolicyRules, mCarrierIds, mPolicyRuleFlags);
+ return new EuiccRulesAuthTable(mPolicyRules, mCarrierIds, mPolicyRuleFlags);
}
/**
@@ -125,7 +125,8 @@ public final class EuiccRat implements Parcelable {
return true;
}
- private EuiccRat(int[] policyRules, CarrierIdentifier[][] carrierIds, int[] policyRuleFlags) {
+ private EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds,
+ int[] policyRuleFlags) {
mPolicyRules = policyRules;
mCarrierIds = carrierIds;
mPolicyRuleFlags = policyRuleFlags;
@@ -207,7 +208,7 @@ public final class EuiccRat implements Parcelable {
return false;
}
- EuiccRat that = (EuiccRat) obj;
+ EuiccRulesAuthTable that = (EuiccRulesAuthTable) obj;
if (mCarrierIds.length != that.mCarrierIds.length) {
return false;
}
@@ -234,7 +235,7 @@ public final class EuiccRat implements Parcelable {
&& Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
}
- private EuiccRat(Parcel source) {
+ private EuiccRulesAuthTable(Parcel source) {
mPolicyRules = source.createIntArray();
int len = mPolicyRules.length;
mCarrierIds = new CarrierIdentifier[len][];
@@ -244,16 +245,16 @@ public final class EuiccRat implements Parcelable {
mPolicyRuleFlags = source.createIntArray();
}
- public static final Creator<EuiccRat> CREATOR =
- new Creator<EuiccRat>() {
+ public static final Creator<EuiccRulesAuthTable> CREATOR =
+ new Creator<EuiccRulesAuthTable>() {
@Override
- public EuiccRat createFromParcel(Parcel source) {
- return new EuiccRat(source);
+ public EuiccRulesAuthTable createFromParcel(Parcel source) {
+ return new EuiccRulesAuthTable(source);
}
@Override
- public EuiccRat[] newArray(int size) {
- return new EuiccRat[size];
+ public EuiccRulesAuthTable[] newArray(int size) {
+ return new EuiccRulesAuthTable[size];
}
};
}
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8230eafc2e4d..aaa0f08594d1 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -26,12 +26,14 @@ import android.telephony.CarrierConfigManager;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MMTelFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsServiceController;
import com.android.internal.annotations.VisibleForTesting;
@@ -113,6 +115,12 @@ public class ImsService extends Service {
throws RemoteException {
ImsService.this.removeImsFeature(slotId, featureType, c);
}
+
+ @Override
+ public IImsRegistration getRegistration(int slotId) throws RemoteException {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ return r != null ? r.getBinder() : null;
+ }
};
/**
@@ -174,6 +182,8 @@ public class ImsService extends Service {
f.setSlotId(slotId);
f.addImsFeatureStatusCallback(c);
addImsFeature(slotId, featureType, f);
+ // TODO: Remove once new onFeatureReady AIDL is merged in.
+ f.onFeatureReady();
}
private void addImsFeature(int slotId, int featureType, ImsFeature f) {
@@ -236,4 +246,13 @@ public class ImsService extends Service {
public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
return null;
}
+
+ /**
+ * @param slotId The slot that is associated with the IMS Registration.
+ * @return the ImsRegistration implementation associated with the slot.
+ * @hide
+ */
+ public ImsRegistrationImplBase getRegistration(int slotId) {
+ return new ImsRegistrationImplBase();
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index ca4a210e30cc..d47cea3097f3 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -96,7 +96,7 @@ public abstract class ImsFeature {
new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
private @ImsState int mState = STATE_NOT_AVAILABLE;
private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- private Context mContext;
+ protected Context mContext;
public void setContext(Context context) {
mContext = context;
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
index b7c8ca0f9799..afaf33294d8a 100644
--- a/telephony/java/android/telephony/ims/internal/ImsService.java
+++ b/telephony/java/android/telephony/ims/internal/ImsService.java
@@ -24,7 +24,6 @@ import android.telephony.CarrierConfigManager;
import android.telephony.ims.internal.aidl.IImsConfig;
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
import android.telephony.ims.internal.aidl.IImsServiceController;
import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
import android.telephony.ims.internal.feature.ImsFeature;
@@ -32,11 +31,12 @@ import android.telephony.ims.internal.feature.MmTelFeature;
import android.telephony.ims.internal.feature.RcsFeature;
import android.telephony.ims.internal.stub.ImsConfigImplBase;
import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
import com.android.internal.annotations.VisibleForTesting;
/**
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
index 8332bc024e37..43f5098af3ca 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
@@ -24,4 +24,5 @@ import com.android.ims.internal.IImsCallSession;
*/
oneway interface IImsMmTelListener {
void onIncomingCall(IImsCallSession c);
+ void onVoiceMessageCountUpdate(int count);
} \ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
index 8afb95588b01..82a85254bbca 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
@@ -18,12 +18,12 @@ package android.telephony.ims.internal.aidl;
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
import android.telephony.ims.internal.aidl.IImsConfig;
import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
/**
* See ImsService and MmTelFeature for more information.
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
index 4d188734c10e..5dbf077ee7c5 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
@@ -18,7 +18,7 @@ package android.telephony.ims.internal.feature;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.ArraySet;
import java.util.ArrayList;
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
index 238411ee4f8f..8d888c2bcb28 100644
--- a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
@@ -28,8 +28,8 @@ import android.telephony.ims.internal.aidl.IImsCallSessionListener;
import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
import android.telephony.ims.internal.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
@@ -262,6 +262,15 @@ public class MmTelFeature extends ImsFeature {
}
/**
+ * Updates the Listener when the voice message count for IMS has changed.
+ * @param count an integer representing the new message count.
+ */
+ @Override
+ public void onVoiceMessageCountUpdate(int count) {
+
+ }
+
+ /**
* Called when the IMS provider receives an incoming call.
* @param c The {@link ImsCallSession} associated with the new call.
*/
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 558b009ab4c2..42af08365f61 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -14,16 +14,19 @@
* limitations under the License
*/
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.net.Uri;
+import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsRegistration;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
import android.util.Log;
import com.android.ims.ImsReasonInfo;
+import com.android.ims.internal.IImsRegistration;
+import com.android.ims.internal.IImsRegistrationCallback;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -62,23 +65,25 @@ public class ImsRegistrationImplBase {
// Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
// state.
+ // The unknown state is set as the initialization state. This is so that we do not call back
+ // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
+ // yet.
+ private static final int REGISTRATION_STATE_UNKNOWN = -1;
private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
private static final int REGISTRATION_STATE_REGISTERING = 1;
private static final int REGISTRATION_STATE_REGISTERED = 2;
-
/**
* Callback class for receiving Registration callback events.
+ * @hide
*/
- public static class Callback extends IImsRegistrationCallback.Stub {
-
+ public static class Callback {
/**
* Notifies the framework when the IMS Provider is connected to the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
- @Override
public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
}
@@ -88,7 +93,6 @@ public class ImsRegistrationImplBase {
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
- @Override
public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
}
@@ -97,7 +101,6 @@ public class ImsRegistrationImplBase {
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
- @Override
public void onDeregistered(ImsReasonInfo info) {
}
@@ -108,10 +111,19 @@ public class ImsRegistrationImplBase {
* @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
- @Override
public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
}
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ */
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+
+ }
}
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@@ -139,9 +151,9 @@ public class ImsRegistrationImplBase {
private @ImsRegistrationTech
int mConnectionType = REGISTRATION_TECH_NONE;
// Locked on mLock
- private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
- // Locked on mLock
- private ImsReasonInfo mLastDisconnectCause;
+ private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
+ // Locked on mLock, create unspecified disconnect cause.
+ private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
public final IImsRegistration getBinder() {
return mBinder;
@@ -221,6 +233,17 @@ public class ImsRegistrationImplBase {
});
}
+ public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ mCallbacks.broadcast((c) -> {
+ try {
+ c.onSubscriberAssociatedUriChanged(uris);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
private void updateToState(@ImsRegistrationTech int connType, int newState) {
synchronized (mLock) {
mConnectionType = connType;
@@ -241,7 +264,8 @@ public class ImsRegistrationImplBase {
}
}
- private @ImsRegistrationTech int getConnectionType() {
+ @VisibleForTesting
+ public final @ImsRegistrationTech int getConnectionType() {
synchronized (mLock) {
return mConnectionType;
}
@@ -271,6 +295,10 @@ public class ImsRegistrationImplBase {
c.onRegistered(getConnectionType());
break;
}
+ case REGISTRATION_STATE_UNKNOWN: {
+ // Do not callback if the state has not been updated yet by the ImsService.
+ break;
+ }
}
}
}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
index 687b7ca408d5..6de264ec90fb 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
@@ -15,10 +15,9 @@
*/
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import com.android.ims.internal.IImsRegistrationCallback;
/**
* See ImsRegistration for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
index a50575b96865..5f21167422dc 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
@@ -15,8 +15,9 @@
*/
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
+import android.net.Uri;
import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
import com.android.ims.ImsReasonInfo;
@@ -31,4 +32,5 @@ oneway interface IImsRegistrationCallback {
void onRegistering(int imsRadioTech);
void onDeregistered(in ImsReasonInfo info);
void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
+ void onSubscriberAssociatedUriChanged(in Uri[] uris);
} \ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 857089fac33a..7ac25ac13fbe 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -18,6 +18,7 @@ package com.android.ims.internal;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsRcsFeature;
/**
@@ -29,4 +30,5 @@ interface IImsServiceController {
IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ IImsRegistration getRegistration(int slotId);
}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 0f3182136997..f8a040da30ba 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -152,6 +152,13 @@ interface IPhoneSubInfo {
in ImsiEncryptionInfo imsiEncryptionInfo);
/**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ */
+ void resetCarrierKeysForImsiEncryption(int subId, String callingPackage);
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
String getVoiceMailAlphaTag(String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 416146fcb98e..fba82ee17f77 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -40,6 +40,7 @@ import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.OperatorInfo;
@@ -808,6 +809,11 @@ interface ITelephony {
IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
/**
+ * Returns the IImsRegistration associated with the slot and feature specified.
+ */
+ IImsRegistration getImsRegistration(int slotId, int feature);
+
+ /**
* Set the network selection mode to automatic.
*
* @param subId the id of the subscription to update.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index f29d993c55da..51369d06bf46 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -486,4 +486,10 @@ public class TelephonyIntents {
*/
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
"com.android.omadm.service.CONFIGURATION_UPDATE";
+
+ /**
+ * Broadcast action to trigger the Carrier Certificate download.
+ */
+ public static final String ACTION_CARRIER_CERTIFICATE_DOWNLOAD =
+ "com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD";
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
new file mode 100644
index 000000000000..8a77bf127f9e
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+/** @hide */
+oneway interface IAuthenticateServerCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
new file mode 100644
index 000000000000..f6b99e2b9801
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+/** @hide */
+oneway interface ICancelSessionCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index 2846a1ad1f9f..ba9b05e2654f 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -17,8 +17,41 @@
package com.android.internal.telephony.euicc;
import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
/** @hide */
interface IEuiccCardController {
oneway void getAllProfiles(String callingPackage, in IGetAllProfilesCallback callback);
+ oneway void getRulesAuthTable(String callingPackage, in IGetRulesAuthTableCallback callback);
+ oneway void getEuiccChallenge(String callingPackage, in IGetEuiccChallengeCallback callback);
+ oneway void getEuiccInfo1(String callingPackage, in IGetEuiccInfo1Callback callback);
+ oneway void getEuiccInfo2(String callingPackage, in IGetEuiccInfo2Callback callback);
+ oneway void authenticateServer(String callingPackage, String matchingId,
+ in byte[] serverSigned1, in byte[] serverSignature1, in byte[] euiccCiPkIdToBeUsed,
+ in byte[] serverCertificatein, in IAuthenticateServerCallback callback);
+ oneway void prepareDownload(String callingPackage, in byte[] hashCc, in byte[] smdpSigned2,
+ in byte[] smdpSignature2, in byte[] smdpCertificate, in IPrepareDownloadCallback callback);
+ oneway void loadBoundProfilePackage(String callingPackage, in byte[] boundProfilePackage,
+ in ILoadBoundProfilePackageCallback callback);
+ oneway void cancelSession(String callingPackage, in byte[] transactionId, int reason,
+ in ICancelSessionCallback callback);
+ oneway void listNotifications(String callingPackage, int events,
+ in IListNotificationsCallback callback);
+ oneway void retrieveNotificationList(String callingPackage, int events,
+ in IRetrieveNotificationListCallback callback);
+ oneway void retrieveNotification(String callingPackage, int seqNumber,
+ in IRetrieveNotificationCallback callback);
+ oneway void removeNotificationFromList(String callingPackage, int seqNumber,
+ in IRemoveNotificationFromListCallback callback);
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
new file mode 100644
index 000000000000..5ffb3400912a
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+/** @hide */
+oneway interface IGetEuiccChallengeCallback {
+ void onComplete(int resultCode, in byte[] challenge);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
new file mode 100644
index 000000000000..9592acb6d330
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+/** @hide */
+oneway interface IGetEuiccInfo1Callback {
+ void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
new file mode 100644
index 000000000000..5256b35c7516
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+/** @hide */
+oneway interface IGetEuiccInfo2Callback {
+ void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
new file mode 100644
index 000000000000..58f0bde65b86
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccRulesAuthTable;
+
+/** @hide */
+oneway interface IGetRulesAuthTableCallback {
+ void onComplete(int resultCode, in EuiccRulesAuthTable rat);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
new file mode 100644
index 000000000000..65aa302a6a64
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
+
+/** @hide */
+oneway interface IListNotificationsCallback {
+ void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
new file mode 100644
index 000000000000..4ad7081c2e67
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+/** @hide */
+oneway interface ILoadBoundProfilePackageCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
new file mode 100644
index 000000000000..c0351841084e
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+/** @hide */
+oneway interface IPrepareDownloadCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
new file mode 100644
index 000000000000..b22d0da5dc5f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
+
+/** @hide */
+oneway interface IRemoveNotificationFromListCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
new file mode 100644
index 000000000000..dd8889a94143
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
+
+/** @hide */
+oneway interface IRetrieveNotificationCallback {
+ void onComplete(int resultCode, in EuiccNotification notification);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
new file mode 100644
index 000000000000..bc4e451329af
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
+
+/** @hide */
+oneway interface IRetrieveNotificationListCallback {
+ void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index d2da613c3832..ccf57b00a379 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -49,7 +49,8 @@ java_library {
// Build the repackaged.android.test.base library
// ==============================================
-// This contains repackaged versions of the classes from legacy-test.
+// This contains repackaged versions of the classes from
+// android.test.base.
java_library_static {
name: "repackaged.android.test.base",
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 8eddec48611b..b1ae40e17b9d 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -24,7 +24,6 @@ java_library {
no_framework_libs: true,
libs: [
"framework",
- "legacy-test",
],
}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index d495e90ac1d5..dfaeed5e271e 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -40,7 +40,7 @@ java_library {
no_framework_libs: true,
libs: [
"framework",
- "legacy-test",
+ "android.test.base",
"android.test.mock",
"junit",
],
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index efc01f2ace6f..f6c5532363e8 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -36,19 +36,16 @@ public class IpSecConfigTest {
public void testDefaults() throws Exception {
IpSecConfig c = new IpSecConfig();
assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode());
- assertEquals("", c.getLocalAddress());
- assertEquals("", c.getRemoteAddress());
+ assertEquals("", c.getSourceAddress());
+ assertEquals("", c.getDestinationAddress());
assertNull(c.getNetwork());
assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType());
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId());
assertEquals(0, c.getEncapRemotePort());
assertEquals(0, c.getNattKeepaliveInterval());
- for (int direction :
- new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) {
- assertNull(c.getEncryption(direction));
- assertNull(c.getAuthentication(direction));
- assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction));
- }
+ assertNull(c.getEncryption());
+ assertNull(c.getAuthentication());
+ assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
}
@Test
@@ -57,34 +54,21 @@ public class IpSecConfigTest {
IpSecConfig c = new IpSecConfig();
c.setMode(IpSecTransform.MODE_TUNNEL);
- c.setLocalAddress("0.0.0.0");
- c.setRemoteAddress("1.2.3.4");
+ c.setSourceAddress("0.0.0.0");
+ c.setDestinationAddress("1.2.3.4");
c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
c.setEncapSocketResourceId(7);
c.setEncapRemotePort(22);
c.setNattKeepaliveInterval(42);
c.setEncryption(
- IpSecTransform.DIRECTION_OUT,
new IpSecAlgorithm(
IpSecAlgorithm.CRYPT_AES_CBC,
new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
c.setAuthentication(
- IpSecTransform.DIRECTION_OUT,
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_MD5,
new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
- c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984);
- c.setEncryption(
- IpSecTransform.DIRECTION_IN,
- new IpSecAlgorithm(
- IpSecAlgorithm.CRYPT_AES_CBC,
- new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
- c.setAuthentication(
- IpSecTransform.DIRECTION_IN,
- new IpSecAlgorithm(
- IpSecAlgorithm.AUTH_HMAC_MD5,
- new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1}));
- c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99);
+ c.setSpiResourceId(1984);
assertParcelingIsLossless(c);
}
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 0f40b4562b0d..cc3366fbc832 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -81,15 +81,13 @@ public class IpSecManagerTest {
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
when(mMockIpSecService.allocateSecurityParameterIndex(
- eq(IpSecTransform.DIRECTION_IN),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(DROID_SPI),
anyObject()))
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex droidSpi =
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, DROID_SPI);
assertEquals(DROID_SPI, droidSpi.getSpi());
droidSpi.close();
@@ -103,15 +101,13 @@ public class IpSecManagerTest {
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
when(mMockIpSecService.allocateSecurityParameterIndex(
- eq(IpSecTransform.DIRECTION_OUT),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
anyObject()))
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex randomSpi =
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
assertEquals(DROID_SPI, randomSpi.getSpi());
@@ -124,16 +120,15 @@ public class IpSecManagerTest {
* Throws resource unavailable exception
*/
@Test
- public void testAllocSpiResUnavaiableExeption() throws Exception {
+ public void testAllocSpiResUnavailableException() throws Exception {
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
when(mMockIpSecService.allocateSecurityParameterIndex(
- anyInt(), anyString(), anyInt(), anyObject()))
+ anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -143,15 +138,14 @@ public class IpSecManagerTest {
* Throws spi unavailable exception
*/
@Test
- public void testAllocSpiSpiUnavaiableExeption() throws Exception {
+ public void testAllocSpiSpiUnavailableException() throws Exception {
IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
when(mMockIpSecService.allocateSecurityParameterIndex(
- anyInt(), anyString(), anyInt(), anyObject()))
+ anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -163,8 +157,7 @@ public class IpSecManagerTest {
@Test
public void testRequestAllocInvalidSpi() throws Exception {
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, 0);
fail("Able to allocate invalid spi");
} catch (IllegalArgumentException e) {
}
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 473dc538f09d..9aad413c354b 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -67,7 +67,7 @@ public class MacAddressTest {
assertEquals(msg, t.expectedType, got);
if (got != MacAddress.TYPE_UNKNOWN) {
- assertEquals(got, MacAddress.fromBytes(t.addr).addressType());
+ assertEquals(got, MacAddress.fromBytes(t.addr).getAddressType());
}
}
}
@@ -191,7 +191,7 @@ public class MacAddressTest {
assertTrue(stringRepr + " expected to be a locally assigned address",
mac.isLocallyAssigned());
- assertEquals(MacAddress.TYPE_UNICAST, mac.addressType());
+ assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType());
assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
stringRepr.startsWith(expectedLocalOui));
}
diff --git a/tests/net/java/android/net/NetworkTest.java b/tests/net/java/android/net/NetworkTest.java
index bacf986b3627..94d01e91d03b 100644
--- a/tests/net/java/android/net/NetworkTest.java
+++ b/tests/net/java/android/net/NetworkTest.java
@@ -147,9 +147,9 @@ public class NetworkTest {
// Adjust as necessary to test an implementation's specific constants.
// When running with runtest, "adb logcat -s TestRunner" can be useful.
- assertEquals(4311403230L, one.getNetworkHandle());
- assertEquals(8606370526L, two.getNetworkHandle());
- assertEquals(12901337822L, three.getNetworkHandle());
+ assertEquals(7700664333L, one.getNetworkHandle());
+ assertEquals(11995631629L, two.getNetworkHandle());
+ assertEquals(16290598925L, three.getNetworkHandle());
}
private static <T> void assertNotEqual(T t1, T t2) {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2b0349c6fa83..b8e37f3a10ea 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1392,39 +1392,75 @@ public class ConnectivityServiceTest {
return null;
}
- void expectAvailableCallbacks(
- MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+ // Expects onAvailable and the callbacks that follow it. These are:
+ // - onSuspended, iff the network was suspended when the callbacks fire.
+ // - onCapabilitiesChanged.
+ // - onLinkPropertiesChanged.
+ //
+ // @param agent the network to expect the callbacks on.
+ // @param expectSuspended whether to expect a SUSPENDED callback.
+ // @param expectValidated the expected value of the VALIDATED capability in the
+ // onCapabilitiesChanged callback.
+ // @param timeoutMs how long to wait for the callbacks.
+ void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
+ boolean expectValidated, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
}
- expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+ if (expectValidated) {
+ expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ } else {
+ expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent);
+ }
expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
}
- void expectAvailableCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ // Expects the available callbacks (validated), plus onSuspended.
+ void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
+ expectAvailableCallbacks(agent, true, expectValidated, TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, true, TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, false, TIMEOUT_MS);
}
- void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+ // Expects the available callbacks (where the onCapabilitiesChanged must contain the
+ // VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
+ // one we just sent.
+ // TODO: this is likely a bug. Fix it and remove this method.
+ void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
+ expectCallback(CallbackState.AVAILABLE, agent, TIMEOUT_MS);
+ NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ expectCallback(CallbackState.LINK_PROPERTIES, agent, TIMEOUT_MS);
+ NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ assertEquals(nc1, nc2);
}
- void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ // Expects the available callbacks where the onCapabilitiesChanged must not have validated,
+ // then expects another onCapabilitiesChanged that has the validated bit set. This is used
+ // when a network connects and satisfies a callback, and then immediately validates.
+ void expectAvailableThenValidatedCallbacks(MockNetworkAgent agent) {
+ expectAvailableCallbacksUnvalidated(agent);
expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
}
- void expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
+ NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
assertTrue(nc.hasCapability(capability));
+ return nc;
}
- void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+ NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
assertFalse(nc.hasCapability(capability));
+ return nc;
}
void assertNoCallback() {
@@ -1461,8 +1497,8 @@ public class ConnectivityServiceTest {
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- genericNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
- cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1476,8 +1512,8 @@ public class ConnectivityServiceTest {
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- wifiNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1500,8 +1536,8 @@ public class ConnectivityServiceTest {
// Test validated networks
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- genericNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1513,10 +1549,10 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- wifiNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1552,32 +1588,32 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.connect(true);
// We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
// We then get LOSING when wifi validates and cell is outscored.
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
for (int i = 0; i < 4; i++) {
MockNetworkAgent oldNetwork, newNetwork;
@@ -1594,7 +1630,7 @@ public class ConnectivityServiceTest {
callback.expectCallback(CallbackState.LOSING, oldNetwork);
// TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
// longer lingering?
- defaultCallback.expectAvailableCallbacks(newNetwork);
+ defaultCallback.expectAvailableCallbacksValidated(newNetwork);
assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
}
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1614,7 +1650,7 @@ public class ConnectivityServiceTest {
// Disconnect our test networks.
mWiFiNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1630,22 +1666,22 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false); // Score: 10
- callback.expectAvailableCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false); // Score: 20
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
@@ -1653,33 +1689,33 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1687,12 +1723,12 @@ public class ConnectivityServiceTest {
// If a network is lingering, and we add and remove a request from it, resume lingering.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
@@ -1711,7 +1747,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Cell is now the default network. Pin it with a cell-specific request.
noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1720,8 +1756,8 @@ public class ConnectivityServiceTest {
// Now connect wifi, and expect it to become the default network.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// The default request is lingering on cell, but nothing happens to cell, and we send no
// callbacks for it, because it's kept up by cellRequest.
callback.assertNoCallback();
@@ -1737,14 +1773,14 @@ public class ConnectivityServiceTest {
// Register a TRACK_DEFAULT request and check that it does not affect lingering.
TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(trackDefaultCallback);
- trackDefaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
- trackDefaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
// Let linger run its course.
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
@@ -1771,13 +1807,13 @@ public class ConnectivityServiceTest {
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up unvalidated wifi with explicitlySelected=true.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// Cell Remains the default.
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1800,7 +1836,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the
// network to disconnect.
@@ -1811,7 +1847,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1820,7 +1856,7 @@ public class ConnectivityServiceTest {
// TODO: fix this.
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
@@ -1993,7 +2029,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mCellNetworkAgent.connectWithoutInternet();
- networkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
@@ -2022,7 +2058,7 @@ public class ConnectivityServiceTest {
MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
- networkCallback.expectAvailableCallbacks(mmsNetworkAgent);
+ networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
@@ -2049,7 +2085,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
@@ -2062,7 +2098,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
@@ -2072,9 +2108,7 @@ public class ConnectivityServiceTest {
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- // TODO: Investigate only sending available callbacks.
- validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -2098,7 +2132,7 @@ public class ConnectivityServiceTest {
// Bring up wifi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- validatedCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Check that calling startCaptivePortalApp does nothing.
@@ -2109,7 +2143,7 @@ public class ConnectivityServiceTest {
// Turn into a captive portal.
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 302;
mCm.reportNetworkConnectivity(wifiNetwork, false);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Check that startCaptivePortalApp sends the expected intent.
@@ -2122,7 +2156,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
CaptivePortal c = (CaptivePortal) intent.getExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
c.reportCaptivePortalDismissed();
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(validatedCallback);
@@ -2165,7 +2199,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
// But there should be no CaptivePortal callback.
captivePortalCallback.assertNoCallback();
}
@@ -2203,14 +2237,14 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- cEmpty1.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty2.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty3.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty4.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty3.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertNoCallbacks(cFoo, cBar);
mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo"));
- cFoo.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
@@ -2219,7 +2253,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar"));
cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- cBar.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
@@ -2348,14 +2382,14 @@ public class ConnectivityServiceTest {
// Bring up cell and expect CALLBACK_AVAILABLE.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up wifi and expect CALLBACK_AVAILABLE.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
- defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
@@ -2365,7 +2399,7 @@ public class ConnectivityServiceTest {
// Bring up cell. Expect no default network callback, since it won't be the default.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
// Bring down wifi. Expect the default network callback to notified of LOST wifi
@@ -2373,7 +2407,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.disconnect();
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -2394,7 +2428,7 @@ public class ConnectivityServiceTest {
// We should get onAvailable(), onCapabilitiesChanged(), and
// onLinkPropertiesChanged() in rapid succession. Additionally, we
// should get onCapabilitiesChanged() when the mobile network validates.
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Update LinkProperties.
@@ -2415,7 +2449,7 @@ public class ConnectivityServiceTest {
mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
// We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
// as well as onNetworkSuspended() in rapid succession.
- dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent);
+ dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true);
dfltNetworkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(dfltNetworkCallback);
@@ -2455,18 +2489,18 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- fgCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
// When wifi connects, cell lingers.
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- fgCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
@@ -2490,8 +2524,8 @@ public class ConnectivityServiceTest {
// is currently delivered before the onAvailable() callbacks.
// TODO: Fix this.
cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
- cellCallback.expectAvailableCallbacks(mCellNetworkAgent);
- fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Expect a network capabilities update with FOREGROUND, because the most recent
// request causes its state to change.
callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
@@ -2511,7 +2545,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mCm.unregisterNetworkCallback(callback);
@@ -2651,7 +2685,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
testFactory.expectAddRequests(2); // Because the cell request changes score twice.
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
testFactory.waitForNetworkRequests(2);
assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us.
@@ -2742,16 +2776,15 @@ public class ConnectivityServiceTest {
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
Network cellNetwork = mCellNetworkAgent.getNetwork();
// Bring up validated wifi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
@@ -2772,18 +2805,18 @@ public class ConnectivityServiceTest {
// that we switch back to cell.
tracker.configRestrictsAvoidBadWifi = false;
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// Switch back to a restrictive carrier.
tracker.configRestrictsAvoidBadWifi = true;
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
// Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
mCm.setAvoidUnvalidated(wifiNetwork);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2794,9 +2827,8 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.disconnect();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
@@ -2810,7 +2842,7 @@ public class ConnectivityServiceTest {
tracker.reevaluate();
// We now switch to cell.
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2821,17 +2853,17 @@ public class ConnectivityServiceTest {
// We switch to wifi and then to cell.
Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedWifiCallback.assertNoCallback();
mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2873,7 +2905,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, timeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, timeoutMs);
// pass timeout and validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
@@ -2894,7 +2926,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
final int assertTimeoutMs = 100;
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, assertTimeoutMs);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3381,7 +3413,7 @@ public class ConnectivityServiceTest {
// Bring up wifi aware network.
wifiAware.connect(false, false);
- callback.expectAvailableCallbacks(wifiAware);
+ callback.expectAvailableCallbacksUnvalidated(wifiAware);
assertNull(mCm.getActiveNetworkInfo());
assertNull(mCm.getActiveNetwork());
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 2282c1319a9a..4fbb228e6e53 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -32,7 +32,6 @@ import android.net.IpSecAlgorithm;
import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecSpiResponse;
-import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.NetworkUtils;
import android.os.Binder;
@@ -54,14 +53,14 @@ import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class IpSecServiceParameterizedTest {
- private static final int TEST_SPI_OUT = 0xD1201D;
- private static final int TEST_SPI_IN = TEST_SPI_OUT + 1;
+ private static final int TEST_SPI = 0xD1201D;
- private final String mRemoteAddr;
+ private final String mDestinationAddr;
+ private final String mSourceAddr;
@Parameterized.Parameters
public static Collection ipSecConfigs() {
- return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
+ return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}});
}
private static final byte[] AEAD_KEY = {
@@ -96,11 +95,9 @@ public class IpSecServiceParameterizedTest {
private static final IpSecAlgorithm AEAD_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
- private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
-
- public IpSecServiceParameterizedTest(String remoteAddr) {
- mRemoteAddr = remoteAddr;
+ public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) {
+ mSourceAddr = sourceAddr;
+ mDestinationAddr = destAddr;
}
@Before
@@ -116,44 +113,30 @@ public class IpSecServiceParameterizedTest {
@Test
public void testIpSecServiceReserveSpi() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
assertEquals(IpSecManager.Status.OK, spiResp.status);
- assertEquals(TEST_SPI_OUT, spiResp.spi);
+ assertEquals(TEST_SPI, spiResp.spi);
}
@Test
public void testReleaseSecurityParameterIndex() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
- anyInt(),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
+ eq(spiResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
@@ -169,17 +152,12 @@ public class IpSecServiceParameterizedTest {
@Test
public void testSecurityParameterIndexBinderDeath() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -190,11 +168,7 @@ public class IpSecServiceParameterizedTest {
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
- anyInt(),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
+ eq(spiResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
@@ -206,14 +180,12 @@ public class IpSecServiceParameterizedTest {
}
}
- private int getNewSpiResourceId(int direction, String remoteAddress, int returnSpi)
- throws Exception {
- when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
+ private int getNewSpiResourceId(String remoteAddress, int returnSpi) throws Exception {
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), anyString(), anyInt()))
.thenReturn(returnSpi);
IpSecSpiResponse spi =
mIpSecService.allocateSecurityParameterIndex(
- direction,
NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(),
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
new Binder());
@@ -221,20 +193,14 @@ public class IpSecServiceParameterizedTest {
}
private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception {
- config.setSpiResourceId(
- IpSecTransform.DIRECTION_OUT,
- getNewSpiResourceId(IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT));
- config.setSpiResourceId(
- IpSecTransform.DIRECTION_IN,
- getNewSpiResourceId(IpSecTransform.DIRECTION_IN, mRemoteAddr, TEST_SPI_IN));
- config.setRemoteAddress(mRemoteAddr);
+ config.setSpiResourceId(getNewSpiResourceId(mDestinationAddr, TEST_SPI));
+ config.setSourceAddress(mSourceAddr);
+ config.setDestinationAddress(mDestinationAddr);
}
private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception {
- for (int direction : DIRECTIONS) {
- config.setEncryption(direction, CRYPT_ALGO);
- config.setAuthentication(direction, AUTH_ALGO);
- }
+ config.setEncryption(CRYPT_ALGO);
+ config.setAuthentication(AUTH_ALGO);
}
@Test
@@ -251,32 +217,10 @@ public class IpSecServiceParameterizedTest {
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
anyLong(),
- eq(TEST_SPI_OUT),
- eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
- eq(AUTH_KEY),
- anyInt(),
- eq(IpSecAlgorithm.CRYPT_AES_CBC),
- eq(CRYPT_KEY),
- anyInt(),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- anyInt(),
- anyInt(),
- anyInt());
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- anyLong(),
- eq(TEST_SPI_IN),
+ eq(TEST_SPI),
eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
eq(AUTH_KEY),
anyInt(),
@@ -296,8 +240,7 @@ public class IpSecServiceParameterizedTest {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
- ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_OUT, AEAD_ALGO);
- ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
+ ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
@@ -307,32 +250,10 @@ public class IpSecServiceParameterizedTest {
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- anyLong(),
- eq(TEST_SPI_OUT),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(AEAD_KEY),
- anyInt(),
- anyInt(),
- anyInt(),
- anyInt());
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_IN),
anyString(),
anyString(),
anyLong(),
- eq(TEST_SPI_IN),
+ eq(TEST_SPI),
eq(""),
eq(new byte[] {}),
eq(0),
@@ -359,18 +280,7 @@ public class IpSecServiceParameterizedTest {
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(createTransformResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
@@ -404,18 +314,7 @@ public class IpSecServiceParameterizedTest {
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(createTransformResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
@@ -439,30 +338,22 @@ public class IpSecServiceParameterizedTest {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
int resourceId = createTransformResp.resourceId;
- mIpSecService.applyTransportModeTransform(pfd, resourceId);
+ mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
eq(resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
- eq(resourceId),
- eq(IpSecTransform.DIRECTION_IN),
+ eq(IpSecManager.DIRECTION_OUT),
anyString(),
anyString(),
- eq(TEST_SPI_IN));
+ eq(TEST_SPI));
}
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransform(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 0467989d8984..3eba881df427 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -105,9 +105,6 @@ public class IpSecServiceTest {
private static final IpSecAlgorithm AEAD_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
- private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
-
static {
try {
INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -303,83 +300,75 @@ public class IpSecServiceTest {
@Test
public void testValidateAlgorithmsAuth() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setAuthentication(direction, AUTH_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
-
- // Validate that incorrect algorithm types fails
- for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
- try {
- config = new IpSecConfig();
- config.setAuthentication(direction, algo);
- mIpSecService.validateAlgorithms(config, direction);
- fail("Did not throw exception on invalid algorithm type");
- } catch (IllegalArgumentException expected) {
- }
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthentication(AUTH_ALGO);
+ mIpSecService.validateAlgorithms(config);
+
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setAuthentication(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
}
}
}
@Test
public void testValidateAlgorithmsCrypt() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setEncryption(direction, CRYPT_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
-
- // Validate that incorrect algorithm types fails
- for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
- try {
- config = new IpSecConfig();
- config.setEncryption(direction, algo);
- mIpSecService.validateAlgorithms(config, direction);
- fail("Did not throw exception on invalid algorithm type");
- } catch (IllegalArgumentException expected) {
- }
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setEncryption(CRYPT_ALGO);
+ mIpSecService.validateAlgorithms(config);
+
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setEncryption(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
}
}
}
@Test
public void testValidateAlgorithmsAead() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(direction, AEAD_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
-
- // Validate that incorrect algorithm types fails
- for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
- try {
- config = new IpSecConfig();
- config.setAuthenticatedEncryption(direction, algo);
- mIpSecService.validateAlgorithms(config, direction);
- fail("Did not throw exception on invalid algorithm type");
- } catch (IllegalArgumentException expected) {
- }
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ mIpSecService.validateAlgorithms(config);
+
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setAuthenticatedEncryption(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
}
}
}
@Test
public void testValidateAlgorithmsAuthCrypt() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setAuthentication(direction, AUTH_ALGO);
- config.setEncryption(direction, CRYPT_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
- }
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthentication(AUTH_ALGO);
+ config.setEncryption(CRYPT_ALGO);
+ mIpSecService.validateAlgorithms(config);
}
@Test
public void testValidateAlgorithmsNoAlgorithms() {
IpSecConfig config = new IpSecConfig();
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; no algorithms specified");
} catch (IllegalArgumentException expected) {
}
@@ -388,10 +377,10 @@ public class IpSecServiceTest {
@Test
public void testValidateAlgorithmsAeadWithAuth() {
IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
- config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setAuthentication(AUTH_ALGO);
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; both AEAD and auth algorithm specified");
} catch (IllegalArgumentException expected) {
}
@@ -400,10 +389,10 @@ public class IpSecServiceTest {
@Test
public void testValidateAlgorithmsAeadWithCrypt() {
IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
- config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setEncryption(CRYPT_ALGO);
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; both AEAD and crypt algorithm specified");
} catch (IllegalArgumentException expected) {
}
@@ -412,11 +401,11 @@ public class IpSecServiceTest {
@Test
public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
- config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
- config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setAuthentication(AUTH_ALGO);
+ config.setEncryption(CRYPT_ALGO);
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; AEAD, auth and crypt algorithm specified");
} catch (IllegalArgumentException expected) {
}
@@ -434,7 +423,7 @@ public class IpSecServiceTest {
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransform(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
@@ -447,7 +436,7 @@ public class IpSecServiceTest {
try {
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
+ address, DROID_SPI, new Binder());
fail("Invalid address was passed through IpSecService validation: " + address);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
@@ -519,7 +508,6 @@ public class IpSecServiceTest {
// tracks the resource ID.
when(mMockNetd.ipSecAllocateSpi(
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
eq(InetAddress.getLoopbackAddress().getHostAddress()),
anyInt()))
@@ -528,7 +516,6 @@ public class IpSecServiceTest {
for (int i = 0; i < MAX_NUM_SPIS; i++) {
IpSecSpiResponse newSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + i,
new Binder());
@@ -544,7 +531,6 @@ public class IpSecServiceTest {
// Try to reserve one more SPI, and should fail.
IpSecSpiResponse extraSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
new Binder());
@@ -558,7 +544,6 @@ public class IpSecServiceTest {
// Should successfully reserve one more spi.
extraSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
new Binder());
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index b692ccf7e52d..f5f5b05491bb 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -45,8 +45,18 @@ ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember>
Result result = Result::kAdded;
auto iter = indexed_members_.find(member->GetName());
if (iter != indexed_members_.end()) {
- // Overwrite the entry.
- ordered_members_[iter->second].reset();
+ // Overwrite the entry. Be careful, as the key in indexed_members_ is actually memory owned
+ // by the value at ordered_members_[index]. Since overwriting a value for a key doesn't replace
+ // the key (the initial key inserted into the unordered_map is kept), we must erase and then
+ // insert a new key, whose memory is being kept around. We do all this to avoid using more
+ // memory for each key.
+ size_t index = iter->second;
+
+ // Erase the key + value from the map.
+ indexed_members_.erase(iter);
+
+ // Now clear the memory that was backing the key (now erased).
+ ordered_members_[index].reset();
result = Result::kOverridden;
}
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index c324238d3ecb..f4e10ab2e584 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -129,13 +129,16 @@ TEST(ManifestClassGeneratorTest, LastSeenPermissionWithSameLeafNameTakesPreceden
std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission android:name="android.permission.ACCESS_INTERNET" />
- <permission android:name="com.android.aapt.test.ACCESS_INTERNET" />
+ <permission android:name="com.android.sample.ACCESS_INTERNET" />
+ <permission android:name="com.android.permission.UNRELATED_PERMISSION" />
+ <permission android:name="com.android.aapt.test.ACCESS_INTERNET" /> -->
</manifest>)");
std::string actual;
ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
EXPECT_THAT(actual, HasSubstr("ACCESS_INTERNET=\"com.android.aapt.test.ACCESS_INTERNET\";"));
EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"android.permission.ACCESS_INTERNET\";")));
+ EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"com.android.sample.ACCESS_INTERNET\";")));
}
static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 80853b13a7cc..0e57f7ff0ed2 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -15,6 +15,7 @@
*/
#include "Collation.h"
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include <stdio.h>
#include <map>
@@ -137,6 +138,16 @@ java_type(const FieldDescriptor* field)
}
/**
+ * Gather the enums info.
+ */
+void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) {
+ for (int i = 0; i < enumDescriptor.value_count(); i++) {
+ atomField->enumValues[enumDescriptor.value(i)->number()] =
+ enumDescriptor.value(i)->name().c_str();
+ }
+}
+
+/**
* Gather the info about an atom proto.
*/
int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
@@ -221,11 +232,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
if (javaType == JAVA_TYPE_ENUM) {
// All enums are treated as ints when it comes to function signatures.
signature->push_back(JAVA_TYPE_INT);
- const EnumDescriptor *enumDescriptor = field->enum_type();
- for (int i = 0; i < enumDescriptor->value_count(); i++) {
- atField.enumValues[enumDescriptor->value(i)->number()] =
- enumDescriptor->value(i)->name().c_str();
- }
+ collate_enums(*field->enum_type(), &atField);
} else {
signature->push_back(javaType);
}
@@ -235,6 +242,53 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
return errorCount;
}
+// This function flattens the fields of the AttributionNode proto in an Atom proto and generates
+// the corresponding atom decl and signature.
+bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl,
+ vector<java_type_t> *signature) {
+ // Build a sorted list of the fields. Descriptor has them in source file
+ // order.
+ map<int, const FieldDescriptor *> fields;
+ for (int j = 0; j < atom->field_count(); j++) {
+ const FieldDescriptor *field = atom->field(j);
+ fields[field->number()] = field;
+ }
+
+ AtomDecl attributionDecl;
+ vector<java_type_t> attributionSignature;
+ collate_atom(android::os::statsd::AttributionNode::descriptor(),
+ &attributionDecl, &attributionSignature);
+
+ // Build the type signature and the atom data.
+ bool has_attribution_node = false;
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor *field = it->second;
+ java_type_t javaType = java_type(field);
+ if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ atomDecl->fields.insert(
+ atomDecl->fields.end(),
+ attributionDecl.fields.begin(), attributionDecl.fields.end());
+ signature->insert(
+ signature->end(),
+ attributionSignature.begin(), attributionSignature.end());
+ has_attribution_node = true;
+
+ } else {
+ AtomField atField(field->name(), javaType);
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ collate_enums(*field->enum_type(), &atField);
+ } else {
+ signature->push_back(javaType);
+ }
+ atomDecl->fields.push_back(atField);
+ }
+ }
+ return has_attribution_node;
+}
+
/**
* Gather the info about the atoms.
*/
@@ -266,6 +320,13 @@ int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
errorCount += collate_atom(atom, &atomDecl, &signature);
atoms->signatures.insert(signature);
atoms->decls.insert(atomDecl);
+
+ AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
+ vector<java_type_t> nonChainedSignature;
+ if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
+ atoms->non_chained_signatures.insert(nonChainedSignature);
+ atoms->non_chained_decls.insert(nonChainedAtomDecl);
+ }
}
if (dbg) {
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index cd0625c3a61d..0455eca62da8 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -32,6 +32,7 @@ using std::set;
using std::string;
using std::vector;
using google::protobuf::Descriptor;
+using google::protobuf::FieldDescriptor;
/**
* The types for atom parameters.
@@ -93,14 +94,15 @@ struct AtomDecl {
struct Atoms {
set<vector<java_type_t>> signatures;
set<AtomDecl> decls;
+ set<AtomDecl> non_chained_decls;
+ set<vector<java_type_t>> non_chained_signatures;
};
/**
* Gather the information about the atoms. Returns the number of errors.
*/
int collate_atoms(const Descriptor* descriptor, Atoms* atoms);
-int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
- vector<java_type_t> *signature);
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, vector<java_type_t> *signature);
} // namespace stats_log_api_gen
} // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index bbe6d63073c1..e0e6b5883e32 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -195,6 +195,47 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
fprintf(out, "\n");
}
+ for (set<vector<java_type_t>>::const_iterator signature = atoms.non_chained_signatures.begin();
+ signature != atoms.non_chained_signatures.end(); signature++) {
+ int argIndex;
+
+ fprintf(out, "void\n");
+ fprintf(out, "stats_write_non_chained(int32_t code");
+ argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ argIndex++;
+ }
+ fprintf(out, ")\n");
+
+ fprintf(out, "{\n");
+ argIndex = 1;
+ fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
+ fprintf(out, " event << code;\n\n");
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (argIndex == 1) {
+ fprintf(out, " event.begin();\n\n");
+ fprintf(out, " event.begin();\n");
+ }
+ if (*arg == JAVA_TYPE_STRING) {
+ fprintf(out, " if (arg%d == NULL) {\n", argIndex);
+ fprintf(out, " arg%d = \"\";\n", argIndex);
+ fprintf(out, " }\n");
+ }
+ fprintf(out, " event << arg%d;\n", argIndex);
+ if (argIndex == 2) {
+ fprintf(out, " event.end();\n\n");
+ fprintf(out, " event.end();\n\n");
+ }
+ argIndex++;
+ }
+
+ fprintf(out, " event.write(LOG_ID_STATS);\n");
+ fprintf(out, "}\n");
+ fprintf(out, "\n");
+ }
// Print footer
fprintf(out, "\n");
fprintf(out, "} // namespace util\n");
@@ -203,6 +244,68 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
return 0;
}
+void build_non_chained_decl_map(const Atoms& atoms,
+ std::map<int, set<AtomDecl>::const_iterator>* decl_map){
+ for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
+ atom != atoms.non_chained_decls.end(); atom++) {
+ decl_map->insert(std::make_pair(atom->code, atom));
+ }
+}
+
+static void write_cpp_usage(
+ FILE* out, const string& method_name, const string& atom_code_name,
+ const AtomDecl& atom, const AtomDecl &attributionDecl) {
+ fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
+ for (vector<AtomField>::const_iterator field = atom.fields.begin();
+ field != atom.fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+ }
+ }
+ fprintf(out, ");\n");
+}
+
+static void write_cpp_method_header(
+ FILE* out, const string& method_name, const set<vector<java_type_t>>& signatures,
+ const AtomDecl &attributionDecl) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
+ fprintf(out, "void %s(int32_t code ", method_name.c_str());
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType), chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ }
+ argIndex++;
+ }
+ fprintf(out, ");\n");
+
+ }
+}
static int
write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
@@ -228,6 +331,9 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio
fprintf(out, " */\n");
fprintf(out, "enum {\n");
+ std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
+ build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
+
size_t i = 0;
// Print constants
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
@@ -236,26 +342,13 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio
fprintf(out, "\n");
fprintf(out, " /**\n");
fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
- fprintf(out, " * Usage: stats_write(StatsLog.%s", constant.c_str());
- for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- if (chainField.javaType == JAVA_TYPE_STRING) {
- fprintf(out, ", const std::vector<%s>& %s",
- cpp_type_name(chainField.javaType),
- chainField.name.c_str());
- } else {
- fprintf(out, ", const %s* %s, size_t %s_length",
- cpp_type_name(chainField.javaType),
- chainField.name.c_str(), chainField.name.c_str());
- }
- }
- } else {
- fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
- }
+ write_cpp_usage(out, "stats_write", constant, *atom, attributionDecl);
+
+ auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
+ if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
+ write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
+ attributionDecl);
}
- fprintf(out, ");\n");
fprintf(out, " */\n");
char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
fprintf(out, " %s = %d%s\n", constant.c_str(), atom->code, comma);
@@ -274,38 +367,64 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio
fprintf(out, "//\n");
fprintf(out, "// Write methods\n");
fprintf(out, "//\n");
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
- fprintf(out, "void stats_write(int32_t code ");
+ write_cpp_method_header(out, "stats_write", atoms.signatures, attributionDecl);
+
+ fprintf(out, "//\n");
+ fprintf(out, "// Write flattened methods\n");
+ fprintf(out, "//\n");
+ write_cpp_method_header(out, "stats_write_non_chained", atoms.non_chained_signatures,
+ attributionDecl);
+
+ fprintf(out, "\n");
+ fprintf(out, "} // namespace util\n");
+ fprintf(out, "} // namespace android\n");
+
+ return 0;
+}
+
+static void write_java_usage(
+ FILE* out, const string& method_name, const string& atom_code_name,
+ const AtomDecl& atom, const AtomDecl &attributionDecl) {
+ fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s",
+ method_name.c_str(), atom_code_name.c_str());
+ for (vector<AtomField>::const_iterator field = atom.fields.begin();
+ field != atom.fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+ }
+ }
+ fprintf(out, ");\n");
+}
+
+static void write_java_method(
+ FILE* out, const string& method_name, const set<vector<java_type_t>>& signatures,
+ const AtomDecl &attributionDecl) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
+ fprintf(out, " public static native void %s(int code", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
for (auto chainField : attributionDecl.fields) {
- if (chainField.javaType == JAVA_TYPE_STRING) {
- fprintf(out, ", const std::vector<%s>& %s",
- cpp_type_name(chainField.javaType), chainField.name.c_str());
- } else {
- fprintf(out, ", const %s* %s, size_t %s_length",
- cpp_type_name(chainField.javaType),
- chainField.name.c_str(), chainField.name.c_str());
- }
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
}
} else {
- fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
}
argIndex++;
}
fprintf(out, ");\n");
}
-
- fprintf(out, "\n");
- fprintf(out, "} // namespace util\n");
- fprintf(out, "} // namespace android\n");
-
- return 0;
}
+
static int
write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
@@ -322,6 +441,9 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD
fprintf(out, "public class StatsLogInternal {\n");
fprintf(out, " // Constants for atom codes.\n");
+ std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
+ build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
+
// Print constants for the atom codes.
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
atom != atoms.decls.end(); atom++) {
@@ -329,19 +451,12 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD
fprintf(out, "\n");
fprintf(out, " /**\n");
fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
- fprintf(out, " * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
- for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s",
- java_type_name(chainField.javaType), chainField.name.c_str());
- }
- } else {
- fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
- }
+ write_java_usage(out, "write", constant, *atom, attributionDecl);
+ auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
+ if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
+ write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second,
+ attributionDecl);
}
- fprintf(out, ");\n");
fprintf(out, " */\n");
fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
}
@@ -371,24 +486,8 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD
// Print write methods
fprintf(out, " // Write methods\n");
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
- fprintf(out, " public static native void write(int code");
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s",
- java_type_name(chainField.javaType), chainField.name.c_str());
- }
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ");\n");
- }
+ write_java_method(out, "write", atoms.signatures, attributionDecl);
+ write_java_method(out, "write_non_chained", atoms.non_chained_signatures, attributionDecl);
fprintf(out, "}\n");
@@ -431,9 +530,9 @@ jni_array_type_name(java_type_t type)
}
static string
-jni_function_name(const vector<java_type_t>& signature)
+jni_function_name(const string& method_name, const vector<java_type_t>& signature)
{
- string result("StatsLog_write");
+ string result("StatsLog_" + method_name);
for (vector<java_type_t>::const_iterator arg = signature.begin();
arg != signature.end(); arg++) {
switch (*arg) {
@@ -509,34 +608,17 @@ jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &att
}
static int
-write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
+write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp_method_name,
+ const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl)
{
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
-
- fprintf(out, "#include <statslog.h>\n");
- fprintf(out, "\n");
- fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
- fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
- fprintf(out, "#include <utils/Vector.h>\n");
- fprintf(out, "#include \"core_jni_helpers.h\"\n");
- fprintf(out, "#include \"jni.h\"\n");
- fprintf(out, "\n");
- fprintf(out, "#define UNUSED __attribute__((__unused__))\n");
- fprintf(out, "\n");
-
- fprintf(out, "namespace android {\n");
- fprintf(out, "\n");
-
// Print write methods
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
int argIndex;
fprintf(out, "static void\n");
fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
- jni_function_name(*signature).c_str());
+ jni_function_name(java_method_name, *signature).c_str());
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -624,7 +706,7 @@ write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDe
// stats_write call
argIndex = 1;
- fprintf(out, " android::util::stats_write(code");
+ fprintf(out, " android::util::%s(code", cpp_method_name.c_str());
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -675,17 +757,53 @@ write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDe
fprintf(out, "\n");
}
+
+ return 0;
+}
+
+void write_jni_registration(FILE* out, const string& java_method_name,
+ const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
+ fprintf(out, " { \"%s\", \"%s\", (void*)%s },\n",
+ java_method_name.c_str(),
+ jni_function_signature(*signature, attributionDecl).c_str(),
+ jni_function_name(java_method_name, *signature).c_str());
+ }
+}
+
+static int
+write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
+{
+ // Print prelude
+ fprintf(out, "// This file is autogenerated\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "#include <statslog.h>\n");
+ fprintf(out, "\n");
+ fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+ fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
+ fprintf(out, "#include <utils/Vector.h>\n");
+ fprintf(out, "#include \"core_jni_helpers.h\"\n");
+ fprintf(out, "#include \"jni.h\"\n");
+ fprintf(out, "\n");
+ fprintf(out, "#define UNUSED __attribute__((__unused__))\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "namespace android {\n");
+ fprintf(out, "\n");
+
+ write_stats_log_jni(out, "write", "stats_write", atoms.signatures, attributionDecl);
+ write_stats_log_jni(out, "write_non_chained", "stats_write_non_chained",
+ atoms.non_chained_signatures, attributionDecl);
+
// Print registration function table
fprintf(out, "/*\n");
fprintf(out, " * JNI registration.\n");
fprintf(out, " */\n");
fprintf(out, "static const JNINativeMethod gRegisterMethods[] = {\n");
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
- fprintf(out, " { \"write\", \"%s\", (void*)%s },\n",
- jni_function_signature(*signature, attributionDecl).c_str(),
- jni_function_name(*signature).c_str());
- }
+ write_jni_registration(out, "write", atoms.signatures, attributionDecl);
+ write_jni_registration(out, "write_non_chained", atoms.non_chained_signatures, attributionDecl);
fprintf(out, "};\n");
fprintf(out, "\n");
@@ -699,11 +817,9 @@ write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDe
fprintf(out, "\n");
fprintf(out, "} // namespace android\n");
-
return 0;
}
-
static void
print_usage()
{