summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp24
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java13
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java10
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java4
-rw-r--r--api/current.txt71
-rw-r--r--api/system-current.txt18
-rw-r--r--cmds/content/src/com/android/commands/content/Content.java8
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/src/atoms.proto58
-rw-r--r--cmds/statsd/src/external/CarStatsPuller.cpp96
-rw-r--r--cmds/statsd/src/external/CarStatsPuller.h36
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp5
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java2
-rw-r--r--core/java/Android.bp5
-rw-r--r--core/java/android/app/ActivityManager.java4
-rw-r--r--core/java/android/app/AliasActivity.java3
-rw-r--r--core/java/android/app/ApplicationPackageManager.java39
-rw-r--r--core/java/android/app/LoadedApk.java3
-rw-r--r--core/java/android/app/SystemServiceRegistry.java18
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java23
-rw-r--r--core/java/android/companion/BluetoothDeviceFilterUtils.java2
-rw-r--r--core/java/android/content/ContentInterface.java13
-rw-r--r--core/java/android/content/ContentProvider.java193
-rw-r--r--core/java/android/content/ContentProviderClient.java35
-rw-r--r--core/java/android/content/ContentProviderNative.java45
-rw-r--r--core/java/android/content/ContentProviderOperation.java27
-rw-r--r--core/java/android/content/ContentResolver.java199
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/ContextWrapper.java3
-rw-r--r--core/java/android/content/IContentProvider.java20
-rw-r--r--core/java/android/content/LoggingContentInterface.java23
-rw-r--r--core/java/android/content/pm/UserInfo.java124
-rw-r--r--core/java/android/content/res/AssetManager.java12
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java2
-rw-r--r--core/java/android/net/StaticIpConfiguration.java9
-rw-r--r--core/java/android/net/util/MultinetworkPolicyTracker.java3
-rw-r--r--core/java/android/os/Binder.java17
-rwxr-xr-xcore/java/android/os/Build.java9
-rw-r--r--core/java/android/os/IDeviceIdentifiersPolicyService.aidl4
-rw-r--r--core/java/android/os/IUserManager.aidl85
-rw-r--r--core/java/android/os/PowerManager.java9
-rw-r--r--core/java/android/os/RecoverySystem.java2
-rw-r--r--core/java/android/os/RemoteException.java16
-rw-r--r--core/java/android/os/UserManager.java441
-rw-r--r--core/java/android/os/image/DynamicSystemManager.java30
-rw-r--r--core/java/android/os/image/IDynamicSystemService.aidl17
-rw-r--r--core/java/android/provider/Settings.java85
-rw-r--r--core/java/android/telephony/Rlog.java (renamed from telephony/java/android/telephony/Rlog.java)0
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java17
-rw-r--r--core/java/android/text/util/Linkify.java5
-rw-r--r--core/java/android/util/IconDrawableFactory.java25
-rw-r--r--core/java/android/util/LauncherIcons.java2
-rw-r--r--core/java/android/view/ViewConfiguration.java8
-rw-r--r--core/java/android/view/ViewDebug.java20
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java4
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java23
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java20
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java326
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java7
-rw-r--r--core/java/com/android/internal/app/LocaleStore.java2
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java32
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java2
-rw-r--r--core/java/com/android/internal/car/ICarStatsService.aidl31
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl25
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java2
-rw-r--r--core/java/com/android/internal/package-info.java4
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl10
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl10
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl12
-rw-r--r--core/java/com/android/internal/widget/GridLayoutManager.java1065
-rw-r--r--core/java/com/android/server/pm/UserTypeDetails.java388
-rw-r--r--core/java/com/android/server/pm/UserTypeFactory.java168
-rw-r--r--core/jni/android_util_AssetManager.cpp13
-rw-r--r--core/proto/android/server/activitymanagerservice.proto3
-rw-r--r--core/proto/android/server/powermanagerservice.proto8
-rw-r--r--core/proto/android/server/windowmanagerservice.proto8
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/layout/chooser_grid.xml5
-rw-r--r--core/res/res/values-television/config.xml4
-rw-r--r--core/res/res/values/config.xml8
-rw-r--r--core/res/res/values/strings.xml11
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java3
-rw-r--r--core/tests/coretests/src/android/os/BuildTest.java2
-rw-r--r--data/etc/services.core.protolog.json72
-rw-r--r--data/sounds/AudioPackage11.mk2
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java106
-rw-r--r--libs/androidfw/ApkAssets.cpp49
-rw-r--r--libs/androidfw/Idmap.cpp17
-rw-r--r--libs/androidfw/LoadedArsc.cpp41
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h21
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h9
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h57
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp9
-rw-r--r--location/java/android/location/GnssStatus.java277
-rw-r--r--location/java/android/location/GpsStatus.java116
-rw-r--r--location/java/android/location/LocationManager.java58
-rw-r--r--location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java19
-rw-r--r--media/java/android/media/ExifInterface.java118
-rw-r--r--media/java/android/media/tv/OWNER5
-rw-r--r--media/java/android/media/tv/OWNERS3
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java77
-rw-r--r--media/java/android/media/tv/tuner/TunerConstants.java8
-rw-r--r--media/jni/android_media_MediaDrm.h2
-rw-r--r--media/jni/android_media_tv_Tuner.cpp236
-rw-r--r--media/jni/android_media_tv_Tuner.h18
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java3
-rw-r--r--native/android/Android.bp5
-rw-r--r--native/android/choreographer.cpp225
-rw-r--r--packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml37
-rw-r--r--packages/CarSystemUI/res/layout/car_top_navigation_bar.xml2
-rw-r--r--packages/CarSystemUI/res/values/dimens.xml22
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java15
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java225
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java78
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyDialogBuilder.java59
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java62
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyType.java53
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java6
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml3
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java10
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java4
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/README.md6
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java60
-rw-r--r--packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml53
-rw-r--r--packages/SystemUI/res/layout/inattentive_sleep_warning.xml53
-rw-r--r--packages/SystemUI/res/values/config.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUI.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningController.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java500
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java472
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java737
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java)29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifListBuilder.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/SectionsProvider.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java209
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java190
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java1197
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java111
-rw-r--r--services/core/java/android/os/UserManagerInternal.java10
-rw-r--r--services/core/java/com/android/server/DynamicSystemService.java37
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java43
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java98
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java4
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java84
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java81
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java4
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java62
-rw-r--r--services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java6
-rw-r--r--services/core/java/com/android/server/location/NtpTimeHelper.java8
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java2
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java2
-rw-r--r--services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java7
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java498
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java108
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java253
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java39
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java43
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java646
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java9
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionsState.java153
-rw-r--r--services/core/java/com/android/server/policy/LegacyGlobalActions.java2
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java19
-rw-r--r--services/core/java/com/android/server/power/InattentiveSleepWarningController.java103
-rw-r--r--services/core/java/com/android/server/power/Notifier.java1
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java218
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java231
-rw-r--r--services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java1929
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java123
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java31
-rw-r--r--services/core/java/com/android/server/wm/DragResizeMode.java2
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java4
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java4
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java5
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java1
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java25
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java1816
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java14
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowFrames.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java56
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java65
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java25
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java14
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java23
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java160
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java79
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java192
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java142
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserTests.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java141
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java48
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java3
-rw-r--r--startop/apps/test/Android.bp5
-rw-r--r--startop/apps/test/AndroidManifest.xml8
-rw-r--r--startop/apps/test/src/ApplicationBenchmarks.java39
-rw-r--r--startop/apps/test/src/CPUIntensiveBenchmarkActivity.java2
-rw-r--r--startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java2
-rw-r--r--startop/apps/test/src/InteractiveMicrobenchmarkActivity.java (renamed from startop/apps/test/src/SystemServerBenchmarkActivity.java)27
-rw-r--r--startop/apps/test/src/NonInteractiveMicrobenchmarkActivity.java (renamed from startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java)2
-rw-r--r--startop/apps/test/src/SystemServerBenchmarks.java3
-rw-r--r--telecomm/java/android/telecom/Logging/Session.java21
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java13
-rw-r--r--telephony/common/com/android/internal/telephony/PackageChangeReceiver.java167
-rw-r--r--telephony/common/com/android/internal/telephony/SmsApplication.java10
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java3
-rw-r--r--telephony/java/android/telephony/DataSpecificRegistrationInfo.java3
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java3
-rw-r--r--telephony/java/android/telephony/ServiceState.java89
-rw-r--r--telephony/java/android/telephony/SmsManager.java7
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java1
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java77
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java158
-rw-r--r--telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl4
-rwxr-xr-xtelephony/java/com/android/internal/telephony/IOns.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl65
-rw-r--r--telephony/java/com/android/internal/telephony/ISms.aidl3
-rw-r--r--telephony/java/com/android/internal/telephony/ISmsImplBase.java5
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl39
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl136
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyPermissions.java88
-rw-r--r--test-mock/src/android/test/mock/MockContentProvider.java13
-rw-r--r--test-mock/src/android/test/mock/MockIContentProvider.java6
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java6
-rw-r--r--tests/net/java/com/android/server/connectivity/TetheringTest.java6
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsServiceTest.java4
-rw-r--r--tools/aapt2/Android.bp1
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists.py2
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists_test.py18
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java19
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java12
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java18
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java21
439 files changed, 14462 insertions, 6629 deletions
diff --git a/Android.bp b/Android.bp
index 5070b5ea2403..e7a3efcf398f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -916,28 +916,6 @@ python_binary_host {
],
}
-// TODO: Don't rely on this list by switching package.html into package-info.java
-frameworks_base_subdirs = [
- "core/java",
- "graphics/java",
- "location/java",
- "media/java",
- "media/mca/effect/java",
- "media/mca/filterfw/java",
- "media/mca/filterpacks/java",
- "drm/java",
- "mms/java",
- "opengl/java",
- "sax/java",
- "telecomm/java",
- "telephony/common",
- "telephony/java",
- "wifi/java",
- "lowpan/java",
- "keystore/java",
- "rs/java",
-]
-
// Make the api/current.txt file available for use by modules in other
// directories.
filegroup {
@@ -1042,7 +1020,6 @@ stubs_defaults {
"test-runner/src/**/*.java",
],
libs: framework_docs_only_libs,
- local_sourcepaths: frameworks_base_subdirs,
create_doc_stubs: true,
annotations_enabled: true,
api_levels_annotations_enabled: true,
@@ -1103,7 +1080,6 @@ stubs_defaults {
":updatable-media-srcs",
],
libs: ["framework-internal-utils"],
- local_sourcepaths: frameworks_base_subdirs,
installable: false,
annotations_enabled: true,
previous_api: ":last-released-public-api-for-metalava-annotations",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index e74e4a958eb9..278a78676a0b 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -516,7 +516,7 @@ public class UserLifecycleTests {
/** Creates a managed (work) profile under the current user, returning its userId. */
private int createManagedProfile() {
final UserInfo userInfo = mUm.createProfileForUser("TestProfile",
- UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
+ UserManager.USER_TYPE_PROFILE_MANAGED, /* flags */ 0, mAm.getCurrentUser());
if (userInfo == null) {
throw new IllegalStateException("Creating managed profile failed. Most likely there is "
+ "already a pre-existing profile on the device.");
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 4ee46f453bca..dfe7a90ba246 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1390,8 +1390,10 @@ public class DeviceIdleController extends SystemService
private static final int MSG_FINISH_IDLE_OP = 8;
private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
- private static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11;
- private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12;
+ @VisibleForTesting
+ static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11;
+ @VisibleForTesting
+ static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12;
final class MyHandler extends Handler {
MyHandler(Looper looper) {
@@ -3327,8 +3329,7 @@ public class DeviceIdleController extends SystemService
mHandler.sendEmptyMessage(MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR);
}
- @VisibleForTesting
- void updatePreIdleFactor() {
+ private void updatePreIdleFactor() {
synchronized (this) {
if (!shouldUseIdleTimeoutFactorLocked()) {
return;
@@ -3350,8 +3351,7 @@ public class DeviceIdleController extends SystemService
}
}
- @VisibleForTesting
- void maybeDoImmediateMaintenance() {
+ private void maybeDoImmediateMaintenance() {
synchronized (this) {
if (mState == STATE_IDLE) {
long duration = SystemClock.elapsedRealtime() - mIdleStartTime;
@@ -3377,6 +3377,7 @@ public class DeviceIdleController extends SystemService
void setIdleStartTimeForTest(long idleStartTime) {
synchronized (this) {
mIdleStartTime = idleStartTime;
+ maybeDoImmediateMaintenance();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 593e49490e94..0a4e020e07cd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -57,6 +57,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -2688,13 +2689,14 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
- protected int handleShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- @NonNull FileDescriptor err, @NonNull String[] args) {
+ protected int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
- this, in, out, err, args);
+ this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+ args);
}
-
/**
* <b>For internal system user only!</b>
* Returns a list of all currently-executing jobs.
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 5cb9834de130..8a00c8318d58 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -1179,7 +1179,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
long token = Binder.clearCallingIdentity();
synchronized (this) {
if (mTelephony == null) {
- mTelephony = TelephonyManager.from(mContext);
+ mTelephony = mContext.getSystemService(TelephonyManager.class);
}
}
if (mTelephony != null) {
@@ -1802,7 +1802,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
e.writeString(Build.BRAND);
e.writeString(Build.PRODUCT);
e.writeString(Build.DEVICE);
- e.writeString(Build.VERSION.RELEASE_OR_CODENAME);
+ e.writeString(Build.VERSION.RELEASE);
e.writeString(Build.ID);
e.writeString(Build.VERSION.INCREMENTAL);
e.writeString(Build.TYPE);
diff --git a/api/current.txt b/api/current.txt
index 0ddc42a38122..61013e81c473 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4271,8 +4271,8 @@ package android.app {
method public android.app.AlertDialog show();
}
- public class AliasActivity extends android.app.Activity {
- ctor public AliasActivity();
+ @Deprecated public class AliasActivity extends android.app.Activity {
+ ctor @Deprecated public AliasActivity();
}
public class AppComponentFactory {
@@ -8445,6 +8445,7 @@ package android.bluetooth {
method public int describeContents();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp();
method public String getAddress();
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public String getAlias();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothClass getBluetoothClass();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getBondState();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public String getName();
@@ -8456,6 +8457,7 @@ package android.bluetooth {
field public static final String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
field public static final String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
field public static final String ACTION_ACL_DISCONNECT_REQUESTED = "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
+ field public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.action.ALIAS_CHANGED";
field public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED";
field public static final String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED";
field public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND";
@@ -9475,6 +9477,7 @@ package android.content {
method @Nullable public android.net.Uri canonicalize(@NonNull android.net.Uri);
method @NonNull public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
+ method public int delete(@NonNull android.net.Uri, @Nullable android.os.Bundle);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method @Nullable public final String getCallingFeatureId();
method @Nullable public final String getCallingPackage();
@@ -9485,6 +9488,7 @@ package android.content {
method @Nullable public abstract String getType(@NonNull android.net.Uri);
method @Nullable public final String getWritePermission();
method @Nullable public abstract android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues);
+ method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle);
method protected boolean isTemporary();
method public void onConfigurationChanged(android.content.res.Configuration);
method public abstract boolean onCreate();
@@ -9510,6 +9514,7 @@ package android.content {
method public void shutdown();
method @Nullable public android.net.Uri uncanonicalize(@NonNull android.net.Uri);
method public abstract int update(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]);
+ method public int update(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle);
}
public final class ContentProvider.CallingIdentity {
@@ -9528,10 +9533,12 @@ package android.content {
method @Nullable public final android.net.Uri canonicalize(@NonNull android.net.Uri) throws android.os.RemoteException;
method public void close();
method public int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]) throws android.os.RemoteException;
+ method public int delete(@NonNull android.net.Uri, @Nullable android.os.Bundle) throws android.os.RemoteException;
method @Nullable public android.content.ContentProvider getLocalContentProvider();
method @Nullable public String[] getStreamTypes(@NonNull android.net.Uri, @NonNull String) throws android.os.RemoteException;
method @Nullable public String getType(@NonNull android.net.Uri) throws android.os.RemoteException;
method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues) throws android.os.RemoteException;
+ method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle) throws android.os.RemoteException;
method @Nullable public android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException, android.os.RemoteException;
method @Nullable public android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method @Nullable public android.os.ParcelFileDescriptor openFile(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException, android.os.RemoteException;
@@ -9546,6 +9553,7 @@ package android.content {
method @Deprecated public boolean release();
method @Nullable public final android.net.Uri uncanonicalize(@NonNull android.net.Uri) throws android.os.RemoteException;
method public int update(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]) throws android.os.RemoteException;
+ method public int update(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle) throws android.os.RemoteException;
}
public class ContentProviderOperation implements android.os.Parcelable {
@@ -9633,6 +9641,7 @@ package android.content {
method public static void cancelSync(android.content.SyncRequest);
method @Nullable public final android.net.Uri canonicalize(@NonNull android.net.Uri);
method public final int delete(@NonNull @RequiresPermission.Write android.net.Uri, @Nullable String, @Nullable String[]);
+ method public final int delete(@NonNull @RequiresPermission.Write android.net.Uri, @Nullable android.os.Bundle);
method @Deprecated public static android.content.SyncInfo getCurrentSync();
method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
method public static int getIsSyncable(android.accounts.Account, String);
@@ -9646,6 +9655,7 @@ package android.content {
method @Nullable public final String getType(@NonNull android.net.Uri);
method @NonNull public final android.content.ContentResolver.MimeTypeInfo getTypeInfo(@NonNull String);
method @Nullable public final android.net.Uri insert(@NonNull @RequiresPermission.Write android.net.Uri, @Nullable android.content.ContentValues);
+ method @Nullable public final android.net.Uri insert(@NonNull @RequiresPermission.Write android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle);
method public static boolean isSyncActive(android.accounts.Account, String);
method public static boolean isSyncPending(android.accounts.Account, String);
method @NonNull public android.graphics.Bitmap loadThumbnail(@NonNull android.net.Uri, @NonNull android.util.Size, @Nullable android.os.CancellationSignal) throws java.io.IOException;
@@ -9683,6 +9693,7 @@ package android.content {
method @Nullable public final android.net.Uri uncanonicalize(@NonNull android.net.Uri);
method public final void unregisterContentObserver(@NonNull android.database.ContentObserver);
method public final int update(@NonNull @RequiresPermission.Write android.net.Uri, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]);
+ method public final int update(@NonNull @RequiresPermission.Write android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle);
method public static void validateSyncExtrasBundle(android.os.Bundle);
method @NonNull public static android.content.ContentResolver wrap(@NonNull android.content.ContentProvider);
method @NonNull public static android.content.ContentResolver wrap(@NonNull android.content.ContentProviderClient);
@@ -9695,12 +9706,16 @@ package android.content {
field public static final String EXTRA_TOTAL_COUNT = "android.content.extra.TOTAL_COUNT";
field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
+ field public static final String QUERY_ARG_GROUP_COLUMNS = "android:query-arg-group-columns";
field public static final String QUERY_ARG_LIMIT = "android:query-arg-limit";
field public static final String QUERY_ARG_OFFSET = "android:query-arg-offset";
field public static final String QUERY_ARG_SORT_COLLATION = "android:query-arg-sort-collation";
field public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
field public static final String QUERY_ARG_SORT_DIRECTION = "android:query-arg-sort-direction";
field public static final String QUERY_ARG_SORT_LOCALE = "android:query-arg-sort-locale";
+ field public static final String QUERY_ARG_SQL_GROUP_BY = "android:query-arg-sql-group-by";
+ field public static final String QUERY_ARG_SQL_HAVING = "android:query-arg-sql-having";
+ field public static final String QUERY_ARG_SQL_LIMIT = "android:query-arg-sql-limit";
field public static final String QUERY_ARG_SQL_SELECTION = "android:query-arg-sql-selection";
field public static final String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-arg-sql-selection-args";
field public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
@@ -15713,6 +15728,8 @@ package android.graphics.drawable {
public final class Icon implements android.os.Parcelable {
method public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap);
+ method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmapContentUri(@NonNull String);
+ method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmapContentUri(@NonNull android.net.Uri);
method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
method public static android.graphics.drawable.Icon createWithContentUri(String);
method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
@@ -15739,6 +15756,7 @@ package android.graphics.drawable {
field public static final int TYPE_DATA = 3; // 0x3
field public static final int TYPE_RESOURCE = 2; // 0x2
field public static final int TYPE_URI = 4; // 0x4
+ field public static final int TYPE_URI_ADAPTIVE_BITMAP = 6; // 0x6
}
public static interface Icon.OnDrawableLoadedListener {
@@ -23039,17 +23057,17 @@ package android.location {
}
public final class GnssStatus {
- method public float getAzimuthDegrees(int);
- method public float getCarrierFrequencyHz(int);
- method public float getCn0DbHz(int);
- method public int getConstellationType(int);
- method public float getElevationDegrees(int);
- method public int getSatelliteCount();
- method public int getSvid(int);
- method public boolean hasAlmanacData(int);
- method public boolean hasCarrierFrequencyHz(int);
- method public boolean hasEphemerisData(int);
- method public boolean usedInFix(int);
+ method @FloatRange(from=0, to=360) public float getAzimuthDegrees(@IntRange(from=0) int);
+ method @FloatRange(from=0) public float getCarrierFrequencyHz(@IntRange(from=0) int);
+ method @FloatRange(from=0, to=63) public float getCn0DbHz(@IntRange(from=0) int);
+ method public int getConstellationType(@IntRange(from=0) int);
+ method @FloatRange(from=0xffffffa6, to=90) public float getElevationDegrees(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getSatelliteCount();
+ method @IntRange(from=1, to=200) public int getSvid(@IntRange(from=0) int);
+ method public boolean hasAlmanacData(@IntRange(from=0) int);
+ method public boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
+ method public boolean hasEphemerisData(@IntRange(from=0) int);
+ method public boolean usedInFix(@IntRange(from=0) int);
field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
field public static final int CONSTELLATION_GALILEO = 6; // 0x6
field public static final int CONSTELLATION_GLONASS = 3; // 0x3
@@ -23060,10 +23078,17 @@ package android.location {
field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
}
+ public static final class GnssStatus.Builder {
+ ctor public GnssStatus.Builder();
+ method @NonNull public android.location.GnssStatus.Builder addSatellite(int, @IntRange(from=1, to=200) int, @FloatRange(from=0, to=63) float, @FloatRange(from=0xffffffa6, to=90) float, @FloatRange(from=0, to=360) float, boolean, boolean, boolean, boolean, @FloatRange(from=0) float);
+ method @NonNull public android.location.GnssStatus build();
+ method @NonNull public android.location.GnssStatus.Builder clearSatellites();
+ }
+
public abstract static class GnssStatus.Callback {
ctor public GnssStatus.Callback();
method public void onFirstFix(int);
- method public void onSatelliteStatusChanged(android.location.GnssStatus);
+ method public void onSatelliteStatusChanged(@NonNull android.location.GnssStatus);
method public void onStarted();
method public void onStopped();
}
@@ -23079,6 +23104,7 @@ package android.location {
}
@Deprecated public final class GpsStatus {
+ method @Deprecated @NonNull public static android.location.GpsStatus create(@NonNull android.location.GnssStatus, int);
method @Deprecated public int getMaxSatellites();
method @Deprecated public Iterable<android.location.GpsSatellite> getSatellites();
method @Deprecated public int getTimeToFirstFix();
@@ -24042,7 +24068,7 @@ package android.media {
ctor public ExifInterface(@NonNull String) throws java.io.IOException;
ctor public ExifInterface(@NonNull java.io.FileDescriptor) throws java.io.IOException;
ctor public ExifInterface(@NonNull java.io.InputStream) throws java.io.IOException;
- method @NonNull public static android.media.ExifInterface fromStandalone(@NonNull java.io.InputStream) throws java.io.IOException;
+ ctor public ExifInterface(@NonNull java.io.InputStream, int) throws java.io.IOException;
method public double getAltitude(double);
method @Nullable public String getAttribute(@NonNull String);
method @Nullable public byte[] getAttributeBytes(@NonNull String);
@@ -24069,6 +24095,8 @@ package android.media {
field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+ field public static final int STREAM_TYPE_EXIF_DATA_ONLY = 1; // 0x1
+ field public static final int STREAM_TYPE_FULL_IMAGE_DATA = 0; // 0x0
field @Deprecated public static final String TAG_APERTURE = "FNumber";
field public static final String TAG_APERTURE_VALUE = "ApertureValue";
field public static final String TAG_ARTIST = "Artist";
@@ -30046,6 +30074,7 @@ package android.net.wifi {
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
method public int getWifiState();
method public boolean is5GHzBandSupported();
+ method public boolean is6GHzBandSupported();
method @Deprecated public boolean isDeviceToApRttSupported();
method public boolean isEasyConnectSupported();
method public boolean isEnhancedOpenSupported();
@@ -30360,6 +30389,7 @@ package android.net.wifi.aware {
public static final class WifiAwareNetworkSpecifier.Builder {
ctor public WifiAwareNetworkSpecifier.Builder(@NonNull android.net.wifi.aware.DiscoverySession, @NonNull android.net.wifi.aware.PeerHandle);
method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier build();
+ method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPmk(@NonNull byte[]);
method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPort(@IntRange(from=0, to=65535) int);
method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPskPassphrase(@NonNull String);
method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setTransportProtocol(@IntRange(from=0, to=255) int);
@@ -34393,7 +34423,6 @@ package android.os {
field public static final String INCREMENTAL;
field public static final int PREVIEW_SDK_INT;
field public static final String RELEASE;
- field @NonNull public static final String RELEASE_OR_CODENAME;
field @Deprecated public static final String SDK;
field public static final int SDK_INT;
field public static final String SECURITY_PATCH;
@@ -35360,6 +35389,8 @@ package android.os {
public class RemoteException extends android.util.AndroidException {
ctor public RemoteException();
ctor public RemoteException(String);
+ method @NonNull public RuntimeException rethrowAsRuntimeException();
+ method @NonNull public RuntimeException rethrowFromSystemServer();
}
public class ResultReceiver implements android.os.Parcelable {
@@ -38993,10 +39024,14 @@ package android.provider {
field public static final String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
field public static final String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final String ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS";
+ field public static final String ACTION_WIFI_ADD_NETWORKS = "android.settings.WIFI_ADD_NETWORKS";
field public static final String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
field public static final String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
field public static final String ACTION_ZEN_MODE_PRIORITY_SETTINGS = "android.settings.ZEN_MODE_PRIORITY_SETTINGS";
+ field public static final int ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED = 1; // 0x1
+ field public static final int ADD_WIFI_RESULT_ALREADY_EXISTS = 2; // 0x2
+ field public static final int ADD_WIFI_RESULT_SUCCESS = 0; // 0x0
field public static final String AUTHORITY = "settings";
field public static final String EXTRA_ACCOUNT_TYPES = "account_types";
field public static final String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled";
@@ -39009,6 +39044,8 @@ package android.provider {
field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
+ field public static final String EXTRA_WIFI_CONFIGURATION_LIST = "android.provider.extra.WIFI_CONFIGURATION_LIST";
+ field public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST = "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
}
@@ -45229,6 +45266,8 @@ package android.telephony {
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
+ method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList();
+ method public static int getActiveDataSubscriptionId();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount();
method public int getActiveSubscriptionInfoCountMax();
diff --git a/api/system-current.txt b/api/system-current.txt
index c68d083070cb..d91ecd49afdb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1306,6 +1306,7 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
@@ -1414,6 +1415,7 @@ package android.content {
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.content.Intent registerReceiverForAllUsers(@Nullable android.content.BroadcastReceiver, @NonNull android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler);
method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
+ method public void sendBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[]);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
@@ -4847,7 +4849,7 @@ package android.net.wifi {
method public boolean isPortableHotspotSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
- method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback, @Nullable java.util.concurrent.Executor);
+ method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
@@ -5093,10 +5095,6 @@ package android.net.wifi.aware {
method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]);
}
- public static final class WifiAwareNetworkSpecifier.Builder {
- method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPmk(@NonNull byte[]);
- }
-
public class WifiAwareSession implements java.lang.AutoCloseable {
method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]);
}
@@ -7969,6 +7967,7 @@ package android.telephony {
public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
+ method public boolean isUsingCarrierAggregation();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
}
@@ -8107,6 +8106,7 @@ package android.telephony {
method @Nullable public android.telephony.CellIdentity getCellIdentity();
method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
method public int getDomain();
+ method public int getNrState();
method public int getRegistrationState();
method public int getRejectCause();
method public int getRoamingType();
@@ -8349,6 +8349,9 @@ package android.telephony {
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList();
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
+ method public int getNrFrequencyRange();
+ method @Nullable public String getOperatorAlphaLongRaw();
+ method @Nullable public String getOperatorAlphaShortRaw();
field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2
field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3
field public static final int ROAMING_TYPE_NOT_ROAMING = 0; // 0x0
@@ -8485,6 +8488,7 @@ package android.telephony {
public class SubscriptionInfo implements android.os.Parcelable {
method @Nullable public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
method public int getProfileClass();
+ method public boolean isGroupDisabled();
}
public class SubscriptionManager {
@@ -8567,6 +8571,7 @@ package android.telephony {
method @Deprecated public boolean getDataEnabled();
method @Deprecated public boolean getDataEnabled(int);
method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+ method @NonNull public static String getDefaultSimCountryIso();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
@@ -8598,7 +8603,9 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
+ method public boolean isModemEnabledForSlot(int);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
@@ -8619,6 +8626,7 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean);
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 7e278e964ab5..59544a971e5f 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -508,7 +508,7 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(resolveCallingPackage(), null, mUri, mContentValues);
+ provider.insert(resolveCallingPackage(), null, mUri, mContentValues, null);
}
}
@@ -522,7 +522,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(resolveCallingPackage(), null, mUri, mWhere, null);
+ provider.delete(resolveCallingPackage(), null, mUri,
+ ContentResolver.createSqlQueryBundle(mWhere, null));
}
}
@@ -679,7 +680,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.update(resolveCallingPackage(), null, mUri, mContentValues, mWhere, null);
+ provider.update(resolveCallingPackage(), null, mUri, mContentValues,
+ ContentResolver.createSqlQueryBundle(mWhere, null));
}
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 8af925aa7a63..cf286e662630 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -50,6 +50,7 @@ cc_defaults {
srcs: [
":statsd_aidl",
+ ":ICarStatsService.aidl",
"src/active_config_list.proto",
"src/anomaly/AlarmMonitor.cpp",
"src/anomaly/AlarmTracker.cpp",
@@ -64,6 +65,7 @@ cc_defaults {
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
+ "src/external/CarStatsPuller.cpp",
"src/external/GpuStatsPuller.cpp",
"src/external/Perfetto.cpp",
"src/external/PowerStatsPuller.cpp",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index c196c61a2b55..b0570fd83bbf 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -339,6 +339,7 @@ message Atom {
228 [(allow_from_any_uid) = true];
PerfettoUploaded perfetto_uploaded =
229 [(log_from_module) = "perfetto"];
+ VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
}
// Pulled events will start at field 10000.
@@ -408,6 +409,7 @@ message Atom {
SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
ProcessMemorySnapshot process_memory_snapshot = 10064;
+ VmsClientStats vms_client_stats = 10065;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3732,6 +3734,33 @@ message RoleRequestResultReported {
optional Result result = 9;
}
+/**
+ * Logs when a Vehicle Maps Service client's connection state has changed
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/stats/VmsClientLog.java
+ */
+message VmsClientConnectionStateChanged {
+ // The UID of the VMS client app
+ optional int32 uid = 1 [(is_uid) = true];
+
+ enum State {
+ UNKNOWN = 0;
+ // Attempting to connect to the client
+ CONNECTING = 1;
+ // Client connection established
+ CONNECTED = 2;
+ // Client connection closed unexpectedly
+ DISCONNECTED = 3;
+ // Client connection closed by VMS
+ TERMINATED = 4;
+ // Error establishing the client connection
+ CONNECTION_ERROR = 5;
+ }
+
+ optional State state = 2;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -7275,3 +7304,32 @@ message PerfettoUploaded {
optional int64 trace_uuid_lsb = 2;
optional int64 trace_uuid_msb = 3;
}
+
+/**
+ * Pulls client metrics on data transferred via Vehicle Maps Service.
+ * Metrics are keyed by uid + layer.
+ *
+ * Pulled from:
+ * packages/services/Car/service/src/com/android/car/stats/CarStatsService.java
+ */
+message VmsClientStats {
+ // UID of the VMS client app
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // VMS layer definition
+ optional int32 layer_type = 2;
+ optional int32 layer_channel = 3;
+ optional int32 layer_version = 4;
+
+ // Bytes and packets sent by the client for the layer
+ optional int64 tx_bytes = 5;
+ optional int64 tx_packets = 6;
+
+ // Bytes and packets received by the client for the layer
+ optional int64 rx_bytes = 7;
+ optional int64 rx_packets = 8;
+
+ // Bytes and packets dropped due to client error
+ optional int64 dropped_bytes = 9;
+ optional int64 dropped_packets = 10;
+}
diff --git a/cmds/statsd/src/external/CarStatsPuller.cpp b/cmds/statsd/src/external/CarStatsPuller.cpp
new file mode 100644
index 000000000000..70c0456b5eb4
--- /dev/null
+++ b/cmds/statsd/src/external/CarStatsPuller.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false
+#include "Log.h"
+
+#include <binder/IServiceManager.h>
+#include <com/android/internal/car/ICarStatsService.h>
+
+#include "CarStatsPuller.h"
+#include "logd/LogEvent.h"
+#include "stats_log_util.h"
+
+using android::binder::Status;
+using com::android::internal::car::ICarStatsService;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static std::mutex gCarStatsMutex;
+static sp<ICarStatsService> gCarStats = nullptr;
+
+class CarStatsDeathRecipient : public android::IBinder::DeathRecipient {
+ public:
+ CarStatsDeathRecipient() = default;
+ ~CarStatsDeathRecipient() override = default;
+
+ // android::IBinder::DeathRecipient override:
+ void binderDied(const android::wp<android::IBinder>& /* who */) override {
+ ALOGE("Car service has died");
+ std::lock_guard<std::mutex> lock(gCarStatsMutex);
+ if (gCarStats) {
+ sp<IBinder> binder = IInterface::asBinder(gCarStats);
+ binder->unlinkToDeath(this);
+ gCarStats = nullptr;
+ }
+ }
+};
+
+static sp<CarStatsDeathRecipient> gDeathRecipient = new CarStatsDeathRecipient();
+
+static sp<ICarStatsService> getCarService() {
+ std::lock_guard<std::mutex> lock(gCarStatsMutex);
+ if (!gCarStats) {
+ const sp<IBinder> binder = defaultServiceManager()->checkService(String16("car_stats"));
+ if (!binder) {
+ ALOGW("Car service is unavailable");
+ return nullptr;
+ }
+ gCarStats = interface_cast<ICarStatsService>(binder);
+ binder->linkToDeath(gDeathRecipient);
+ }
+ return gCarStats;
+}
+
+CarStatsPuller::CarStatsPuller(const int tagId) : StatsPuller(tagId) {
+}
+
+bool CarStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
+ const sp<ICarStatsService> carService = getCarService();
+ if (!carService) {
+ return false;
+ }
+
+ vector<StatsLogEventWrapper> returned_value;
+ Status status = carService->pullData(mTagId, &returned_value);
+ if (!status.isOk()) {
+ ALOGW("CarStatsPuller::pull failed for %d", mTagId);
+ return false;
+ }
+
+ data->clear();
+ for (const StatsLogEventWrapper& it : returned_value) {
+ LogEvent::createLogEvents(it, *data);
+ }
+ VLOG("CarStatsPuller::pull succeeded for %d", mTagId);
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CarStatsPuller.h b/cmds/statsd/src/external/CarStatsPuller.h
new file mode 100644
index 000000000000..ca0f1a9c9a17
--- /dev/null
+++ b/cmds/statsd/src/external/CarStatsPuller.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Pull atoms from CarService.
+ */
+class CarStatsPuller : public StatsPuller {
+public:
+ explicit CarStatsPuller(const int tagId);
+ bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 5a76d1f9c80d..535fcfbfc454 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -32,6 +32,7 @@
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
+#include "CarStatsPuller.h"
#include "GpuStatsPuller.h"
#include "PowerStatsPuller.h"
#include "ResourceHealthManagerPuller.h"
@@ -272,6 +273,10 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
{android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
{.puller =
new SurfaceflingerStatsPuller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO)}},
+ // VmsClientStats
+ {android::util::VMS_CLIENT_STATS,
+ {.additiveFields = {5, 6, 7, 8, 9, 10},
+ .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index db0bca0f2344..a4b30587469f 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -376,7 +376,7 @@ public final class Telecom extends BaseCommand {
private void runGetMaxPhones() throws RemoteException {
// This assumes the max number of SIMs is 2, which it currently is
if (TelephonyManager.MULTISIM_ALLOWED
- == mTelephonyService.isMultiSimSupported("com.android.commands.telecom")) {
+ == mTelephonyService.isMultiSimSupported("com.android.commands.telecom", null)) {
System.out.println("2");
} else {
System.out.println("1");
diff --git a/core/java/Android.bp b/core/java/Android.bp
index fb27f74211fb..9a8e130436f8 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,3 +7,8 @@ filegroup {
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
}
+
+filegroup {
+ name: "ICarStatsService.aidl",
+ srcs: ["com/android/internal/car/ICarStatsService.aidl"],
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 6d63fd0936dd..590b3db5fe84 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -70,6 +70,7 @@ import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Singleton;
import android.util.Size;
+import android.view.IWindowContainer;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.procstats.ProcessStats;
@@ -2526,6 +2527,7 @@ public class ActivityManager {
// Index of the stack in the display's stack list, can be used for comparison of stack order
@UnsupportedAppUsage
public int position;
+ public IWindowContainer stackToken;
/**
* The full configuration the stack is currently running in.
* @hide
@@ -2559,6 +2561,7 @@ public class ActivityManager {
dest.writeInt(userId);
dest.writeInt(visible ? 1 : 0);
dest.writeInt(position);
+ dest.writeStrongInterface(stackToken);
if (topActivity != null) {
dest.writeInt(1);
topActivity.writeToParcel(dest, 0);
@@ -2590,6 +2593,7 @@ public class ActivityManager {
userId = source.readInt();
visible = source.readInt() > 0;
position = source.readInt();
+ stackToken = IWindowContainer.Stub.asInterface(source.readStrongBinder());
if (source.readInt() > 0) {
topActivity = ComponentName.readFromParcel(source);
}
diff --git a/core/java/android/app/AliasActivity.java b/core/java/android/app/AliasActivity.java
index 37565298c8cb..37be90160d9a 100644
--- a/core/java/android/app/AliasActivity.java
+++ b/core/java/android/app/AliasActivity.java
@@ -39,7 +39,10 @@ import java.io.IOException;
* To use this activity, you should include in the manifest for the associated
* component an entry named "android.app.alias". It is a reference to an XML
* resource describing an intent that launches the real application.
+ *
+ * @deprecated Use {@code <activity-alias>} or subclass Activity directly.
*/
+@Deprecated
public class AliasActivity extends Activity {
/**
* This is the name under which you should store in your component the
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 566134733b87..0113f6912183 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -92,7 +92,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
-import android.util.IconDrawableFactory;
import android.util.LauncherIcons;
import android.util.Log;
import android.view.Display;
@@ -1474,11 +1473,11 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) {
- if (!isManagedProfile(user.getIdentifier())) {
+ if (!hasUserBadge(user.getIdentifier())) {
return icon;
}
Drawable badge = new LauncherIcons(mContext).getBadgeDrawable(
- com.android.internal.R.drawable.ic_corp_icon_badge_case,
+ getUserManager().getUserIconBadgeResId(user.getIdentifier()),
getUserBadgeColor(user));
return getBadgedDrawable(icon, badge, null, true);
}
@@ -1493,26 +1492,21 @@ public class ApplicationPackageManager extends PackageManager {
return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true);
}
- @VisibleForTesting
- public static final int[] CORP_BADGE_LABEL_RES_ID = new int[] {
- com.android.internal.R.string.managed_profile_label_badge,
- com.android.internal.R.string.managed_profile_label_badge_2,
- com.android.internal.R.string.managed_profile_label_badge_3
- };
-
+ /** Returns the color of the user's actual badge (not the badge's shadow). */
private int getUserBadgeColor(UserHandle user) {
- return IconDrawableFactory.getUserBadgeColor(getUserManager(), user.getIdentifier());
+ return getUserManager().getUserBadgeColor(user.getIdentifier());
}
@Override
public Drawable getUserBadgeForDensity(UserHandle user, int density) {
- Drawable badgeColor = getManagedProfileIconForDensity(user,
+ // This is part of the shadow, not the main color, and is not actually corp-specific.
+ Drawable badgeColor = getProfileIconForDensity(user,
com.android.internal.R.drawable.ic_corp_badge_color, density);
if (badgeColor == null) {
return null;
}
Drawable badgeForeground = getDrawableForDensity(
- com.android.internal.R.drawable.ic_corp_badge_case, density);
+ getUserManager().getUserBadgeResId(user.getIdentifier()), density);
badgeForeground.setTint(getUserBadgeColor(user));
Drawable badge = new LayerDrawable(new Drawable[] {badgeColor, badgeForeground });
return badge;
@@ -1520,8 +1514,8 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public Drawable getUserBadgeForDensityNoBackground(UserHandle user, int density) {
- Drawable badge = getManagedProfileIconForDensity(user,
- com.android.internal.R.drawable.ic_corp_badge_no_background, density);
+ Drawable badge = getProfileIconForDensity(user,
+ getUserManager().getUserBadgeNoBackgroundResId(user.getIdentifier()), density);
if (badge != null) {
badge.setTint(getUserBadgeColor(user));
}
@@ -1535,8 +1529,8 @@ public class ApplicationPackageManager extends PackageManager {
return mContext.getResources().getDrawableForDensity(drawableId, density);
}
- private Drawable getManagedProfileIconForDensity(UserHandle user, int drawableId, int density) {
- if (isManagedProfile(user.getIdentifier())) {
+ private Drawable getProfileIconForDensity(UserHandle user, int drawableId, int density) {
+ if (hasUserBadge(user.getIdentifier())) {
return getDrawableForDensity(drawableId, density);
}
return null;
@@ -1544,12 +1538,7 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
- if (isManagedProfile(user.getIdentifier())) {
- int badge = getUserManager().getManagedProfileBadge(user.getIdentifier());
- int resourceId = CORP_BADGE_LABEL_RES_ID[badge % CORP_BADGE_LABEL_RES_ID.length];
- return Resources.getSystem().getString(resourceId, label);
- }
- return label;
+ return getUserManager().getBadgedLabelForUser(label, user);
}
@Override
@@ -2865,8 +2854,8 @@ public class ApplicationPackageManager extends PackageManager {
return drawable;
}
- private boolean isManagedProfile(int userId) {
- return getUserManager().isManagedProfile(userId);
+ private boolean hasUserBadge(int userId) {
+ return getUserManager().hasBadge(userId);
}
/**
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f0b354650cf4..e858e6a976bc 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1215,7 +1215,8 @@ public final class LoadedApk {
}
// Rewrite the R 'constants' for all library apks.
- SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
+ SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
+ false, false);
final int N = packageIdentifiers.size();
for (int i = 0; i < N; i++) {
final int id = packageIdentifiers.keyAt(i);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1e87ab1a2866..4fb2196d4e26 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -171,8 +171,6 @@ import android.telephony.TelephonyManager;
import android.telephony.TelephonyRegistryManager;
import android.telephony.euicc.EuiccCardManager;
import android.telephony.euicc.EuiccManager;
-import android.telephony.ims.ImsManager;
-import android.telephony.ims.RcsMessageManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.ContextThemeWrapper;
@@ -625,22 +623,6 @@ public final class SystemServiceRegistry {
return new SubscriptionManager(ctx.getOuterContext());
}});
- registerService(Context.TELEPHONY_RCS_MESSAGE_SERVICE, RcsMessageManager.class,
- new CachedServiceFetcher<RcsMessageManager>() {
- @Override
- public RcsMessageManager createService(ContextImpl ctx) {
- return new RcsMessageManager(ctx.getOuterContext());
- }
- });
-
- registerService(Context.TELEPHONY_IMS_SERVICE, ImsManager.class,
- new CachedServiceFetcher<ImsManager>() {
- @Override
- public ImsManager createService(ContextImpl ctx) {
- return new ImsManager(ctx.getOuterContext());
- }
- });
-
registerService(Context.CARRIER_CONFIG_SERVICE, CarrierConfigManager.class,
new CachedServiceFetcher<CarrierConfigManager>() {
@Override
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index c6160446c798..19f42b6a4c9e 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -173,13 +173,10 @@ public final class BluetoothDevice implements Parcelable {
* changed.
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
- *
- * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @UnsupportedAppUsage
public static final String ACTION_ALIAS_CHANGED =
- "android.bluetooth.device.action.ALIAS_CHANGED";
+ "android.bluetooth.action.ALIAS_CHANGED";
/**
* Broadcast Action: Indicates a change in the bond state of a remote
@@ -1048,10 +1045,11 @@ public final class BluetoothDevice implements Parcelable {
* Get the Bluetooth alias of the remote device.
* <p>Alias is the locally modified name of a remote device.
*
- * @return the Bluetooth alias, or null if no alias or there was a problem
- * @hide
+ * @return the Bluetooth alias, the friendly device name if no alias, or
+ * null if there was a problem
*/
- @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.")
+ @Nullable
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
public String getAlias() {
final IBluetooth service = sService;
if (service == null) {
@@ -1059,7 +1057,11 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
try {
- return service.getRemoteAlias(this);
+ String alias = service.getRemoteAlias(this);
+ if (alias == null) {
+ return getName();
+ }
+ return alias;
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1076,8 +1078,9 @@ public final class BluetoothDevice implements Parcelable {
* @return true on success, false on error
* @hide
*/
- @UnsupportedAppUsage
- public boolean setAlias(String alias) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public boolean setAlias(@NonNull String alias) {
final IBluetooth service = sService;
if (service == null) {
Log.e(TAG, "BT not enabled. Cannot set Remote Device name");
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 75e726bfad0d..0f67f6b50251 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -129,7 +129,7 @@ public class BluetoothDeviceFilterUtils {
@UnsupportedAppUsage
public static String getDeviceDisplayNameInternal(@NonNull BluetoothDevice device) {
- return firstNotEmpty(device.getAliasName(), device.getAddress());
+ return firstNotEmpty(device.getAlias(), device.getAddress());
}
@UnsupportedAppUsage
diff --git a/core/java/android/content/ContentInterface.java b/core/java/android/content/ContentInterface.java
index 197de9711296..5988dd3914f1 100644
--- a/core/java/android/content/ContentInterface.java
+++ b/core/java/android/content/ContentInterface.java
@@ -53,23 +53,22 @@ public interface ContentInterface {
public @Nullable Uri uncanonicalize(@NonNull Uri uri) throws RemoteException;
- public boolean refresh(@NonNull Uri uri, @Nullable Bundle args,
+ public boolean refresh(@NonNull Uri uri, @Nullable Bundle extras,
@Nullable CancellationSignal cancellationSignal) throws RemoteException;
public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)
throws RemoteException;
- public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues)
- throws RemoteException;
+ public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues,
+ @Nullable Bundle extras) throws RemoteException;
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] initialValues)
throws RemoteException;
- public int delete(@NonNull Uri uri, @Nullable String selection,
- @Nullable String[] selectionArgs) throws RemoteException;
+ public int delete(@NonNull Uri uri, @Nullable Bundle extras) throws RemoteException;
- public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
- @Nullable String[] selectionArgs) throws RemoteException;
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Bundle extras)
+ throws RemoteException;
public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
@Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 17f1a07d6e01..2240823ebe92 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -299,7 +299,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
@Override
public Uri insert(String callingPkg, @Nullable String featureId, Uri uri,
- ContentValues initialValues) {
+ ContentValues initialValues, Bundle extras) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
@@ -317,7 +317,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
final Pair<String, String> original = setCallingPackage(
new Pair<>(callingPkg, featureId));
try {
- return maybeAddUserId(mInterface.insert(uri, initialValues), userId);
+ return maybeAddUserId(mInterface.insert(uri, initialValues, extras), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
@@ -403,8 +403,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public int delete(String callingPkg, @Nullable String featureId, Uri uri, String selection,
- String[] selectionArgs) {
+ public int delete(String callingPkg, @Nullable String featureId, Uri uri, Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, featureId, uri, null)
@@ -415,7 +414,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
final Pair<String, String> original = setCallingPackage(
new Pair<>(callingPkg, featureId));
try {
- return mInterface.delete(uri, selection, selectionArgs);
+ return mInterface.delete(uri, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
@@ -426,7 +425,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
@Override
public int update(String callingPkg, @Nullable String featureId, Uri uri,
- ContentValues values, String selection, String[] selectionArgs) {
+ ContentValues values, Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, featureId, uri, null)
@@ -437,7 +436,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
final Pair<String, String> original = setCallingPackage(
new Pair<>(callingPkg, featureId));
try {
- return mInterface.update(uri, values, selection, selectionArgs);
+ return mInterface.update(uri, values, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
@@ -593,7 +592,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle args,
+ public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle extras,
ICancellationSignal cancellationSignal) throws RemoteException {
uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
@@ -605,7 +604,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
final Pair<String, String> original = setCallingPackage(
new Pair<>(callingPkg, featureId));
try {
- return mInterface.refresh(uri, args,
+ return mInterface.refresh(uri, extras,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
@@ -1494,29 +1493,34 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/**
- * Implement this to support refresh of content identified by {@code uri}. By default, this
- * method returns false; providers who wish to implement this should return true to signal the
- * client that the provider has tried refreshing with its own implementation.
+ * Implement this to support refresh of content identified by {@code uri}.
+ * By default, this method returns false; providers who wish to implement
+ * this should return true to signal the client that the provider has tried
+ * refreshing with its own implementation.
* <p>
- * This allows clients to request an explicit refresh of content identified by {@code uri}.
+ * This allows clients to request an explicit refresh of content identified
+ * by {@code uri}.
* <p>
- * Client code should only invoke this method when there is a strong indication (such as a user
- * initiated pull to refresh gesture) that the content is stale.
+ * Client code should only invoke this method when there is a strong
+ * indication (such as a user initiated pull to refresh gesture) that the
+ * content is stale.
* <p>
- * Remember to send {@link ContentResolver#notifyChange(Uri, android.database.ContentObserver)}
+ * Remember to send
+ * {@link ContentResolver#notifyChange(Uri, android.database.ContentObserver)}
* notifications when content changes.
*
* @param uri The Uri identifying the data to refresh.
- * @param args Additional options from the client. The definitions of these are specific to the
- * content provider being called.
- * @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if
- * none. For example, if you called refresh on a particular uri, you should call
- * {@link CancellationSignal#throwIfCanceled()} to check whether the client has
- * canceled the refresh request.
+ * @param extras Additional options from the client. The definitions of
+ * these are specific to the content provider being called.
+ * @param cancellationSignal A signal to cancel the operation in progress,
+ * or {@code null} if none. For example, if you called refresh on
+ * a particular uri, you should call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the refresh request.
* @return true if the provider actually tried refreshing.
*/
@Override
- public boolean refresh(Uri uri, @Nullable Bundle args,
+ public boolean refresh(Uri uri, @Nullable Bundle extras,
@Nullable CancellationSignal cancellationSignal) {
return false;
}
@@ -1545,20 +1549,42 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/**
- * Implement this to handle requests to insert a new row.
- * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
- * after inserting.
- * This method can be called from multiple threads, as described in
- * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * Implement this to handle requests to insert a new row. As a courtesy,
+ * call
+ * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+ * notifyChange()} after inserting. This method can be called from multiple
+ * threads, as described in <a href="
+ * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
* and Threads</a>.
+ *
* @param uri The content:// URI of the insertion request.
* @param values A set of column_name/value pairs to add to the database.
* @return The URI for the newly inserted item.
*/
- @Override
public abstract @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values);
/**
+ * Implement this to handle requests to insert a new row. As a courtesy,
+ * call
+ * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+ * notifyChange()} after inserting. This method can be called from multiple
+ * threads, as described in <a href="
+ * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * and Threads</a>.
+ *
+ * @param uri The content:// URI of the insertion request.
+ * @param values A set of column_name/value pairs to add to the database.
+ * @param extras A Bundle containing all additional information necessary
+ * for the insert.
+ * @return The URI for the newly inserted item.
+ */
+ @Override
+ public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values,
+ @Nullable Bundle extras) {
+ return insert(uri, values);
+ }
+
+ /**
* Override this to handle requests to insert a set of new rows, or the
* default implementation will iterate over the values and call
* {@link #insert} on each of them.
@@ -1583,50 +1609,111 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/**
- * Implement this to handle requests to delete one or more rows.
- * The implementation should apply the selection clause when performing
+ * Implement this to handle requests to delete one or more rows. The
+ * implementation should apply the selection clause when performing
* deletion, allowing the operation to affect multiple rows in a directory.
- * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
- * after deleting.
- * This method can be called from multiple threads, as described in
- * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * As a courtesy, call
+ * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+ * notifyChange()} after deleting. This method can be called from multiple
+ * threads, as described in <a href="
+ * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
* and Threads</a>.
- *
- * <p>The implementation is responsible for parsing out a row ID at the end
- * of the URI, if a specific row is being deleted. That is, the client would
- * pass in <code>content://contacts/people/22</code> and the implementation is
- * responsible for parsing the record number (22) when creating a SQL statement.
- *
- * @param uri The full URI to query, including a row ID (if a specific record is requested).
+ * <p>
+ * The implementation is responsible for parsing out a row ID at the end of
+ * the URI, if a specific row is being deleted. That is, the client would
+ * pass in <code>content://contacts/people/22</code> and the implementation
+ * is responsible for parsing the record number (22) when creating a SQL
+ * statement.
+ *
+ * @param uri The full URI to query, including a row ID (if a specific
+ * record is requested).
* @param selection An optional restriction to apply to rows when deleting.
* @return The number of rows affected.
* @throws SQLException
*/
- @Override
public abstract int delete(@NonNull Uri uri, @Nullable String selection,
@Nullable String[] selectionArgs);
/**
- * Implement this to handle requests to update one or more rows.
- * The implementation should update all rows matching the selection
- * to set the columns according to the provided values map.
- * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
- * after updating.
- * This method can be called from multiple threads, as described in
- * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * Implement this to handle requests to delete one or more rows. The
+ * implementation should apply the selection clause when performing
+ * deletion, allowing the operation to affect multiple rows in a directory.
+ * As a courtesy, call
+ * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+ * notifyChange()} after deleting. This method can be called from multiple
+ * threads, as described in <a href="
+ * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * and Threads</a>.
+ * <p>
+ * The implementation is responsible for parsing out a row ID at the end of
+ * the URI, if a specific row is being deleted. That is, the client would
+ * pass in <code>content://contacts/people/22</code> and the implementation
+ * is responsible for parsing the record number (22) when creating a SQL
+ * statement.
+ *
+ * @param uri The full URI to query, including a row ID (if a specific
+ * record is requested).
+ * @param extras A Bundle containing all additional information necessary
+ * for the delete. Values in the Bundle may include SQL style
+ * arguments.
+ * @return The number of rows affected.
+ * @throws SQLException
+ */
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable Bundle extras) {
+ extras = (extras != null) ? extras : Bundle.EMPTY;
+ return delete(uri,
+ extras.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
+ extras.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS));
+ }
+
+ /**
+ * Implement this to handle requests to update one or more rows. The
+ * implementation should update all rows matching the selection to set the
+ * columns according to the provided values map. As a courtesy, call
+ * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+ * notifyChange()} after updating. This method can be called from multiple
+ * threads, as described in <a href="
+ * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
* and Threads</a>.
*
- * @param uri The URI to query. This can potentially have a record ID if this
- * is an update request for a specific record.
+ * @param uri The URI to query. This can potentially have a record ID if
+ * this is an update request for a specific record.
* @param values A set of column_name/value pairs to update in the database.
* @param selection An optional filter to match rows to update.
* @return the number of rows affected.
*/
- @Override
public abstract int update(@NonNull Uri uri, @Nullable ContentValues values,
@Nullable String selection, @Nullable String[] selectionArgs);
/**
+ * Implement this to handle requests to update one or more rows. The
+ * implementation should update all rows matching the selection to set the
+ * columns according to the provided values map. As a courtesy, call
+ * {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
+ * notifyChange()} after updating. This method can be called from multiple
+ * threads, as described in <a href="
+ * {@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * and Threads</a>.
+ *
+ * @param uri The URI to query. This can potentially have a record ID if
+ * this is an update request for a specific record.
+ * @param values A set of column_name/value pairs to update in the database.
+ * @param extras A Bundle containing all additional information necessary
+ * for the update. Values in the Bundle may include SQL style
+ * arguments.
+ * @return the number of rows affected.
+ */
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values,
+ @Nullable Bundle extras) {
+ extras = (extras != null) ? extras : Bundle.EMPTY;
+ return update(uri, values,
+ extras.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
+ extras.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS));
+ }
+
+ /**
* Override this to handle requests to open a file blob.
* The default implementation always throws {@link FileNotFoundException}.
* This method can be called from multiple threads, as described in
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index d2632e78c00c..bb65aa013f72 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -286,7 +286,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
/** See {@link ContentProvider#refresh} */
@Override
- public boolean refresh(Uri url, @Nullable Bundle args,
+ public boolean refresh(Uri url, @Nullable Bundle extras,
@Nullable CancellationSignal cancellationSignal) throws RemoteException {
Preconditions.checkNotNull(url, "url");
@@ -298,7 +298,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return mContentProvider.refresh(mPackageName, mFeatureId, url, args,
+ return mContentProvider.refresh(mPackageName, mFeatureId, url, extras,
remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -331,14 +331,20 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
}
/** See {@link ContentProvider#insert ContentProvider.insert} */
- @Override
public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
throws RemoteException {
+ return insert(url, initialValues, null);
+ }
+
+ /** See {@link ContentProvider#insert ContentProvider.insert} */
+ @Override
+ public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues,
+ @Nullable Bundle extras) throws RemoteException {
Preconditions.checkNotNull(url, "url");
beforeRemote();
try {
- return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues);
+ return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -370,15 +376,19 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
}
/** See {@link ContentProvider#delete ContentProvider.delete} */
- @Override
public int delete(@NonNull Uri url, @Nullable String selection,
@Nullable String[] selectionArgs) throws RemoteException {
+ return delete(url, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+ }
+
+ /** See {@link ContentProvider#delete ContentProvider.delete} */
+ @Override
+ public int delete(@NonNull Uri url, @Nullable Bundle extras) throws RemoteException {
Preconditions.checkNotNull(url, "url");
beforeRemote();
try {
- return mContentProvider.delete(mPackageName, mFeatureId, url, selection,
- selectionArgs);
+ return mContentProvider.delete(mPackageName, mFeatureId, url, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -390,15 +400,20 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
}
/** See {@link ContentProvider#update ContentProvider.update} */
- @Override
public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) throws RemoteException {
+ return update(url, values, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+ }
+
+ /** See {@link ContentProvider#update ContentProvider.update} */
+ @Override
+ public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable Bundle extras)
+ throws RemoteException {
Preconditions.checkNotNull(url, "url");
beforeRemote();
try {
- return mContentProvider.update(mPackageName, mFeatureId, url, values, selection,
- selectionArgs);
+ return mContentProvider.update(mPackageName, mFeatureId, url, values, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index f082690e2ceb..dfa71f88fa0a 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -153,8 +153,9 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
+ Bundle extras = data.readBundle();
- Uri out = insert(callingPkg, featureId, url, values);
+ Uri out = insert(callingPkg, featureId, url, values, extras);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -199,10 +200,9 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String callingPkg = data.readString();
String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
- String selection = data.readString();
- String[] selectionArgs = data.readStringArray();
+ Bundle extras = data.readBundle();
- int count = delete(callingPkg, featureId, url, selection, selectionArgs);
+ int count = delete(callingPkg, featureId, url, extras);
reply.writeNoException();
reply.writeInt(count);
@@ -216,11 +216,9 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
- String selection = data.readString();
- String[] selectionArgs = data.readStringArray();
+ Bundle extras = data.readBundle();
- int count = update(callingPkg, featureId, url, values, selection,
- selectionArgs);
+ int count = update(callingPkg, featureId, url, values, extras);
reply.writeNoException();
reply.writeInt(count);
@@ -283,10 +281,10 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String authority = data.readString();
String method = data.readString();
String stringArg = data.readString();
- Bundle args = data.readBundle();
+ Bundle extras = data.readBundle();
Bundle responseBundle = call(callingPkg, featureId, authority, method,
- stringArg, args);
+ stringArg, extras);
reply.writeNoException();
reply.writeBundle(responseBundle);
@@ -370,11 +368,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String callingPkg = data.readString();
String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
- Bundle args = data.readBundle();
+ Bundle extras = data.readBundle();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- boolean out = refresh(callingPkg, featureId, url, args, signal);
+ boolean out = refresh(callingPkg, featureId, url, extras, signal);
reply.writeNoException();
reply.writeInt(out ? 0 : -1);
return true;
@@ -498,7 +496,7 @@ final class ContentProviderProxy implements IContentProvider
@Override
public Uri insert(String callingPkg, @Nullable String featureId, Uri url,
- ContentValues values) throws RemoteException
+ ContentValues values, Bundle extras) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -509,6 +507,7 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(featureId);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
+ data.writeBundle(extras);
mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0);
@@ -573,8 +572,8 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public int delete(String callingPkg, @Nullable String featureId, Uri url, String selection,
- String[] selectionArgs) throws RemoteException {
+ public int delete(String callingPkg, @Nullable String featureId, Uri url, Bundle extras)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
@@ -583,8 +582,7 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(callingPkg);
data.writeString(featureId);
url.writeToParcel(data, 0);
- data.writeString(selection);
- data.writeStringArray(selectionArgs);
+ data.writeBundle(extras);
mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0);
@@ -599,7 +597,7 @@ final class ContentProviderProxy implements IContentProvider
@Override
public int update(String callingPkg, @Nullable String featureId, Uri url,
- ContentValues values, String selection, String[] selectionArgs) throws RemoteException {
+ ContentValues values, Bundle extras) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
@@ -609,8 +607,7 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(featureId);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
- data.writeString(selection);
- data.writeStringArray(selectionArgs);
+ data.writeBundle(extras);
mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0);
@@ -682,7 +679,7 @@ final class ContentProviderProxy implements IContentProvider
@Override
public Bundle call(String callingPkg, @Nullable String featureId, String authority,
- String method, String request, Bundle args) throws RemoteException {
+ String method, String request, Bundle extras) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
@@ -693,7 +690,7 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(authority);
data.writeString(method);
data.writeString(request);
- data.writeBundle(args);
+ data.writeBundle(extras);
mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0);
@@ -824,7 +821,7 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle extras,
ICancellationSignal signal) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -834,7 +831,7 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(callingPkg);
data.writeString(featureId);
url.writeToParcel(data, 0);
- data.writeBundle(args);
+ data.writeBundle(extras);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.REFRESH_TRANSACTION, data, reply, 0);
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 5c2de57d77a5..93f42879d946 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -357,11 +357,22 @@ public class ContentProviderOperation implements Parcelable {
ContentProviderResult[] backRefs, int numBackRefs)
throws OperationApplicationException {
final ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
- final Bundle extras = resolveExtrasBackReferences(backRefs, numBackRefs);
- final String[] selectionArgs = resolveSelectionArgsBackReferences(backRefs, numBackRefs);
+
+ // If the creator requested explicit selection or selectionArgs, it
+ // should take precedence over similar values they defined in extras
+ Bundle extras = resolveExtrasBackReferences(backRefs, numBackRefs);
+ if (mSelection != null) {
+ extras = (extras != null) ? extras : new Bundle();
+ extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, mSelection);
+ }
+ if (mSelectionArgs != null) {
+ extras = (extras != null) ? extras : new Bundle();
+ extras.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
+ resolveSelectionArgsBackReferences(backRefs, numBackRefs));
+ }
if (mType == TYPE_INSERT) {
- final Uri newUri = provider.insert(mUri, values);
+ final Uri newUri = provider.insert(mUri, values, extras);
if (newUri != null) {
return new ContentProviderResult(newUri);
} else {
@@ -375,9 +386,9 @@ public class ContentProviderOperation implements Parcelable {
final int numRows;
if (mType == TYPE_DELETE) {
- numRows = provider.delete(mUri, mSelection, selectionArgs);
+ numRows = provider.delete(mUri, extras);
} else if (mType == TYPE_UPDATE) {
- numRows = provider.update(mUri, values, mSelection, selectionArgs);
+ numRows = provider.update(mUri, values, extras);
} else if (mType == TYPE_ASSERT) {
// Assert that all rows match expected values
String[] projection = null;
@@ -389,7 +400,7 @@ public class ContentProviderOperation implements Parcelable {
}
projection = projectionList.toArray(new String[projectionList.size()]);
}
- final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null);
+ final Cursor cursor = provider.query(mUri, projection, extras, null);
try {
numRows = cursor.getCount();
if (projection != null) {
@@ -1013,6 +1024,10 @@ public class ContentProviderOperation implements Parcelable {
private void assertExtrasAllowed() {
switch (mType) {
+ case TYPE_INSERT:
+ case TYPE_UPDATE:
+ case TYPE_DELETE:
+ case TYPE_ASSERT:
case TYPE_CALL:
break;
default:
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 61c8db5db124..d4280f8992c2 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -304,31 +304,61 @@ public abstract class ContentResolver implements ContentInterface {
*/
public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
- /** {@hide} */
+ /**
+ * Key for an SQL style {@code GROUP BY} string that may be present in the
+ * query Bundle argument passed to
+ * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}.
+ *
+ * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+ * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+ *
+ * @see #QUERY_ARG_GROUP_COLUMNS
+ */
public static final String QUERY_ARG_SQL_GROUP_BY = "android:query-arg-sql-group-by";
- /** {@hide} */
- public static final String QUERY_ARG_SQL_HAVING = "android:query-arg-sql-having";
- /** {@hide} */
- public static final String QUERY_ARG_SQL_LIMIT = "android:query-arg-sql-limit";
/**
- * Specifies the list of columns against which to sort results. When first column values
- * are identical, records are then sorted based on second column values, and so on.
+ * Key for an SQL style {@code HAVING} string that may be present in the
+ * query Bundle argument passed to
+ * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}.
*
- * <p>Columns present in this list must also be included in the projection
- * supplied to {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+ * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+ * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
+ */
+ public static final String QUERY_ARG_SQL_HAVING = "android:query-arg-sql-having";
+
+ /**
+ * Key for an SQL style {@code LIMIT} string that may be present in the
+ * query Bundle argument passed to
+ * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}.
*
- * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher:
+ * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
+ * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
*
+ * @see #QUERY_ARG_LIMIT
+ * @see #QUERY_ARG_OFFSET
+ */
+ public static final String QUERY_ARG_SQL_LIMIT = "android:query-arg-sql-limit";
+
+ /**
+ * Specifies the list of columns (stored as a {@code String[]}) against
+ * which to sort results. When first column values are identical, records
+ * are then sorted based on second column values, and so on.
+ * <p>
+ * Columns present in this list must also be included in the projection
+ * supplied to
+ * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+ * <p>
+ * Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher:
* <li>{@link ContentProvider} implementations: When preparing data in
- * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}, if sort columns
- * is reflected in the returned Cursor, it is strongly recommended that
- * {@link #QUERY_ARG_SORT_COLUMNS} then be included in the array of honored arguments
- * reflected in {@link Cursor} extras {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
- *
- * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
- * arguments {@link Bundle}, the Content framework will attempt to synthesize
- * an QUERY_ARG_SQL* argument using the corresponding QUERY_ARG_SORT* values.
+ * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+ * if sort columns is reflected in the returned Cursor, it is strongly
+ * recommended that {@link #QUERY_ARG_SORT_COLUMNS} then be included in the
+ * array of honored arguments reflected in {@link Cursor} extras
+ * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+ * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in
+ * the arguments {@link Bundle}, the Content framework will attempt to
+ * synthesize an QUERY_ARG_SQL* argument using the corresponding
+ * QUERY_ARG_SORT* values.
*/
public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
@@ -402,6 +432,29 @@ public abstract class ContentResolver implements ContentInterface {
public static final String QUERY_ARG_SORT_LOCALE = "android:query-arg-sort-locale";
/**
+ * Specifies the list of columns (stored as a {@code String[]}) against
+ * which to group results. When column values are identical, multiple
+ * records are collapsed together into a single record.
+ * <p>
+ * Columns present in this list must also be included in the projection
+ * supplied to
+ * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+ * <p>
+ * Apps targeting {@link android.os.Build.VERSION_CODES#R} or higher:
+ * <li>{@link ContentProvider} implementations: When preparing data in
+ * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+ * if group columns is reflected in the returned Cursor, it is strongly
+ * recommended that {@link #QUERY_ARG_SORT_COLUMNS} then be included in the
+ * array of honored arguments reflected in {@link Cursor} extras
+ * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+ * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in
+ * the arguments {@link Bundle}, the Content framework will attempt to
+ * synthesize an QUERY_ARG_SQL* argument using the corresponding
+ * QUERY_ARG_SORT* values.
+ */
+ public static final String QUERY_ARG_GROUP_COLUMNS = "android:query-arg-group-columns";
+
+ /**
* Allows provider to report back to client which query keys are honored in a Cursor.
*
* <p>Key identifying a {@code String[]} containing all QUERY_ARG_SORT* arguments
@@ -415,6 +468,7 @@ public abstract class ContentResolver implements ContentInterface {
* @see #QUERY_ARG_SORT_DIRECTION
* @see #QUERY_ARG_SORT_COLLATION
* @see #QUERY_ARG_SORT_LOCALE
+ * @see #QUERY_ARG_GROUP_COLUMNS
*/
public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
@@ -1127,28 +1181,31 @@ public abstract class ContentResolver implements ContentInterface {
}
/**
- * This allows clients to request an explicit refresh of content identified by {@code uri}.
+ * This allows clients to request an explicit refresh of content identified
+ * by {@code uri}.
* <p>
- * Client code should only invoke this method when there is a strong indication (such as a user
- * initiated pull to refresh gesture) that the content is stale.
+ * Client code should only invoke this method when there is a strong
+ * indication (such as a user initiated pull to refresh gesture) that the
+ * content is stale.
* <p>
*
* @param url The Uri identifying the data to refresh.
- * @param args Additional options from the client. The definitions of these are specific to the
- * content provider being called.
- * @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if
- * none. For example, if you called refresh on a particular uri, you should call
- * {@link CancellationSignal#throwIfCanceled()} to check whether the client has
- * canceled the refresh request.
+ * @param extras Additional options from the client. The definitions of
+ * these are specific to the content provider being called.
+ * @param cancellationSignal A signal to cancel the operation in progress,
+ * or {@code null} if none. For example, if you called refresh on
+ * a particular uri, you should call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the refresh request.
* @return true if the provider actually tried refreshing.
*/
@Override
- public final boolean refresh(@NonNull Uri url, @Nullable Bundle args,
+ public final boolean refresh(@NonNull Uri url, @Nullable Bundle extras,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(url, "url");
try {
- if (mWrapped != null) return mWrapped.refresh(url, args, cancellationSignal);
+ if (mWrapped != null) return mWrapped.refresh(url, extras, cancellationSignal);
} catch (RemoteException e) {
return false;
}
@@ -1165,7 +1222,7 @@ public abstract class ContentResolver implements ContentInterface {
remoteCancellationSignal = provider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return provider.refresh(mPackageName, mFeatureId, url, args,
+ return provider.refresh(mPackageName, mFeatureId, url, extras,
remoteCancellationSignal);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
@@ -1856,13 +1913,30 @@ public abstract class ContentResolver implements ContentInterface {
* @return the URL of the newly created row. May return <code>null</code> if the underlying
* content provider returns <code>null</code>, or if it crashes.
*/
- @Override
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@Nullable ContentValues values) {
+ return insert(url, values, null);
+ }
+
+ /**
+ * Inserts a row into a table at the given URL.
+ *
+ * If the content provider supports transactions the insertion will be atomic.
+ *
+ * @param url The URL of the table to insert into.
+ * @param values The initial values for the newly inserted row. The key is the column name for
+ * the field. Passing an empty ContentValues will create an empty row.
+ * @param extras A Bundle containing all additional information necessary for the insert.
+ * @return the URL of the newly created row. May return <code>null</code> if the underlying
+ * content provider returns <code>null</code>, or if it crashes.
+ */
+ @Override
+ public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
+ @Nullable ContentValues values, @Nullable Bundle extras) {
Preconditions.checkNotNull(url, "url");
try {
- if (mWrapped != null) return mWrapped.insert(url, values);
+ if (mWrapped != null) return mWrapped.insert(url, values, extras);
} catch (RemoteException e) {
return null;
}
@@ -1873,7 +1947,7 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
long startTime = SystemClock.uptimeMillis();
- Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values);
+ Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
@@ -1977,13 +2051,27 @@ public abstract class ContentResolver implements ContentInterface {
(excluding the WHERE itself).
* @return The number of rows deleted.
*/
- @Override
public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
@Nullable String[] selectionArgs) {
+ return delete(url, createSqlQueryBundle(where, selectionArgs));
+ }
+
+ /**
+ * Deletes row(s) specified by a content URI.
+ *
+ * If the content provider supports transactions, the deletion will be atomic.
+ *
+ * @param url The URL of the row to delete.
+ * @param extras A Bundle containing all additional information necessary for the delete.
+ * Values in the Bundle may include SQL style arguments.
+ * @return The number of rows deleted.
+ */
+ @Override
+ public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable Bundle extras) {
Preconditions.checkNotNull(url, "url");
try {
- if (mWrapped != null) return mWrapped.delete(url, where, selectionArgs);
+ if (mWrapped != null) return mWrapped.delete(url, extras);
} catch (RemoteException e) {
return 0;
}
@@ -1994,10 +2082,9 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, where,
- selectionArgs);
+ int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
- maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
+ maybeLogUpdateToEventLog(durationMillis, url, "delete", null);
return rowsDeleted;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
@@ -2021,14 +2108,32 @@ public abstract class ContentResolver implements ContentInterface {
* @return the number of rows updated.
* @throws NullPointerException if uri or values are null
*/
- @Override
public final int update(@RequiresPermission.Write @NonNull Uri uri,
@Nullable ContentValues values, @Nullable String where,
@Nullable String[] selectionArgs) {
+ return update(uri, values, createSqlQueryBundle(where, selectionArgs));
+ }
+
+ /**
+ * Update row(s) in a content URI.
+ *
+ * If the content provider supports transactions the update will be atomic.
+ *
+ * @param uri The URI to modify.
+ * @param values The new field values. The key is the column name for the field.
+ A null value will remove an existing field value.
+ * @param extras A Bundle containing all additional information necessary for the update.
+ * Values in the Bundle may include SQL style arguments.
+ * @return the number of rows updated.
+ * @throws NullPointerException if uri or values are null
+ */
+ @Override
+ public final int update(@RequiresPermission.Write @NonNull Uri uri,
+ @Nullable ContentValues values, @Nullable Bundle extras) {
Preconditions.checkNotNull(uri, "uri");
try {
- if (mWrapped != null) return mWrapped.update(uri, values, where, selectionArgs);
+ if (mWrapped != null) return mWrapped.update(uri, values, extras);
} catch (RemoteException e) {
return 0;
}
@@ -2039,10 +2144,9 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, where,
- selectionArgs);
+ int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
- maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
+ maybeLogUpdateToEventLog(durationMillis, uri, "update", null);
return rowsUpdated;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
@@ -3589,6 +3693,15 @@ public abstract class ContentResolver implements ContentInterface {
*/
public static @Nullable Bundle createSqlQueryBundle(
@Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ return createSqlQueryBundle(selection, selectionArgs, null);
+ }
+
+ /**
+ * @hide
+ */
+ public static @Nullable Bundle createSqlQueryBundle(
+ @Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable String sortOrder) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 41b773e81c46..03b49136bde4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2072,7 +2072,7 @@ public abstract class Context {
* Intent will receive the broadcast.
* @param receiverPermissions Array of names of permissions that a receiver must hold
* in order to receive your broadcast.
- * If null or empty, no permissions are required.
+ * If empty, no permissions are required.
*
* @see android.content.BroadcastReceiver
* @see #registerReceiver
@@ -2081,8 +2081,11 @@ public abstract class Context {
* @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
* @hide
*/
- public abstract void sendBroadcastMultiplePermissions(Intent intent,
- String[] receiverPermissions);
+ @SystemApi
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
/**
* Broadcast the given intent to all interested BroadcastReceivers, allowing
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 5bdea52d63e1..b04f7810ed45 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -464,7 +464,8 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
- public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions) {
mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions);
}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index d2c97c4ebdd3..1fb29586e15b 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -48,10 +48,10 @@ public interface IContentProvider extends IInterface {
+ "instead")
public default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
throws RemoteException {
- return insert(callingPkg, null, url, initialValues);
+ return insert(callingPkg, null, url, initialValues, null);
}
- public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues)
- throws RemoteException;
+ public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues,
+ Bundle extras) throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
@@ -68,20 +68,22 @@ public interface IContentProvider extends IInterface {
+ ".String[])} instead")
public default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
throws RemoteException {
- return delete(callingPkg, null, url, selection, selectionArgs);
+ return delete(callingPkg, null, url,
+ ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
- public int delete(String callingPkg, String featureId, Uri url, String selection,
- String[] selectionArgs) throws RemoteException;
+ public int delete(String callingPkg, String featureId, Uri url, Bundle extras)
+ throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#update(android.net.Uri, android.content.ContentValues, java"
+ ".lang.String, java.lang.String[])} instead")
public default int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
- return update(callingPkg, null, url, values, selection, selectionArgs);
+ return update(callingPkg, null, url, values,
+ ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
public int update(String callingPkg, String featureId, Uri url, ContentValues values,
- String selection, String[] selectionArgs) throws RemoteException;
+ Bundle extras) throws RemoteException;
public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
String mode, ICancellationSignal signal, IBinder callerToken)
@@ -119,7 +121,7 @@ public interface IContentProvider extends IInterface {
throws RemoteException;
public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
- @Nullable Bundle args, ICancellationSignal cancellationSignal) throws RemoteException;
+ @Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
diff --git a/core/java/android/content/LoggingContentInterface.java b/core/java/android/content/LoggingContentInterface.java
index 1df1c4faf2fe..3bd083268d45 100644
--- a/core/java/android/content/LoggingContentInterface.java
+++ b/core/java/android/content/LoggingContentInterface.java
@@ -178,11 +178,11 @@ public class LoggingContentInterface implements ContentInterface {
}
@Override
- public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues)
- throws RemoteException {
- try (Logger l = new Logger("insert", uri, initialValues)) {
+ public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues,
+ @Nullable Bundle extras) throws RemoteException {
+ try (Logger l = new Logger("insert", uri, initialValues, extras)) {
try {
- return l.setResult(delegate.insert(uri, initialValues));
+ return l.setResult(delegate.insert(uri, initialValues, extras));
} catch (Exception res) {
l.setResult(res);
throw res;
@@ -204,11 +204,10 @@ public class LoggingContentInterface implements ContentInterface {
}
@Override
- public int delete(@NonNull Uri uri, @Nullable String selection,
- @Nullable String[] selectionArgs) throws RemoteException {
- try (Logger l = new Logger("delete", uri, selection, selectionArgs)) {
+ public int delete(@NonNull Uri uri, @Nullable Bundle extras) throws RemoteException {
+ try (Logger l = new Logger("delete", uri, extras)) {
try {
- return l.setResult(delegate.delete(uri, selection, selectionArgs));
+ return l.setResult(delegate.delete(uri, extras));
} catch (Exception res) {
l.setResult(res);
throw res;
@@ -217,11 +216,11 @@ public class LoggingContentInterface implements ContentInterface {
}
@Override
- public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
- @Nullable String[] selectionArgs) throws RemoteException {
- try (Logger l = new Logger("update", uri, values, selection, selectionArgs)) {
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Bundle extras)
+ throws RemoteException {
+ try (Logger l = new Logger("update", uri, values, extras)) {
try {
- return l.setResult(delegate.update(uri, values, selection, selectionArgs));
+ return l.setResult(delegate.update(uri, values, extras));
} catch (Exception res) {
l.setResult(res);
throw res;
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 1e88ce7e3f5c..42d64d81b17e 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
import android.os.Parcel;
@@ -25,6 +26,8 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.DebugUtils;
+import com.android.server.pm.UserTypeDetails;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,13 +35,13 @@ import java.lang.annotation.RetentionPolicy;
* Per-user information.
*
* <p>There are 3 base properties of users: {@link #FLAG_SYSTEM}, {@link #FLAG_FULL}, and
- * {@link #FLAG_MANAGED_PROFILE}. Every user must have one of the following combination of these
+ * {@link #FLAG_PROFILE}. Every user must have one of the following combination of these
* flags:
* <ul>
* <li>FLAG_SYSTEM (user {@link UserHandle#USER_SYSTEM} on a headless-user-0 device)</li>
* <li>FLAG_SYSTEM and FLAG_FULL (user {@link UserHandle#USER_SYSTEM} on a regular device)</li>
* <li>FLAG_FULL (non-profile secondary user)</li>
- * <li>FLAG_MANAGED_PROFILE (profile users)</li>
+ * <li>FLAG_PROFILE (profile users)</li>
* </ul>
* Users can have also have additional flags (such as FLAG_GUEST) as appropriate.
*
@@ -70,13 +73,17 @@ public class UserInfo implements Parcelable {
/**
* Indicates a guest user that may be transient.
+ * @deprecated Use {@link UserManager#USER_TYPE_FULL_GUEST} instead.
*/
+ @Deprecated
public static final int FLAG_GUEST = 0x00000004;
/**
* Indicates the user has restrictions in privileges, in addition to those for normal users.
* Exact meaning TBD. For instance, maybe they can't install apps or administer WiFi access pts.
+ * @deprecated Use {@link UserManager#USER_TYPE_FULL_RESTRICTED} instead.
*/
+ @Deprecated
public static final int FLAG_RESTRICTED = 0x00000008;
/**
@@ -87,7 +94,9 @@ public class UserInfo implements Parcelable {
/**
* Indicates that this user is a profile of another user, for example holding a users
* corporate data.
+ * @deprecated Use {@link UserManager#USER_TYPE_PROFILE_MANAGED} instead.
*/
+ @Deprecated
public static final int FLAG_MANAGED_PROFILE = 0x00000020;
/**
@@ -108,14 +117,16 @@ public class UserInfo implements Parcelable {
/**
* User is for demo purposes only and can be removed at any time.
+ * @deprecated Use {@link UserManager#USER_TYPE_FULL_DEMO} instead.
*/
+ @Deprecated
public static final int FLAG_DEMO = 0x00000200;
/**
* Indicates that this user is a non-profile human user.
*
* <p>When creating a new (non-system) user, this flag will always be forced true unless the
- * user is a {@link #FLAG_MANAGED_PROFILE}. If user {@link UserHandle#USER_SYSTEM} is also a
+ * user is a {@link #FLAG_PROFILE}. If user {@link UserHandle#USER_SYSTEM} is also a
* human user, it must also be flagged as FULL.
*/
public static final int FLAG_FULL = 0x00000400;
@@ -126,11 +137,10 @@ public class UserInfo implements Parcelable {
public static final int FLAG_SYSTEM = 0x00000800;
/**
- * Indicates that this user is some sort of profile. Right now, the only profile type is
- * {@link #FLAG_MANAGED_PROFILE}, but this can include other types of profiles too if any
- * are created in the future. This is therefore not a flag, but an OR of several flags.
+ * Indicates that this user is a profile human user, such as a managed profile.
+ * Mutually exclusive with {@link #FLAG_FULL}.
*/
- public static final int PROFILE_FLAGS_MASK = FLAG_MANAGED_PROFILE;
+ public static final int FLAG_PROFILE = 0x00001000;
/**
* @hide
@@ -147,7 +157,8 @@ public class UserInfo implements Parcelable {
FLAG_EPHEMERAL,
FLAG_DEMO,
FLAG_FULL,
- FLAG_SYSTEM
+ FLAG_SYSTEM,
+ FLAG_PROFILE
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserInfoFlag {
@@ -170,6 +181,13 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public long lastLoggedInTime;
public String lastLoggedInFingerprint;
+
+ /**
+ * Type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}, corresponding to
+ * {@link UserTypeDetails#getName()}.
+ */
+ public String userType;
+
/**
* If this user is a parent user, it would be its own user id.
* If this user is a child user, it would be its parent user id.
@@ -178,7 +196,12 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public int profileGroupId;
public int restrictedProfileParentId;
- /** Which profile badge color/label to use. */
+
+ /**
+ * Which badge color/label to use within a particular {@link UserTypeDetails}, i.e.
+ * the badgeIndex.
+ * This is an index for distinguishing different profiles with the same parent and user type.
+ */
public int profileBadge;
/** User is only partially created. */
@@ -199,21 +222,68 @@ public class UserInfo implements Parcelable {
*/
public boolean preCreated;
+ /**
+ * Creates a UserInfo whose user type is determined automatically by the flags according to
+ * {@link #getDefaultUserType}; can only be used for user types handled there.
+ */
@UnsupportedAppUsage
public UserInfo(int id, String name, int flags) {
this(id, name, null, flags);
}
+ /**
+ * Creates a UserInfo whose user type is determined automatically by the flags according to
+ * {@link #getDefaultUserType}; can only be used for user types handled there.
+ */
@UnsupportedAppUsage
public UserInfo(int id, String name, String iconPath, int flags) {
+ this(id, name, iconPath, flags, getDefaultUserType(flags));
+ }
+
+ public UserInfo(int id, String name, String iconPath, int flags, String userType) {
this.id = id;
this.name = name;
this.flags = flags;
+ this.userType = userType;
this.iconPath = iconPath;
this.profileGroupId = NO_PROFILE_GROUP_ID;
this.restrictedProfileParentId = NO_PROFILE_GROUP_ID;
}
+ /**
+ * Get the user type (such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}) that corresponds to
+ * the given {@link UserInfoFlag}s.
+
+ * <p>The userInfoFlag can contain GUEST, RESTRICTED, MANAGED_PROFILE, DEMO, or else be
+ * interpreted as a regular "secondary" user. It cannot contain more than one of these.
+ * It can contain other UserInfoFlag properties (like EPHEMERAL), which will be ignored here.
+ *
+ * @throws IllegalArgumentException if userInfoFlag is more than one type of user or if it
+ * is a SYSTEM user.
+ *
+ * @hide
+ */
+ public static @NonNull String getDefaultUserType(@UserInfoFlag int userInfoFlag) {
+ if ((userInfoFlag & FLAG_SYSTEM) != 0) {
+ throw new IllegalArgumentException("Cannot getDefaultUserType for flags "
+ + Integer.toHexString(userInfoFlag) + " because it corresponds to a "
+ + "SYSTEM user type.");
+ }
+ final int supportedFlagTypes =
+ FLAG_GUEST | FLAG_RESTRICTED | FLAG_MANAGED_PROFILE | FLAG_DEMO;
+ switch (userInfoFlag & supportedFlagTypes) {
+ case 0 : return UserManager.USER_TYPE_FULL_SECONDARY;
+ case FLAG_GUEST: return UserManager.USER_TYPE_FULL_GUEST;
+ case FLAG_RESTRICTED: return UserManager.USER_TYPE_FULL_RESTRICTED;
+ case FLAG_MANAGED_PROFILE: return UserManager.USER_TYPE_PROFILE_MANAGED;
+ case FLAG_DEMO: return UserManager.USER_TYPE_FULL_DEMO;
+ default:
+ throw new IllegalArgumentException("Cannot getDefaultUserType for flags "
+ + Integer.toHexString(userInfoFlag) + " because it doesn't correspond to a "
+ + "valid user type.");
+ }
+ }
+
@UnsupportedAppUsage
public boolean isPrimary() {
return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
@@ -226,31 +296,21 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public boolean isGuest() {
- return isGuest(flags);
- }
-
- /**
- * Checks if the flag denotes a guest user.
- */
- public static boolean isGuest(@UserInfoFlag int flags) {
- return (flags & FLAG_GUEST) == FLAG_GUEST;
+ return UserManager.isUserTypeGuest(userType);
}
@UnsupportedAppUsage
public boolean isRestricted() {
- return (flags & FLAG_RESTRICTED) == FLAG_RESTRICTED;
+ return UserManager.isUserTypeRestricted(userType);
}
- @UnsupportedAppUsage
- public boolean isManagedProfile() {
- return isManagedProfile(flags);
+ public boolean isProfile() {
+ return (flags & FLAG_PROFILE) != 0;
}
- /**
- * Checks if the flag denotes a managed profile.
- */
- public static boolean isManagedProfile(@UserInfoFlag int flags) {
- return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
+ @UnsupportedAppUsage
+ public boolean isManagedProfile() {
+ return UserManager.isUserTypeManagedProfile(userType);
}
@UnsupportedAppUsage
@@ -271,7 +331,7 @@ public class UserInfo implements Parcelable {
}
public boolean isDemo() {
- return (flags & FLAG_DEMO) == FLAG_DEMO;
+ return UserManager.isUserTypeDemo(userType);
}
public boolean isFull() {
@@ -304,7 +364,7 @@ public class UserInfo implements Parcelable {
// Don't support switching to an ephemeral user with removal in progress.
return false;
}
- return !isManagedProfile();
+ return !isProfile();
}
/**
@@ -316,9 +376,10 @@ public class UserInfo implements Parcelable {
return (!hideSystemUser || id != UserHandle.USER_SYSTEM) && supportsSwitchTo();
}
+ // TODO(b/142482943): Make this logic more specific and customizable. (canHaveProfile(userType))
/* @hide */
public boolean canHaveProfile() {
- if (isManagedProfile() || isGuest() || isRestricted()) {
+ if (isProfile() || isGuest() || isRestricted()) {
return false;
}
if (UserManager.isSplitSystemUser() || UserManager.isHeadlessSystemUserMode()) {
@@ -336,6 +397,7 @@ public class UserInfo implements Parcelable {
iconPath = orig.iconPath;
id = orig.id;
flags = orig.flags;
+ userType = orig.userType;
serialNumber = orig.serialNumber;
creationTime = orig.creationTime;
lastLoggedInTime = orig.lastLoggedInTime;
@@ -353,6 +415,7 @@ public class UserInfo implements Parcelable {
return UserHandle.of(id);
}
+ // TODO(b/142482943): Probably include mUserType here, which means updating TestDevice, etc.
@Override
public String toString() {
// NOTE: do not change this string, it's used by 'pm list users', which in turn is
@@ -365,6 +428,7 @@ public class UserInfo implements Parcelable {
public String toFullString() {
return "UserInfo[id=" + id
+ ", name=" + name
+ + ", type=" + userType
+ ", flags=" + flagsToString(flags)
+ (preCreated ? " (pre-created)" : "")
+ (partial ? " (partial)" : "")
@@ -387,6 +451,7 @@ public class UserInfo implements Parcelable {
dest.writeString(name);
dest.writeString(iconPath);
dest.writeInt(flags);
+ dest.writeString(userType);
dest.writeInt(serialNumber);
dest.writeLong(creationTime);
dest.writeLong(lastLoggedInTime);
@@ -415,6 +480,7 @@ public class UserInfo implements Parcelable {
name = source.readString();
iconPath = source.readString();
flags = source.readInt();
+ userType = source.readString();
serialNumber = source.readInt();
creationTime = source.readLong();
lastLoggedInTime = source.readLong();
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 23e772075ad6..070e282a0eb2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1491,9 +1491,17 @@ public final class AssetManager implements AutoCloseable {
*/
@UnsupportedAppUsage
public SparseArray<String> getAssignedPackageIdentifiers() {
+ return getAssignedPackageIdentifiers(true, true);
+ }
+
+ /**
+ * @hide
+ */
+ public SparseArray<String> getAssignedPackageIdentifiers(boolean includeOverlays,
+ boolean includeLoaders) {
synchronized (this) {
ensureValidLocked();
- return nativeGetAssignedPackageIdentifiers(mObject);
+ return nativeGetAssignedPackageIdentifiers(mObject, includeOverlays, includeLoaders);
}
}
@@ -1557,7 +1565,7 @@ public final class AssetManager implements AutoCloseable {
int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
int uiMode, int colorMode, int majorVersion);
private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
- long ptr);
+ long ptr, boolean includeOverlays, boolean includeLoaders);
// File native methods.
private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index fc90096e5add..b61b1efed125 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -404,7 +404,7 @@ public final class CameraManager {
"Camera service is currently unavailable");
}
cameraUser = cameraService.connectDevice(callbacks, cameraId,
- mContext.getOpPackageName(), uid);
+ mContext.getOpPackageName(), mContext.getFeatureId(), uid);
} else {
// Use legacy camera implementation for HAL1 devices
int id;
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 5bc9953e0d05..990c1142284c 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -25,6 +25,8 @@ import android.net.shared.InetAddressUtils;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
@@ -152,6 +154,7 @@ public final class StaticIpConfiguration implements Parcelable {
* @return The {@link Builder} for chaining.
*/
public @NonNull Builder setDnsServers(@NonNull Iterable<InetAddress> dnsServers) {
+ Preconditions.checkNotNull(dnsServers);
mDnsServers = dnsServers;
return this;
}
@@ -175,8 +178,10 @@ public final class StaticIpConfiguration implements Parcelable {
final StaticIpConfiguration config = new StaticIpConfiguration();
config.ipAddress = mIpAddress;
config.gateway = mGateway;
- for (InetAddress server : mDnsServers) {
- config.dnsServers.add(server);
+ if (mDnsServers != null) {
+ for (InetAddress server : mDnsServers) {
+ config.dnsServers.add(server);
+ }
}
config.domains = mDomains;
return config;
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index 4e88149b8095..aa0f6220036c 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -94,7 +94,8 @@ public class MultinetworkPolicyTracker {
}
};
- TelephonyManager.from(ctx).listen(new PhoneStateListener(handler.getLooper()) {
+ ctx.getSystemService(TelephonyManager.class).listen(
+ new PhoneStateListener(handler.getLooper()) {
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
mActiveSubId = subId;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a856975e2501..ec3919997603 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -932,8 +932,14 @@ public class Binder implements IBinder {
}
int result = -1;
- try {
- result = handleShellCommand(in, out, err, args);
+ try (ParcelFileDescriptor inPfd = ParcelFileDescriptor.dup(in);
+ ParcelFileDescriptor outPfd = ParcelFileDescriptor.dup(out);
+ ParcelFileDescriptor errPfd = ParcelFileDescriptor.dup(err)) {
+ result = handleShellCommand(inPfd, outPfd, errPfd, args);
+ } catch (IOException e) {
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(err));
+ pw.println("dup() failed: " + e.getMessage());
+ pw.flush();
} finally {
resultReceiver.send(result, null);
}
@@ -954,9 +960,10 @@ public class Binder implements IBinder {
* @hide
*/
// @SystemApi TODO Make it a system API.
- protected int handleShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- @NonNull FileDescriptor err, @NonNull String[] args) {
- FileOutputStream ferr = new FileOutputStream(err);
+ protected int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ FileOutputStream ferr = new FileOutputStream(err.getFileDescriptor());
PrintWriter pw = new FastPrintWriter(ferr);
pw.println("No shell command implementation.");
pw.flush();
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 733079691a15..89ccef64d4cd 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -161,7 +161,7 @@ public class Build {
try {
Application application = ActivityThread.currentApplication();
String callingPackage = application != null ? application.getPackageName() : null;
- return service.getSerialForPackage(callingPackage);
+ return service.getSerialForPackage(callingPackage, null);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -240,13 +240,6 @@ public class Build {
public static final String RELEASE = getString("ro.build.version.release");
/**
- * The version string we show to the user; may be {@link #RELEASE} or
- * {@link #CODENAME} if not a final release build.
- */
- @NonNull public static final String RELEASE_OR_CODENAME = getString(
- "ro.build.version.release_or_codename");
-
- /**
* The base OS build the product is based on.
*/
public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
diff --git a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl
index 87d358f50e74..d11aa0c0bac6 100644
--- a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl
+++ b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl
@@ -21,5 +21,5 @@ package android.os;
*/
interface IDeviceIdentifiersPolicyService {
String getSerial();
- String getSerialForPackage(in String callingPackage);
-} \ No newline at end of file
+ String getSerialForPackage(in String callingPackage, String callingFeatureId);
+}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e8cc73f43a3d..40048d9154de 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,77 +35,84 @@ interface IUserManager {
/*
* DO NOT MOVE - UserManager.h depends on the ordering of this function.
*/
- int getCredentialOwnerProfile(int userHandle);
- int getProfileParentId(int userHandle);
+ int getCredentialOwnerProfile(int userId);
+ int getProfileParentId(int userId);
/*
* END OF DO NOT MOVE
*/
- UserInfo createUser(in String name, int flags);
- UserInfo preCreateUser(int flags);
- UserInfo createProfileForUser(in String name, int flags, int userHandle,
+ UserInfo createUser(in String name, in String userType, int flags);
+ UserInfo preCreateUser(in String userType);
+ UserInfo createProfileForUser(in String name, in String userType, int flags, int userId,
in String[] disallowedPackages);
UserInfo createRestrictedProfile(String name, int parentUserHandle);
- void setUserEnabled(int userHandle);
+ void setUserEnabled(int userId);
void setUserAdmin(int userId);
- void evictCredentialEncryptionKey(int userHandle);
- boolean removeUser(int userHandle);
- boolean removeUserEvenWhenDisallowed(int userHandle);
- void setUserName(int userHandle, String name);
- void setUserIcon(int userHandle, in Bitmap icon);
- ParcelFileDescriptor getUserIcon(int userHandle);
+ void evictCredentialEncryptionKey(int userId);
+ boolean removeUser(int userId);
+ boolean removeUserEvenWhenDisallowed(int userId);
+ void setUserName(int userId, String name);
+ void setUserIcon(int userId, in Bitmap icon);
+ ParcelFileDescriptor getUserIcon(int userId);
UserInfo getPrimaryUser();
List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
- List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
+ List<UserInfo> getProfiles(int userId, boolean enabledOnly);
int[] getProfileIds(int userId, boolean enabledOnly);
- boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
- UserInfo getProfileParent(int userHandle);
- boolean isSameProfileGroup(int userHandle, int otherUserHandle);
+ boolean canAddMoreProfilesToUser(in String userType, int userId, boolean allowedToRemoveOne);
+ boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
+ UserInfo getProfileParent(int userId);
+ boolean isSameProfileGroup(int userId, int otherUserHandle);
+ String getUserTypeForUser(int userId);
@UnsupportedAppUsage
- UserInfo getUserInfo(int userHandle);
- String getUserAccount(int userHandle);
- void setUserAccount(int userHandle, String accountName);
- long getUserCreationTime(int userHandle);
+ UserInfo getUserInfo(int userId);
+ String getUserAccount(int userId);
+ void setUserAccount(int userId, String accountName);
+ long getUserCreationTime(int userId);
boolean isRestricted();
- boolean canHaveRestrictedProfile(int userHandle);
- int getUserSerialNumber(int userHandle);
+ boolean canHaveRestrictedProfile(int userId);
+ int getUserSerialNumber(int userId);
int getUserHandle(int userSerialNumber);
- int getUserRestrictionSource(String restrictionKey, int userHandle);
- List<UserManager.EnforcingUser> getUserRestrictionSources(String restrictionKey, int userHandle);
- Bundle getUserRestrictions(int userHandle);
- boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
- boolean hasUserRestriction(in String restrictionKey, int userHandle);
+ int getUserRestrictionSource(String restrictionKey, int userId);
+ List<UserManager.EnforcingUser> getUserRestrictionSources(String restrictionKey, int userId);
+ Bundle getUserRestrictions(int userId);
+ boolean hasBaseUserRestriction(String restrictionKey, int userId);
+ boolean hasUserRestriction(in String restrictionKey, int userId);
boolean hasUserRestrictionOnAnyUser(in String restrictionKey);
boolean isSettingRestrictedForUser(in String setting, int userId, in String value, int callingUid);
void addUserRestrictionsListener(IUserRestrictionsListener listener);
- void setUserRestriction(String key, boolean value, int userHandle);
- void setApplicationRestrictions(in String packageName, in Bundle restrictions,
- int userHandle);
+ void setUserRestriction(String key, boolean value, int userId);
+ void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userId);
Bundle getApplicationRestrictions(in String packageName);
- Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
+ Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
void setDefaultGuestRestrictions(in Bundle restrictions);
Bundle getDefaultGuestRestrictions();
- boolean markGuestForDeletion(int userHandle);
- boolean isQuietModeEnabled(int userHandle);
- void setSeedAccountData(int userHandle, in String accountName,
+ boolean markGuestForDeletion(int userId);
+ boolean isQuietModeEnabled(int userId);
+ void setSeedAccountData(int userId, in String accountName,
in String accountType, in PersistableBundle accountOptions, boolean persist);
String getSeedAccountName();
String getSeedAccountType();
PersistableBundle getSeedAccountOptions();
void clearSeedAccountData();
boolean someUserHasSeedAccount(in String accountName, in String accountType);
+ boolean isProfile(int userId);
boolean isManagedProfile(int userId);
boolean isDemoUser(int userId);
boolean isPreCreated(int userId);
- UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
- in String[] disallowedPackages);
+ UserInfo createProfileForUserEvenWhenDisallowed(in String name, in String userType, int flags,
+ int userId, in String[] disallowedPackages);
boolean isUserUnlockingOrUnlocked(int userId);
- int getManagedProfileBadge(int userId);
+ int getUserIconBadgeResId(int userId);
+ int getUserBadgeResId(int userId);
+ int getUserBadgeNoBackgroundResId(int userId);
+ int getUserBadgeLabelResId(int userId);
+ int getUserBadgeColorResId(int userId);
+ boolean hasBadge(int userId);
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
- boolean isUserNameSet(int userHandle);
+ boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles();
- boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userHandle, in IntentSender target);
+ boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target);
String getUserName();
long getUserStartRealtime();
long getUserUnlockRealtime();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dd1f8c31ebb1..f18b4db6dc51 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -426,9 +426,15 @@ public final class PowerManager {
public static final int GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8;
/**
+ * Go to sleep reason code: Going to sleep due to user inattentiveness.
* @hide
*/
- public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_FORCE_SUSPEND;
+ public static final int GO_TO_SLEEP_REASON_INATTENTIVE = 9;
+
+ /**
+ * @hide
+ */
+ public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_INATTENTIVE;
/**
* @hide
@@ -444,6 +450,7 @@ public final class PowerManager {
case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button";
case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
+ case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
default: return Integer.toString(sleepReason);
}
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 48fc2a6bf449..e3f6e120d9c6 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -888,7 +888,7 @@ public class RecoverySystem {
}
List<SubscriptionInfo> invisibleSubs = new ArrayList<>();
for (SubscriptionInfo sub : availableSubs) {
- if (sub.isEmbedded() && !subscriptionManager.isSubscriptionVisible(sub)) {
+ if (sub.isEmbedded() && sub.getGroupUuid() != null && sub.isOpportunistic()) {
invisibleSubs.add(sub);
}
}
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 2e673a857a93..10ef27952df4 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -16,7 +16,7 @@
package android.os;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.NonNull;
import android.util.AndroidException;
/**
@@ -37,7 +37,15 @@ public class RemoteException extends AndroidException {
super(message, cause, enableSuppression, writableStackTrace);
}
- /** {@hide} */
+ /**
+ * Rethrow this as an unchecked runtime exception.
+ * <p>
+ * Apps making calls into other processes may end up persisting internal
+ * state or making security decisions based on the perceived success or
+ * failure of a call, or any default values returned. For this reason, we
+ * want to strongly throw when there was trouble with the transaction.
+ */
+ @NonNull
public RuntimeException rethrowAsRuntimeException() {
throw new RuntimeException(this);
}
@@ -52,10 +60,8 @@ public class RemoteException extends AndroidException {
* state or making security decisions based on the perceived success or
* failure of a call, or any default values returned. For this reason, we
* want to strongly throw when there was trouble with the transaction.
- *
- * @hide
*/
- @UnsupportedAppUsage
+ @NonNull
public RuntimeException rethrowFromSystemServer() {
if (this instanceof DeadObjectException) {
throw new RuntimeException(new DeadSystemException());
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b096049ebb68..bb62eb25b1a4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -18,6 +18,8 @@ package android.os;
import android.Manifest;
import android.accounts.AccountManager;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -76,6 +78,57 @@ public class UserManager {
private final Context mContext;
private Boolean mIsManagedProfileCached;
+ private Boolean mIsProfileCached;
+
+ /**
+ * User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user.
+ * This type of user cannot be created; it can only pre-exist on first boot.
+ * @hide
+ */
+ public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
+
+ /**
+ * User type representing a regular non-profile non-{@link UserHandle#USER_SYSTEM system} human
+ * user.
+ * This is sometimes called an ordinary 'secondary user'.
+ * @hide
+ */
+ public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
+
+ /**
+ * User type representing a guest user that may be transient.
+ * @hide
+ */
+ public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
+
+ /**
+ * User type representing a user for demo purposes only, which can be removed at any time.
+ * @hide
+ */
+ public static final String USER_TYPE_FULL_DEMO = "android.os.usertype.full.DEMO";
+
+ /**
+ * User type representing a "restricted profile" user, which is a full user that is subject to
+ * certain restrictions from a parent user. Note, however, that it is NOT technically a profile.
+ * @hide
+ */
+ public static final String USER_TYPE_FULL_RESTRICTED = "android.os.usertype.full.RESTRICTED";
+
+ /**
+ * User type representing a managed profile, which is a profile that is to be managed by a
+ * device policy controller (DPC).
+ * The intended purpose is for work profiles, which are managed by a corporate entity.
+ * @hide
+ */
+ public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
+
+ /**
+ * User type representing a {@link UserHandle#USER_SYSTEM system} user that is <b>not</b> a
+ * human user.
+ * This type of user cannot be created; it can only pre-exist on first boot.
+ * @hide
+ */
+ public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
/**
* @hide
@@ -1306,8 +1359,11 @@ public class UserManager {
mContext.getContentResolver(),
Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0;
boolean isSystemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM);
- boolean inCall = TelephonyManager.getDefault().getCallState()
- != TelephonyManager.CALL_STATE_IDLE;
+ boolean inCall = false;
+ TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+ if (telephonyManager != null) {
+ inCall = telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
+ }
boolean isUserSwitchDisallowed = hasUserRestriction(DISALLOW_USER_SWITCH);
return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall
&& !isUserSwitchDisallowed;
@@ -1479,6 +1535,79 @@ public class UserManager {
}
/**
+ * Returns the calling user's user type.
+ *
+ * // TODO(b/142482943): Decide on the appropriate permission requirements.
+ *
+ * @return the name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @hide
+ */
+ public @NonNull String getUserType() {
+ try {
+ return mService.getUserTypeForUser(UserHandle.myUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the given user's user type.
+ *
+ * // TODO(b/142482943): Decide on the appropriate permission requirements.
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+ * must be in the same profile group of specified user.
+ *
+ * @param userHandle the user handle of the user whose type is being requested.
+ * @return the name of the user's user type, e.g. {@link UserManager#USER_TYPE_PROFILE_MANAGED},
+ * or {@code null} if there is no such user.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ public @Nullable String getUserTypeForUser(@NonNull UserHandle userHandle) {
+ try {
+ return mService.getUserTypeForUser(userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the user type is a
+ * {@link UserManager#USER_TYPE_PROFILE_MANAGED managed profile}.
+ * @hide
+ */
+ public static boolean isUserTypeManagedProfile(String userType) {
+ return USER_TYPE_PROFILE_MANAGED.equals(userType);
+ }
+
+ /**
+ * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_GUEST guest user}.
+ * @hide
+ */
+ public static boolean isUserTypeGuest(String userType) {
+ return USER_TYPE_FULL_GUEST.equals(userType);
+ }
+
+ /**
+ * Returns whether the user type is a
+ * {@link UserManager#USER_TYPE_FULL_RESTRICTED restricted user}.
+ * @hide
+ */
+ public static boolean isUserTypeRestricted(String userType) {
+ return USER_TYPE_FULL_RESTRICTED.equals(userType);
+ }
+
+ /**
+ * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_DEMO demo user}.
+ * @hide
+ */
+ public static boolean isUserTypeDemo(String userType) {
+ return USER_TYPE_FULL_DEMO.equals(userType);
+ }
+
+ /**
* @hide
* @deprecated Use {@link #isRestrictedProfile()}
*/
@@ -1589,6 +1718,48 @@ public class UserManager {
}
/**
+ * Checks if the calling app is running in a profile.
+ *
+ * @return whether the caller is in a profile.
+ * @hide
+ */
+ public boolean isProfile() {
+ // No need for synchronization. Once it becomes non-null, it'll be non-null forever.
+ // Worst case we might end up calling the AIDL method multiple times but that's fine.
+ if (mIsProfileCached != null) {
+ return mIsProfileCached;
+ }
+ try {
+ mIsProfileCached = mService.isProfile(UserHandle.myUserId());
+ return mIsProfileCached;
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Checks if the specified user is a profile.
+ *
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+ * must be in the same profile group of specified user.
+ *
+ * @return whether the specified user is a profile.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ public boolean isProfile(@UserIdInt int userId) {
+ if (userId == UserHandle.myUserId()) {
+ return isProfile();
+ }
+ try {
+ return mService.isProfile(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ /**
* Checks if the calling app is running in a managed profile.
*
* @return whether the caller is in a managed profile.
@@ -1612,7 +1783,8 @@ public class UserManager {
/**
* Checks if the specified user is a managed profile.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission, otherwise the caller
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
* must be in the same profile group of specified user.
*
* @return whether the specified user is a managed profile.
@@ -1632,23 +1804,6 @@ public class UserManager {
}
/**
- * Gets badge for a managed profile.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission, otherwise the caller
- * must be in the same profile group of specified user.
- *
- * @return which badge to use for the managed profile badge id will be less than
- * UserManagerService.getMaxManagedProfiles()
- * @hide
- */
- public int getManagedProfileBadge(@UserIdInt int userId) {
- try {
- return mService.getManagedProfileBadge(userId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /**
* Checks if the calling app is running as an ephemeral user.
*
* @return whether the caller is an ephemeral user.
@@ -2120,23 +2275,44 @@ public class UserManager {
/**
* Creates a user with the specified name and options. For non-admin users, default user
+ * restrictions are going to be applied.
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
+ * @param name the user's name
+ * @param flags UserInfo flags that identify the type of user and other properties.
+ * @see UserInfo
+ *
+ * @return the UserInfo object for the created user, or null if the user could not be created.
+ * @throws IllegalArgumentException if flags do not correspond to a valid user type.
+ * @deprecated Use {@link #createUser(String, String, int)} instead.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ @Deprecated
+ public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
+ return createUser(name, UserInfo.getDefaultUserType(flags), flags);
+ }
+
+ /**
+ * Creates a user with the specified name and options. For non-admin users, default user
* restrictions will be applied.
*
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @param name the user's name
- * @param flags UserInfo flags that identify the type of user and other properties.
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
+ * @param flags UserInfo flags that specify user properties.
* @see UserInfo
*
* @return the UserInfo object for the created user, or {@code null} if the user could not be
* created.
* @hide
*/
- @UnsupportedAppUsage
- public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
+ public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
+ @UserInfoFlag int flags) {
UserInfo user = null;
try {
- user = mService.createUser(name, flags);
+ user = mService.createUser(name, userType, flags);
// TODO: Keep this in sync with
// UserManagerService.LocalService.createUserEvenWhenDisallowed
if (user != null && !user.isAdmin() && !user.isDemo()) {
@@ -2150,19 +2326,17 @@ public class UserManager {
}
/**
- * Pre-creates a user with the specified name and options. For non-admin users, default user
+ * Pre-creates a user of the specified type. For non-admin users, default user
* restrictions will be applied.
*
* <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
* at the first boot, so they when the "real" user is created (for example,
- * by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes
- * less time.
+ * by {@link #createUser(String, String, int)} or {@link #createGuest(Context, String)}), it
+ * takes less time.
*
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
- * @param flags UserInfo flags that identify the type of user and other properties.
- * @see UserInfo
- *
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
* @return the UserInfo object for the created user, or {@code null} if the user could not be
* created.
*
@@ -2171,9 +2345,9 @@ public class UserManager {
*
* @hide
*/
- public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
+ public @Nullable UserInfo preCreateUser(@NonNull String userType) {
try {
- return mService.preCreateUser(flags);
+ return mService.preCreateUser(userType);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2188,7 +2362,7 @@ public class UserManager {
public UserInfo createGuest(Context context, String name) {
UserInfo guest = null;
try {
- guest = mService.createUser(name, UserInfo.FLAG_GUEST);
+ guest = mService.createUser(name, USER_TYPE_FULL_GUEST, 0);
if (guest != null) {
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
@@ -2202,6 +2376,7 @@ public class UserManager {
/**
* Creates a user with the specified name and options as a profile of another user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * The type of profile must be specified using the given flags.
*
* @param name the user's name
* @param flags flags that identify the type of user and other properties.
@@ -2209,20 +2384,44 @@ public class UserManager {
*
* @return the {@link UserInfo} object for the created user, or null if the user
* could not be created.
+ * @throws IllegalArgumentException if flags do not correspond to a valid user type.
+ * @deprecated Use {@link #createProfileForUser(String, String, int, int)} instead.
* @hide
*/
@UnsupportedAppUsage
- public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId) {
- return createProfileForUser(name, flags, userId, null);
+ @Deprecated
+ public UserInfo createProfileForUser(String name, @UserInfoFlag int flags,
+ @UserIdInt int userId) {
+ return createProfileForUser(name, UserInfo.getDefaultUserType(flags), flags,
+ userId, null);
}
/**
- * Version of {@link #createProfileForUser(String, int, int)} that allows you to specify
+ * Creates a user with the specified name and options as a profile of another user.
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
+ * @param name the user's name
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @param flags UserInfo flags that specify user properties.
+ * @param userId new user will be a profile of this user.
+ *
+ * @return the {@link UserInfo} object for the created user, or null if the user
+ * could not be created.
+ * @hide
+ */
+ public UserInfo createProfileForUser(String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId) {
+ return createProfileForUser(name, userType, flags, userId, null);
+ }
+
+ /**
+ * Version of {@link #createProfileForUser(String, String, int, int)} that allows you to specify
* any packages that should not be installed in the new profile by default, these packages can
* still be installed later by the user if needed.
*
* @param name the user's name
- * @param flags flags that identify the type of user and other properties.
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @param flags UserInfo flags that specify user properties.
* @param userId new user will be a profile of this user.
* @param disallowedPackages packages that will not be installed in the profile being created.
*
@@ -2230,28 +2429,29 @@ public class UserManager {
* could not be created.
* @hide
*/
- public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId,
- String[] disallowedPackages) {
+ public UserInfo createProfileForUser(String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
try {
- return mService.createProfileForUser(name, flags, userId, disallowedPackages);
+ return mService.createProfileForUser(name, userType, flags, userId, disallowedPackages);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
- * Similar to {@link #createProfileForUser(String, int, int, String[])}
+ * Similar to {@link #createProfileForUser(String, String, int, int, String[])}
* except bypassing the checking of {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
- * @see #createProfileForUser(String, int, int, String[])
+ * @see #createProfileForUser(String, String, int, int, String[])
* @hide
*/
- public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags,
- @UserIdInt int userId, String[] disallowedPackages) {
+ public UserInfo createProfileForUserEvenWhenDisallowed(String name,
+ @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
+ String[] disallowedPackages) {
try {
- return mService.createProfileForUserEvenWhenDisallowed(name, flags, userId,
- disallowedPackages);
+ return mService.createProfileForUserEvenWhenDisallowed(name, userType, flags,
+ userId, disallowedPackages);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2595,6 +2795,8 @@ public class UserManager {
* @hide
*/
public boolean canAddMoreUsers() {
+ // TODO(b/142482943): UMS has different logic, excluding Demo and Profile from counting. Why
+ // not here? The logic is inconsistent. See UMS.canAddMoreManagedProfiles
final List<UserInfo> users = getUsers(true);
final int totalUserCount = users.size();
int aliveUserCount = 0;
@@ -2625,6 +2827,22 @@ public class UserManager {
}
/**
+ * Checks whether it's possible to add more profiles of the given type to the given user.
+ *
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @return true if more profiles can be added, false if limit has been reached.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean canAddMoreProfilesToUser(@NonNull String userType, @UserIdInt int userId) {
+ try {
+ return mService.canAddMoreProfilesToUser(userType, userId, false);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns list of the profiles of userId including userId itself.
* Note that this returns both enabled and not enabled profiles. See
* {@link #getEnabledProfiles(int)} if you need only the enabled ones.
@@ -2858,8 +3076,112 @@ public class UserManager {
}
/**
- * If the target user is a managed profile of the calling user or the caller
- * is itself a managed profile, then this returns a badged copy of the given
+ * Returns whether the given user has a badge (generally to put on profiles' icons).
+ *
+ * @param userId userId of the user in question
+ * @return true if the user's icons should display a badge; false otherwise.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public boolean hasBadge(@UserIdInt int userId) {
+ if (!isProfile(userId)) {
+ // Since currently only profiles actually have badges, we can do this optimization.
+ return false;
+ }
+ try {
+ return mService.hasBadge(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the calling app's user has a badge (generally to put on profiles' icons).
+ *
+ * @return true if the user's icons should display a badge; false otherwise.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public boolean hasBadge() {
+ return hasBadge(UserHandle.myUserId());
+ }
+
+ /**
+ * Returns the badge color for the given user (generally to color a profile's icon's badge).
+ *
+ * <p>To check whether a badge color is expected for the user, first call {@link #hasBadge}.
+ *
+ * @return the color (not the resource ID) to be used for the user's badge
+ * @throws Resources.NotFoundException if no valid badge color exists for this user
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @ColorInt int getUserBadgeColor(@UserIdInt int userId) {
+ try {
+ final int resourceId = mService.getUserBadgeColorResId(userId);
+ return Resources.getSystem().getColor(resourceId, null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the Resource ID of the user's icon badge.
+ *
+ * @return the Resource ID of the user's icon badge if it has one; otherwise
+ * {@link Resources#ID_NULL}.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
+ try {
+ return mService.getUserIconBadgeResId(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the Resource ID of the user's badge.
+ *
+ * @return the Resource ID of the user's badge if it has one; otherwise
+ * {@link Resources#ID_NULL}.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
+ try {
+ return mService.getUserBadgeResId(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the Resource ID of the user's badge without a background.
+ *
+ * @return the Resource ID of the user's no-background badge if it has one; otherwise
+ * {@link Resources#ID_NULL}.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
+ try {
+ return mService.getUserBadgeNoBackgroundResId(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * If the target user is a profile of the calling user or the caller
+ * is itself a profile, then this returns a badged copy of the given
* icon to be able to distinguish it from the original icon. For badging an
* arbitrary drawable use {@link #getBadgedDrawableForUser(
* android.graphics.drawable.Drawable, UserHandle, android.graphics.Rect, int)}.
@@ -2880,8 +3202,8 @@ public class UserManager {
}
/**
- * If the target user is a managed profile of the calling user or the caller
- * is itself a managed profile, then this returns a badged copy of the given
+ * If the target user is a profile of the calling user or the caller
+ * is itself a profile, then this returns a badged copy of the given
* drawable allowing the user to distinguish it from the original drawable.
* The caller can specify the location in the bounds of the drawable to be
* badged where the badge should be applied as well as the density of the
@@ -2911,11 +3233,15 @@ public class UserManager {
}
/**
- * If the target user is a managed profile of the calling user or the caller
- * is itself a managed profile, then this returns a copy of the label with
+ * If the target user is a profile of the calling user or the caller
+ * is itself a profile, then this returns a copy of the label with
* badging for accessibility services like talkback. E.g. passing in "Email"
* and it might return "Work Email" for Email in the work profile.
*
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+ * must be in the same profile group of specified user.
+ *
* @param label The label to change.
* @param user The target user.
* @return A label that combines the original label and a badge as
@@ -2923,7 +3249,16 @@ public class UserManager {
* @removed
*/
public CharSequence getBadgedLabelForUser(CharSequence label, UserHandle user) {
- return mContext.getPackageManager().getUserBadgedLabel(label, user);
+ final int userId = user.getIdentifier();
+ if (!hasBadge(userId)) {
+ return label;
+ }
+ try {
+ final int resourceId = mService.getUserBadgeLabelResId(userId);
+ return Resources.getSystem().getString(resourceId, label);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 0e00d5e9b7bf..4c92c28c1bfa 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -101,6 +101,19 @@ public class DynamicSystemManager {
}
}
/**
+ * Start DynamicSystem installation.
+ *
+ * @return true if the call succeeds
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+ public boolean startInstallation() {
+ try {
+ return mService.startInstallation();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+ /**
* Start DynamicSystem installation. This call may take an unbounded amount of time. The caller
* may use another thread to call the getStartProgress() to get the progress.
*
@@ -112,9 +125,9 @@ public class DynamicSystemManager {
* true.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public Session startInstallation(String name, long size, boolean readOnly) {
+ public Session createPartition(String name, long size, boolean readOnly) {
try {
- if (mService.startInstallation(name, size, readOnly)) {
+ if (mService.createPartition(name, size, readOnly)) {
return new Session();
} else {
return null;
@@ -123,7 +136,18 @@ public class DynamicSystemManager {
throw new RuntimeException(e.toString());
}
}
-
+ /**
+ * Finish a previously started installation. Installations without a cooresponding
+ * finishInstallation() will be cleaned up during device boot.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+ public boolean finishInstallation() {
+ try {
+ return mService.finishInstallation();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
/**
* Query the progress of the current installation operation. This can be called while the
* installation is in progress.
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index 75f67854743f..69cbab2c68ad 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -21,15 +21,26 @@ import android.gsi.GsiProgress;
interface IDynamicSystemService
{
/**
- * Start DynamicSystem installation. This call may take 60~90 seconds. The caller
+ * Start DynamicSystem installation.
+ * @return true if the call succeeds
+ */
+ boolean startInstallation();
+
+ /**
+ * Create a DSU partition. This call may take 60~90 seconds. The caller
* may use another thread to call the getStartProgress() to get the progress.
- *
* @param name The DSU partition name
* @param size Size of the DSU image in bytes
* @param readOnly True if this partition is readOnly
* @return true if the call succeeds
*/
- boolean startInstallation(@utf8InCpp String name, long size, boolean readOnly);
+ boolean createPartition(@utf8InCpp String name, long size, boolean readOnly);
+
+ /**
+ * Finish a previously started installation. Installations without
+ * a cooresponding finishInstallation() will be cleaned up during device boot.
+ */
+ boolean finishInstallation();
/**
* Query the progress of the current installation operation. This can be called while
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ac53f1b27680..3ac7deb0db08 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7707,6 +7707,20 @@ public final class Settings {
public static final String SLEEP_TIMEOUT = "sleep_timeout";
/**
+ * The timeout in milliseconds before the device goes to sleep due to user inattentiveness,
+ * even if the system is holding wakelocks. It should generally be longer than {@code
+ * config_attentiveWarningDuration}, as otherwise the device will show the attentive
+ * warning constantly. Small timeouts are discouraged, as they will cause the device to
+ * go to sleep quickly after waking up.
+ * <p>
+ * Use -1 to disable this timeout.
+ * </p>
+ *
+ * @hide
+ */
+ public static final String ATTENTIVE_TIMEOUT = "attentive_timeout";
+
+ /**
* Controls whether double tap to wake is enabled.
* @hide
*/
@@ -14085,6 +14099,77 @@ public final class Settings {
"android.settings.panel.action.VOLUME";
}
+ /**
+ * Activity Action: Show setting page to process the addition of Wi-Fi networks to the user's
+ * saved network list. The app should send a new intent with an extra that holds a maximum of
+ * five {@link android.net.wifi.WifiConfiguration} that specify credentials for the networks to
+ * be added to the user's database. The Intent should be sent via the {@link
+ * android.app.Activity#startActivityForResult(Intent, int)} API.
+ * <p>
+ * Note: The app sending the Intent to add the credentials doesn't get any ownership over the
+ * newly added network(s). For the Wi-Fi stack, these networks will look like the user
+ * manually added them from the Settings UI.
+ * <p>
+ * Input: The app should put parcelable array list to
+ * {@link android.net.wifi.WifiConfiguration} into the
+ * {@link #EXTRA_WIFI_CONFIGURATION_LIST} extra.
+ * <p>
+ * Output: After {@link android.app.Activity#startActivityForResult(Intent, int)}, the
+ * callback {@link android.app.Activity#onActivityResult(int, int, Intent)} will have a
+ * result code {@link android.app.Activity#RESULT_OK} to indicate user pressed the save
+ * button to save the networks or {@link android.app.Activity#RESULT_CANCELED} to indicate
+ * that the user rejected the request. Additionally, an integer array list, stored in
+ * {@link #EXTRA_WIFI_CONFIGURATION_RESULT_LIST}, will indicate the process result of
+ * each network.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_WIFI_ADD_NETWORKS =
+ "android.settings.WIFI_ADD_NETWORKS";
+
+ /**
+ * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates all the
+ * {@link android.net.wifi.WifiConfiguration} that would be saved.
+ */
+ public static final String EXTRA_WIFI_CONFIGURATION_LIST =
+ "android.provider.extra.WIFI_CONFIGURATION_LIST";
+
+ /**
+ * A bundle extra of the result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that
+ * indicates the action result of the saved {@link android.net.wifi.WifiConfiguration}. It's
+ * value of AddWifiResult interface, and will be 1:1 mapping to the element in {@link
+ * #EXTRA_WIFI_CONFIGURATION_LIST}.
+ */
+ public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST =
+ "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ADD_WIFI_RESULT_"}, value = {
+ ADD_WIFI_RESULT_SUCCESS,
+ ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED,
+ ADD_WIFI_RESULT_ALREADY_EXISTS
+ })
+ public @interface AddWifiResult {
+ }
+
+ /**
+ * A result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that saving or updating the
+ * corresponding Wi-Fi network was successful.
+ */
+ public static final int ADD_WIFI_RESULT_SUCCESS = 0;
+
+ /**
+ * A result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that saving the corresponding
+ * Wi-Fi network failed.
+ */
+ public static final int ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED = 1;
+
+ /**
+ * A result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates the Wi-Fi network
+ * already exists.
+ */
+ public static final int ADD_WIFI_RESULT_ALREADY_EXISTS = 2;
+
private static final String[] PM_WRITE_SETTINGS = {
android.Manifest.permission.WRITE_SETTINGS
};
diff --git a/telephony/java/android/telephony/Rlog.java b/core/java/android/telephony/Rlog.java
index cdab2dc54dd2..cdab2dc54dd2 100644
--- a/telephony/java/android/telephony/Rlog.java
+++ b/core/java/android/telephony/Rlog.java
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 64d612405c34..a0800910d1b3 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -15,7 +15,6 @@
*/
package android.telephony;
-import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Context;
@@ -23,8 +22,6 @@ import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.Annotation.ApnType;
@@ -37,19 +34,12 @@ import android.telephony.Annotation.PreciseCallStates;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
-import android.telephony.CallQuality;
-import android.telephony.CellInfo;
-import android.telephony.DisconnectCause;
-import android.telephony.PhoneCapability;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
import android.util.Log;
-import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+import com.android.internal.telephony.ITelephonyRegistry;
import java.util.HashMap;
import java.util.List;
@@ -120,7 +110,8 @@ public class TelephonyRegistryManager {
};
mSubscriptionChangedListenerMap.put(listener, callback);
try {
- sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(), callback);
+ sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(),
+ mContext.getFeatureId(), callback);
} catch (RemoteException ex) {
// system server crash
}
@@ -179,7 +170,7 @@ public class TelephonyRegistryManager {
mOpportunisticSubscriptionChangedListenerMap.put(listener, callback);
try {
sRegistry.addOnOpportunisticSubscriptionsChangedListener(mContext.getOpPackageName(),
- callback);
+ mContext.getFeatureId(), callback);
} catch (RemoteException ex) {
// system server crash
}
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index df54209bc043..993bbe89fe81 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -663,11 +663,8 @@ public class Linkify {
private static void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s,
@Nullable Context context) {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
- final TelephonyManager tm = (context == null)
- ? TelephonyManager.getDefault()
- : TelephonyManager.from(context);
Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
- tm.getSimCountryIso().toUpperCase(Locale.US),
+ TelephonyManager.getDefaultSimCountryIso().toUpperCase(Locale.US),
Leniency.POSSIBLE, Long.MAX_VALUE);
for (PhoneNumberMatch match : matches) {
LinkSpec spec = new LinkSpec();
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
index d90b65e22171..d86ebf3a44b3 100644
--- a/core/java/android/util/IconDrawableFactory.java
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -26,8 +26,6 @@ import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.internal.annotations.VisibleForTesting;
-
/**
* Utility class to load app drawables with appropriate badging.
*
@@ -78,10 +76,10 @@ public class IconDrawableFactory {
com.android.internal.R.drawable.ic_instant_icon_badge_bolt,
badgeColor);
}
- if (mUm.isManagedProfile(userId)) {
+ if (mUm.hasBadge(userId)) {
icon = mLauncherIcons.getBadgedDrawable(icon,
- com.android.internal.R.drawable.ic_corp_icon_badge_case,
- getUserBadgeColor(mUm, userId));
+ mUm.getUserIconBadgeResId(userId),
+ mUm.getUserBadgeColor(userId));
}
return icon;
}
@@ -93,23 +91,6 @@ public class IconDrawableFactory {
return mLauncherIcons.wrapIconDrawableWithShadow(icon);
}
- // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
- @VisibleForTesting
- public static final int[] CORP_BADGE_COLORS = new int[] {
- com.android.internal.R.color.profile_badge_1,
- com.android.internal.R.color.profile_badge_2,
- com.android.internal.R.color.profile_badge_3
- };
-
- public static int getUserBadgeColor(UserManager um, @UserIdInt int userId) {
- int badge = um.getManagedProfileBadge(userId);
- if (badge < 0) {
- badge = 0;
- }
- int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
- return Resources.getSystem().getColor(resourceId, null);
- }
-
@UnsupportedAppUsage
public static IconDrawableFactory newInstance(Context context) {
return new IconDrawableFactory(context, true);
diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java
index 8501eb5883d5..e652e17bceb3 100644
--- a/core/java/android/util/LauncherIcons.java
+++ b/core/java/android/util/LauncherIcons.java
@@ -106,9 +106,11 @@ public final class LauncherIcons {
Resources overlayableRes =
ActivityThread.currentActivityThread().getApplication().getResources();
+ // ic_corp_icon_badge_shadow is not work-profile-specific.
Drawable badgeShadow = overlayableRes.getDrawable(
com.android.internal.R.drawable.ic_corp_icon_badge_shadow);
+ // ic_corp_icon_badge_color is not work-profile-specific.
Drawable badgeColor = overlayableRes.getDrawable(
com.android.internal.R.drawable.ic_corp_icon_badge_color)
.getConstantState().newDrawable().mutate();
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 6a099d57145b..9e914d4e7d41 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -23,6 +23,7 @@ import android.app.AppGlobals;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Point;
import android.os.Build;
import android.os.RemoteException;
import android.provider.Settings;
@@ -397,7 +398,11 @@ public class ViewConfiguration {
mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
// Size of the screen in bytes, in ARGB_8888 format
- mMaximumDrawingCacheSize = 4 * metrics.heightPixels * metrics.widthPixels;
+ final WindowManager win = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+ final Display display = win.getDefaultDisplay();
+ final Point size = new Point();
+ display.getRealSize(size);
+ mMaximumDrawingCacheSize = 4 * size.x * size.y;
mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);
mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);
@@ -837,7 +842,6 @@ public class ViewConfiguration {
* The maximum drawing cache size expressed in bytes.
*
* @return the maximum size of View's drawing cache expressed in bytes
- *
*/
public int getScaledMaximumDrawingCacheSize() {
return mMaximumDrawingCacheSize;
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 36daa5c57700..a62ba63b64e4 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -415,6 +415,11 @@ public class ViewDebug {
private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
private static final String REMOTE_COMMAND_DUMP = "DUMP";
private static final String REMOTE_COMMAND_DUMP_THEME = "DUMP_THEME";
+ /**
+ * Similar to REMOTE_COMMAND_DUMP but uses ViewHierarchyEncoder instead of flat text
+ * @hide
+ */
+ public static final String REMOTE_COMMAND_DUMP_ENCODED = "DUMP_ENCODED";
private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
private static final String REMOTE_PROFILE = "PROFILE";
@@ -527,7 +532,6 @@ public class ViewDebug {
@UnsupportedAppUsage
static void dispatchCommand(View view, String command, String parameters,
OutputStream clientStream) throws IOException {
-
// Paranoid but safe...
view = view.getRootView();
@@ -535,6 +539,8 @@ public class ViewDebug {
dump(view, false, true, clientStream);
} else if (REMOTE_COMMAND_DUMP_THEME.equalsIgnoreCase(command)) {
dumpTheme(view, clientStream);
+ } else if (REMOTE_COMMAND_DUMP_ENCODED.equalsIgnoreCase(command)) {
+ dumpEncoded(view, clientStream);
} else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
captureLayers(view, new DataOutputStream(clientStream));
} else {
@@ -1198,6 +1204,18 @@ public class ViewDebug {
encoder.endStream();
}
+ private static void dumpEncoded(@NonNull final View view, @NonNull OutputStream out)
+ throws IOException {
+ ByteArrayOutputStream baOut = new ByteArrayOutputStream();
+
+ final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(baOut);
+ encoder.addProperty("window:left", view.mAttachInfo.mWindowLeft);
+ encoder.addProperty("window:top", view.mAttachInfo.mWindowTop);
+ view.encode(encoder);
+ encoder.endStream();
+ out.write(baOut.toByteArray());
+ }
+
/**
* Dumps the theme attributes from the given View.
* @hide
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index b33fdbbf8a96..a38955540927 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4590,10 +4590,6 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param label The label for the new AccessibilityAction.
*/
public AccessibilityAction(int actionId, @Nullable CharSequence label) {
- if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) != 1) {
- throw new IllegalArgumentException("Invalid standard action id");
- }
-
mActionId = actionId;
mLabel = label;
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 090e19f91e38..ae2fb8ebc24a 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -211,6 +211,10 @@ public class BaseInputConnection implements InputConnection {
* If this is greater than the number of existing characters between the cursor and
* the end of the text, then this method does not fail but deletes all the characters in
* that range.
+ *
+ * @return {@code true} when selected text is deleted, {@code false} when either the
+ * selection is invalid or not yet attached (i.e. selection start or end is -1),
+ * or the editable text is {@code null}.
*/
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
@@ -229,6 +233,12 @@ public class BaseInputConnection implements InputConnection {
b = tmp;
}
+ // Skip when the selection is not yet attached.
+ if (a == -1 || b == -1) {
+ endBatchEdit();
+ return false;
+ }
+
// Ignore the composing text.
int ca = getComposingSpanStart(content);
int cb = getComposingSpanEnd(content);
@@ -247,8 +257,12 @@ public class BaseInputConnection implements InputConnection {
if (beforeLength > 0) {
int start = a - beforeLength;
if (start < 0) start = 0;
- content.delete(start, a);
- deleted = a - start;
+
+ final int numDeleteBefore = a - start;
+ if (a >= 0 && numDeleteBefore > 0) {
+ content.delete(start, a);
+ deleted = numDeleteBefore;
+ }
}
if (afterLength > 0) {
@@ -257,7 +271,10 @@ public class BaseInputConnection implements InputConnection {
int end = b + afterLength;
if (end > content.length()) end = content.length();
- content.delete(b, end);
+ final int numDeleteAfter = end - b;
+ if (b >= 0 && numDeleteAfter > 0) {
+ content.delete(b, end);
+ }
}
endBatchEdit();
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e81db16f0743..2650a67652a5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -655,14 +655,14 @@ public final class InputMethodManager {
} catch (RemoteException e) {
}
}
- // Check focus again in case that "onWindowFocus" is called before
- // handling this message.
- if (mServedView != null && canStartInput(mServedView)) {
- if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
- final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
- : StartInputReason.DEACTIVATED_BY_IMMS;
- startInputInner(reason, null, 0, 0, 0);
- }
+ }
+ // Check focus again in case that "onWindowFocus" is called before
+ // handling this message.
+ if (mServedView != null && canStartInput(mServedView)) {
+ if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
+ final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+ : StartInputReason.DEACTIVATED_BY_IMMS;
+ startInputInner(reason, null, 0, 0, 0);
}
}
return;
@@ -1225,6 +1225,10 @@ public final class InputMethodManager {
*/
void clearBindingLocked() {
if (DEBUG) Log.v(TAG, "Clearing binding!");
+ if (mWindowFocusGainFuture != null) {
+ mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+ mWindowFocusGainFuture = null;
+ }
clearConnectionLocked();
setInputChannelLocked(null);
mBindSequence = -1;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 9fbd48ef302d..6c372e43c1c5 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -96,10 +96,7 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
@@ -118,6 +115,8 @@ import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ImageUtils;
+import com.android.internal.widget.GridLayoutManager;
+import com.android.internal.widget.RecyclerView;
import com.android.internal.widget.ResolverDrawerLayout;
import com.google.android.collect.Lists;
@@ -167,6 +166,7 @@ public class ChooserActivity extends ResolverActivity implements
@VisibleForTesting
public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250;
+ private static final int SINGLE_CELL_SPAN_SIZE = 1;
private boolean mIsAppPredictorComponentAvailable;
private AppPredictor mAppPredictor;
@@ -222,8 +222,9 @@ public class ChooserActivity extends ResolverActivity implements
private long mQueriedTargetServicesTimeMs;
private long mQueriedSharingShortcutsTimeMs;
+ private RecyclerView mRecyclerView;
private ChooserListAdapter mChooserListAdapter;
- private ChooserRowAdapter mChooserRowAdapter;
+ private ChooserGridAdapter mChooserGridAdapter;
private int mChooserRowServiceSpacing;
private int mCurrAvailableWidth = 0;
@@ -351,8 +352,8 @@ public class ChooserActivity extends ResolverActivity implements
Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
+ " within " + mImageLoadTimeoutMillis + "ms.");
collapseParentView();
- if (mChooserRowAdapter != null) {
- mChooserRowAdapter.hideContentPreview();
+ if (mChooserGridAdapter != null) {
+ mChooserGridAdapter.hideContentPreview();
}
mHideParentOnFail = false;
}
@@ -671,14 +672,14 @@ public class ChooserActivity extends ResolverActivity implements
final float chooserHeaderScrollElevation =
getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
- mAdapterView.setOnScrollListener(new AbsListView.OnScrollListener() {
- public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ public void onScrollStateChanged(RecyclerView view, int scrollState) {
}
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
+ public void onScrolled(RecyclerView view, int dx, int dy) {
if (view.getChildCount() > 0) {
- if (firstVisibleItem > 0 || view.getChildAt(0).getTop() < 0) {
+ View child = view.getLayoutManager().findViewByPosition(0);
+ if (child == null || child.getTop() < 0) {
chooserHeader.setElevation(chooserHeaderScrollElevation);
return;
}
@@ -847,10 +848,7 @@ public class ChooserActivity extends ResolverActivity implements
}
private ViewGroup displayContentPreview(@ContentPreviewType int previewType,
- Intent targetIntent, LayoutInflater layoutInflater, ViewGroup convertView,
- ViewGroup parent) {
- if (convertView != null) return convertView;
-
+ Intent targetIntent, LayoutInflater layoutInflater, ViewGroup parent) {
ViewGroup layout = null;
switch (previewType) {
@@ -1213,16 +1211,30 @@ public class ChooserActivity extends ResolverActivity implements
}
@Override
- public void onPrepareAdapterView(AbsListView adapterView, ResolverListAdapter adapter) {
- final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
+ public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) {
+ mRecyclerView = findViewById(R.id.resolver_list);
+ if (!isVisible) {
+ mRecyclerView.setVisibility(View.GONE);
+ return;
+ }
+ mRecyclerView.setVisibility(View.VISIBLE);
if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets),
TARGET_TYPE_DEFAULT);
}
- mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
- if (listView != null) {
- listView.setItemsCanFocus(true);
- }
+ mChooserGridAdapter = new ChooserGridAdapter(mChooserListAdapter);
+ GridLayoutManager glm = (GridLayoutManager) mRecyclerView.getLayoutManager();
+ glm.setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow());
+ glm.setSpanSizeLookup(
+ new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ return mChooserGridAdapter.getItemViewType(position)
+ == ChooserGridAdapter.VIEW_TYPE_NORMAL
+ ? SINGLE_CELL_SPAN_SIZE
+ : glm.getSpanCount();
+ }
+ });
}
@Override
@@ -1996,8 +2008,8 @@ public class ChooserActivity extends ResolverActivity implements
}
private void handleScroll(View view, int x, int y, int oldx, int oldy) {
- if (mChooserRowAdapter != null) {
- mChooserRowAdapter.handleScroll(view, y, oldy);
+ if (mChooserGridAdapter != null) {
+ mChooserGridAdapter.handleScroll(view, y, oldy);
}
}
@@ -2008,35 +2020,37 @@ public class ChooserActivity extends ResolverActivity implements
*/
private void handleLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
- if (mChooserRowAdapter == null || mAdapterView == null) {
+ if (mChooserGridAdapter == null || mRecyclerView == null) {
return;
}
final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight();
- if (mChooserRowAdapter.consumeLayoutRequest()
- || mChooserRowAdapter.calculateChooserTargetWidth(availableWidth)
- || mAdapterView.getAdapter() == null
+ if (mChooserGridAdapter.consumeLayoutRequest()
+ || mChooserGridAdapter.calculateChooserTargetWidth(availableWidth)
+ || mRecyclerView.getAdapter() == null
|| availableWidth != mCurrAvailableWidth) {
mCurrAvailableWidth = availableWidth;
- mAdapterView.setAdapter(mChooserRowAdapter);
+ mRecyclerView.setAdapter(mChooserGridAdapter);
+ ((GridLayoutManager) mRecyclerView.getLayoutManager())
+ .setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow());
getMainThreadHandler().post(() -> {
- if (mResolverDrawerLayout == null || mChooserRowAdapter == null) {
+ if (mResolverDrawerLayout == null || mChooserGridAdapter == null) {
return;
}
final int bottomInset = mSystemWindowInsets != null
? mSystemWindowInsets.bottom : 0;
int offset = bottomInset;
- int rowsToShow = mChooserRowAdapter.getContentPreviewRowCount()
- + mChooserRowAdapter.getProfileRowCount()
- + mChooserRowAdapter.getServiceTargetRowCount()
- + mChooserRowAdapter.getCallerAndRankedTargetRowCount();
+ int rowsToShow = mChooserGridAdapter.getContentPreviewRowCount()
+ + mChooserGridAdapter.getProfileRowCount()
+ + mChooserGridAdapter.getServiceTargetRowCount()
+ + mChooserGridAdapter.getCallerAndRankedTargetRowCount();
// then this is most likely not a SEND_* action, so check
// the app target count
if (rowsToShow == 0) {
- rowsToShow = mChooserRowAdapter.getCount();
+ rowsToShow = mChooserGridAdapter.getRowCount();
}
// still zero? then use a default height and leave, which
@@ -2050,15 +2064,22 @@ public class ChooserActivity extends ResolverActivity implements
int directShareHeight = 0;
rowsToShow = Math.min(4, rowsToShow);
- for (int i = 0; i < Math.min(rowsToShow, mAdapterView.getChildCount()); i++) {
- View child = mAdapterView.getChildAt(i);
+ for (int i = 0, childCount = mRecyclerView.getChildCount();
+ i < childCount && rowsToShow > 0; i++) {
+ View child = mRecyclerView.getChildAt(i);
+ if (((GridLayoutManager.LayoutParams)
+ child.getLayoutParams()).getSpanIndex() != 0) {
+ continue;
+ }
int height = child.getHeight();
offset += height;
- if (child.getTag() != null
- && (child.getTag() instanceof DirectShareViewHolder)) {
+ if (mChooserGridAdapter.getTargetType(
+ mRecyclerView.getChildAdapterPosition(child))
+ == mChooserListAdapter.TARGET_SERVICE) {
directShareHeight = height;
}
+ rowsToShow--;
}
boolean isExpandable = getResources().getConfiguration().orientation
@@ -2107,7 +2128,9 @@ public class ChooserActivity extends ResolverActivity implements
@Override // ChooserListCommunicator
public int getMaxRankedTargets() {
- return mChooserRowAdapter == null ? 4 : mChooserRowAdapter.getMaxTargetsPerRow();
+ return mChooserGridAdapter == null
+ ? ChooserGridAdapter.MAX_TARGETS_PER_ROW_PORTRAIT
+ : mChooserGridAdapter.getMaxTargetsPerRow();
}
@Override // ChooserListCommunicator
@@ -2175,7 +2198,40 @@ public class ChooserActivity extends ResolverActivity implements
return false;
}
- class ChooserRowAdapter extends BaseAdapter {
+ /**
+ * Used to bind types of individual item including
+ * {@link ChooserGridAdapter#VIEW_TYPE_NORMAL},
+ * {@link ChooserGridAdapter#VIEW_TYPE_CONTENT_PREVIEW},
+ * {@link ChooserGridAdapter#VIEW_TYPE_PROFILE},
+ * and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}.
+ */
+ final class ItemViewHolder extends RecyclerView.ViewHolder {
+ ResolverListAdapter.ViewHolder mWrappedViewHolder;
+ int mListPosition = ChooserListAdapter.NO_POSITION;
+
+ ItemViewHolder(View itemView, boolean isClickable) {
+ super(itemView);
+ mWrappedViewHolder = new ResolverListAdapter.ViewHolder(itemView);
+ if (isClickable) {
+ itemView.setOnClickListener(v -> startSelected(mListPosition,
+ false/* always */, true/* filterd */));
+ itemView.setOnLongClickListener(v -> {
+ showTargetDetails(
+ mChooserListAdapter.resolveInfoForPosition(
+ mListPosition, true/* filtered */));
+ return true;
+ });
+ }
+ }
+ }
+
+ /**
+ * Adapter for all types of items and targets in ShareSheet.
+ * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
+ * row level by this adapter but not on the item level. Individual targets within the row are
+ * handled by {@link ChooserListAdapter}
+ */
+ final class ChooserGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ChooserListAdapter mChooserListAdapter;
private final LayoutInflater mLayoutInflater;
@@ -2191,13 +2247,15 @@ public class ChooserActivity extends ResolverActivity implements
private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
private static final int VIEW_TYPE_PROFILE = 3;
private static final int VIEW_TYPE_AZ_LABEL = 4;
+ private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
- public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
+ ChooserGridAdapter(ChooserListAdapter wrappedAdapter) {
+ super();
mChooserListAdapter = wrappedAdapter;
mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
@@ -2213,7 +2271,7 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public void onInvalidated() {
super.onInvalidated();
- notifyDataSetInvalidated();
+ notifyDataSetChanged();
}
});
}
@@ -2229,7 +2287,7 @@ public class ChooserActivity extends ResolverActivity implements
return false;
}
- int newWidth = width / getMaxTargetsPerRow();
+ int newWidth = width / getMaxTargetsPerRow();
if (newWidth != mChooserTargetWidth) {
mChooserTargetWidth = newWidth;
return true;
@@ -2259,22 +2317,7 @@ public class ChooserActivity extends ResolverActivity implements
return oldValue;
}
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- @Override
- public boolean isEnabled(int position) {
- int viewType = getItemViewType(position);
- if (viewType == VIEW_TYPE_CONTENT_PREVIEW || viewType == VIEW_TYPE_AZ_LABEL) {
- return false;
- }
- return true;
- }
-
- @Override
- public int getCount() {
+ public int getRowCount() {
return (int) (
getContentPreviewRowCount()
+ getProfileRowCount()
@@ -2326,42 +2369,50 @@ public class ChooserActivity extends ResolverActivity implements
}
@Override
- public Object getItem(int position) {
- // We have nothing useful to return here.
- return position;
+ public int getItemCount() {
+ return (int) (
+ getContentPreviewRowCount()
+ + getProfileRowCount()
+ + getServiceTargetRowCount()
+ + getCallerAndRankedTargetRowCount()
+ + getAzLabelRowCount()
+ + mChooserListAdapter.getAlphaTargetCount()
+ );
}
@Override
- public long getItemId(int position) {
- return position;
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ switch (viewType) {
+ case VIEW_TYPE_CONTENT_PREVIEW:
+ return new ItemViewHolder(createContentPreviewView(parent), false);
+ case VIEW_TYPE_PROFILE:
+ return new ItemViewHolder(createProfileView(parent), false);
+ case VIEW_TYPE_AZ_LABEL:
+ return new ItemViewHolder(createAzLabelView(parent), false);
+ case VIEW_TYPE_NORMAL:
+ return new ItemViewHolder(mChooserListAdapter.createView(parent), true);
+ case VIEW_TYPE_DIRECT_SHARE:
+ case VIEW_TYPE_CALLER_AND_RANK:
+ return createItemGroupViewHolder(viewType, parent);
+ default:
+ // Since we catch all possible viewTypes above, no chance this is being called.
+ return null;
+ }
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final RowViewHolder holder;
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
-
- if (viewType == VIEW_TYPE_CONTENT_PREVIEW) {
- return createContentPreviewView(convertView, parent);
- }
-
- if (viewType == VIEW_TYPE_PROFILE) {
- return createProfileView(convertView, parent);
- }
-
- if (viewType == VIEW_TYPE_AZ_LABEL) {
- return createAzLabelView(parent);
- }
-
- if (convertView == null) {
- holder = createViewHolder(viewType, parent);
- } else {
- holder = (RowViewHolder) convertView.getTag();
+ switch (viewType) {
+ case VIEW_TYPE_DIRECT_SHARE:
+ case VIEW_TYPE_CALLER_AND_RANK:
+ bindItemGroupViewHolder(position, (ItemGroupViewHolder) holder);
+ break;
+ case VIEW_TYPE_NORMAL:
+ bindItemViewHolder(position, (ItemViewHolder) holder);
+ break;
+ default:
}
-
- bindViewHolder(position, holder);
-
- return holder.getViewGroup();
}
@Override
@@ -2378,7 +2429,7 @@ public class ChooserActivity extends ResolverActivity implements
if (count > 0 && position < countSum) return VIEW_TYPE_DIRECT_SHARE;
countSum += (count = getCallerAndRankedTargetRowCount());
- if (count > 0 && position < countSum) return VIEW_TYPE_NORMAL;
+ if (count > 0 && position < countSum) return VIEW_TYPE_CALLER_AND_RANK;
countSum += (count = getAzLabelRowCount());
if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL;
@@ -2386,27 +2437,22 @@ public class ChooserActivity extends ResolverActivity implements
return VIEW_TYPE_NORMAL;
}
- @Override
- public int getViewTypeCount() {
- return 5;
+ public int getTargetType(int position) {
+ return mChooserListAdapter.getPositionTargetType(getListPosition(position));
}
- private ViewGroup createContentPreviewView(View convertView, ViewGroup parent) {
+ private ViewGroup createContentPreviewView(ViewGroup parent) {
Intent targetIntent = getTargetIntent();
int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
- if (convertView == null) {
- getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
+ getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
.setSubtype(previewType));
- }
- return displayContentPreview(previewType, targetIntent, mLayoutInflater,
- (ViewGroup) convertView, parent);
+ return displayContentPreview(previewType, targetIntent, mLayoutInflater, parent);
}
- private View createProfileView(View convertView, ViewGroup parent) {
- View profileRow = convertView != null ? convertView : mLayoutInflater.inflate(
- R.layout.chooser_profile_row, parent, false);
+ private View createProfileView(ViewGroup parent) {
+ View profileRow = mLayoutInflater.inflate(R.layout.chooser_profile_row, parent, false);
profileRow.setBackground(
getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
mProfileView = profileRow.findViewById(R.id.profile_button);
@@ -2419,7 +2465,7 @@ public class ChooserActivity extends ResolverActivity implements
return mLayoutInflater.inflate(R.layout.chooser_az_label_row, parent, false);
}
- private RowViewHolder loadViewsIntoRow(RowViewHolder holder) {
+ private ItemGroupViewHolder loadViewsIntoGroup(ItemGroupViewHolder holder) {
final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
final int exactSpec = MeasureSpec.makeMeasureSpec(mChooserTargetWidth,
MeasureSpec.EXACTLY);
@@ -2445,7 +2491,7 @@ public class ChooserActivity extends ResolverActivity implements
return true;
}
});
- ViewGroup row = holder.addView(i, v);
+ holder.addView(i, v);
// Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll =
// false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be
@@ -2490,7 +2536,7 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- RowViewHolder createViewHolder(int viewType, ViewGroup parent) {
+ ItemGroupViewHolder createItemGroupViewHolder(int viewType, ViewGroup parent) {
if (viewType == VIEW_TYPE_DIRECT_SHARE) {
ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate(
R.layout.chooser_row_direct_share, parent, false);
@@ -2503,14 +2549,14 @@ public class ChooserActivity extends ResolverActivity implements
mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
Lists.newArrayList(row1, row2), getMaxTargetsPerRow());
- loadViewsIntoRow(mDirectShareViewHolder);
+ loadViewsIntoGroup(mDirectShareViewHolder);
return mDirectShareViewHolder;
} else {
ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
false);
- RowViewHolder holder = new SingleRowViewHolder(row, getMaxTargetsPerRow());
- loadViewsIntoRow(holder);
+ ItemGroupViewHolder holder = new SingleRowViewHolder(row, getMaxTargetsPerRow());
+ loadViewsIntoGroup(holder);
return holder;
}
@@ -2538,19 +2584,20 @@ public class ChooserActivity extends ResolverActivity implements
return positionType;
}
- void bindViewHolder(int rowPosition, RowViewHolder holder) {
- final int start = getFirstRowPosition(rowPosition);
- final int startType = getRowType(start);
- final int lastStartType = getRowType(getFirstRowPosition(rowPosition - 1));
-
- final ViewGroup row = holder.getViewGroup();
+ void bindItemViewHolder(int position, ItemViewHolder holder) {
+ View v = holder.itemView;
+ int listPosition = getListPosition(position);
+ holder.mListPosition = listPosition;
+ mChooserListAdapter.bindView(listPosition, v);
+ }
- if (startType != lastStartType
- || rowPosition == getContentPreviewRowCount() + getProfileRowCount()) {
- row.setForeground(
+ void bindItemGroupViewHolder(int position, ItemGroupViewHolder holder) {
+ final ViewGroup viewGroup = (ViewGroup) holder.itemView;
+ int start = getListPosition(position);
+ int startType = getRowType(start);
+ if (viewGroup.getForeground() == null) {
+ viewGroup.setForeground(
getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
- } else {
- row.setForeground(null);
}
int columnCount = holder.getColumnCount();
@@ -2560,7 +2607,7 @@ public class ChooserActivity extends ResolverActivity implements
}
if (end == start && mChooserListAdapter.getItem(start) instanceof EmptyTargetInfo) {
- final TextView textView = row.findViewById(R.id.chooser_row_text_option);
+ final TextView textView = viewGroup.findViewById(R.id.chooser_row_text_option);
if (textView.getVisibility() != View.VISIBLE) {
textView.setAlpha(0.0f);
@@ -2597,27 +2644,28 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- int getFirstRowPosition(int row) {
- row -= getContentPreviewRowCount() + getProfileRowCount();
+ int getListPosition(int position) {
+ position -= getContentPreviewRowCount() + getProfileRowCount();
final int serviceCount = mChooserListAdapter.getServiceTargetCount();
final int serviceRows = (int) Math.ceil((float) serviceCount
/ ChooserListAdapter.MAX_SERVICE_TARGETS);
- if (row < serviceRows) {
- return row * getMaxTargetsPerRow();
+ if (position < serviceRows) {
+ return position * getMaxTargetsPerRow();
}
+ position -= serviceRows;
+
final int callerAndRankedCount = mChooserListAdapter.getCallerTargetCount()
+ mChooserListAdapter.getRankedTargetCount();
final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
- if (row < callerAndRankedRows + serviceRows) {
- return serviceCount + (row - serviceRows) * getMaxTargetsPerRow();
+ if (position < callerAndRankedRows) {
+ return serviceCount + position * getMaxTargetsPerRow();
}
- row -= getAzLabelRowCount();
+ position -= getAzLabelRowCount() + callerAndRankedRows;
- return callerAndRankedCount + serviceCount
- + (row - callerAndRankedRows - serviceRows) * getMaxTargetsPerRow();
+ return callerAndRankedCount + serviceCount + position;
}
public void handleScroll(View v, int y, int oldy) {
@@ -2631,18 +2679,24 @@ public class ChooserActivity extends ResolverActivity implements
&& !isInMultiWindowMode();
if (mDirectShareViewHolder != null && canExpandDirectShare) {
- mDirectShareViewHolder.handleScroll(mAdapterView, y, oldy, getMaxTargetsPerRow());
+ mDirectShareViewHolder.handleScroll(mRecyclerView, y, oldy, getMaxTargetsPerRow());
}
}
}
- abstract class RowViewHolder {
+ /**
+ * Used to bind types for group of items including:
+ * {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE},
+ * and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}.
+ */
+ abstract class ItemGroupViewHolder extends RecyclerView.ViewHolder {
protected int mMeasuredRowHeight;
private int[] mItemIndices;
protected final View[] mCells;
private final int mColumnCount;
- RowViewHolder(int cellCount) {
+ ItemGroupViewHolder(int cellCount, View itemView) {
+ super(itemView);
this.mCells = new View[cellCount];
this.mItemIndices = new int[cellCount];
this.mColumnCount = cellCount;
@@ -2685,11 +2739,11 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- class SingleRowViewHolder extends RowViewHolder {
+ class SingleRowViewHolder extends ItemGroupViewHolder {
private final ViewGroup mRow;
SingleRowViewHolder(ViewGroup row, int cellCount) {
- super(cellCount);
+ super(cellCount, row);
this.mRow = row;
}
@@ -2719,7 +2773,7 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- class DirectShareViewHolder extends RowViewHolder {
+ class DirectShareViewHolder extends ItemGroupViewHolder {
private final ViewGroup mParent;
private final List<ViewGroup> mRows;
private int mCellCountPerRow;
@@ -2732,7 +2786,7 @@ public class ChooserActivity extends ResolverActivity implements
private final boolean[] mCellVisibility;
DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow) {
- super(rows.size() * cellCountPerRow);
+ super(rows.size() * cellCountPerRow, parent);
this.mParent = parent;
this.mRows = rows;
@@ -2767,7 +2821,7 @@ public class ChooserActivity extends ResolverActivity implements
mDirectShareMinHeight = getRow(0).getMeasuredHeight();
mDirectShareCurrHeight = mDirectShareCurrHeight > 0
- ? mDirectShareCurrHeight : mDirectShareMinHeight;
+ ? mDirectShareCurrHeight : mDirectShareMinHeight;
mDirectShareMaxHeight = 2 * mDirectShareMinHeight;
}
@@ -2800,7 +2854,7 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- public void handleScroll(AbsListView view, int y, int oldy, int maxTargetsPerRow) {
+ public void handleScroll(RecyclerView view, int y, int oldy, int maxTargetsPerRow) {
// only exit early if fully collapsed, otherwise onListRebuilt() with shifting
// targets can lock us into an expanded mode
boolean notExpanded = mDirectShareCurrHeight == mDirectShareMinHeight;
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 38f4c64768db..4eccf21677d5 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -49,6 +49,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
private static final String TAG = "ChooserListAdapter";
private static final boolean DEBUG = false;
+ public static final int NO_POSITION = -1;
public static final int TARGET_BAD = -1;
public static final int TARGET_CALLER = 0;
public static final int TARGET_SERVICE = 1;
@@ -189,7 +190,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
@Override
- public View onCreateView(ViewGroup parent) {
+ View onCreateView(ViewGroup parent) {
return mInflater.inflate(
com.android.internal.R.layout.resolve_grid_item, parent, false);
}
@@ -321,6 +322,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
*/
@Override
public TargetInfo targetInfoForPosition(int position, boolean filtered) {
+ if (position == NO_POSITION) {
+ return null;
+ }
+
int offset = 0;
// Direct share targets
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index c11089ba19bd..49f77e11cf80 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -194,7 +194,7 @@ public class LocaleStore {
private static Set<String> getSimCountries(Context context) {
Set<String> result = new HashSet<>();
- TelephonyManager tm = TelephonyManager.from(context);
+ TelephonyManager tm = context.getSystemService(TelephonyManager.class);
if (tm != null) {
String iso = tm.getSimCountryIso().toUpperCase(Locale.US);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3b352a99526b..0997cf87d592 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -97,7 +97,7 @@ public class ResolverActivity extends Activity implements
@UnsupportedAppUsage
protected ResolverListAdapter mAdapter;
private boolean mSafeForwardingMode;
- protected AbsListView mAdapterView;
+ private AbsListView mAdapterView;
private Button mAlwaysButton;
private Button mOnceButton;
protected View mProfileView;
@@ -1075,7 +1075,6 @@ public class ResolverActivity extends Activity implements
mLayoutId = getLayoutResource();
}
setContentView(mLayoutId);
- mAdapterView = findViewById(R.id.resolver_list);
return postRebuildList(rebuildCompleted);
}
@@ -1114,26 +1113,37 @@ public class ResolverActivity extends Activity implements
}
}
+ boolean isAdapterViewVisible = true;
if (count == 0 && mAdapter.getPlaceholderCount() == 0) {
final TextView emptyView = findViewById(R.id.empty);
emptyView.setVisibility(View.VISIBLE);
- mAdapterView.setVisibility(View.GONE);
- } else {
- mAdapterView.setVisibility(View.VISIBLE);
- onPrepareAdapterView(mAdapterView, mAdapter);
+ isAdapterViewVisible = false;
}
+
+ onPrepareAdapterView(mAdapter, isAdapterViewVisible);
return false;
}
- public void onPrepareAdapterView(AbsListView adapterView, ResolverListAdapter adapter) {
+ /**
+ * Prepare the scrollable view which consumes data in the list adapter.
+ * @param adapter The adapter used to provide data to item views.
+ * @param isVisible True if the scrollable view should be visible; false, otherwise.
+ */
+ public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) {
+ mAdapterView = findViewById(R.id.resolver_list);
+ if (!isVisible) {
+ mAdapterView.setVisibility(View.GONE);
+ return;
+ }
+ mAdapterView.setVisibility(View.VISIBLE);
final boolean useHeader = adapter.hasFilteredItem();
- final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
+ final ListView listView = mAdapterView instanceof ListView ? (ListView) mAdapterView : null;
- adapterView.setAdapter(mAdapter);
+ mAdapterView.setAdapter(mAdapter);
final ItemClickListener listener = new ItemClickListener();
- adapterView.setOnItemClickListener(listener);
- adapterView.setOnItemLongClickListener(listener);
+ mAdapterView.setOnItemClickListener(listener);
+ mAdapterView.setOnItemLongClickListener(listener);
if (mSupportsAlwaysUseOption || mUseLayoutForBrowsables) {
listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 0286aa69b063..4570c6f06a28 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -491,7 +491,7 @@ public class ResolverListAdapter extends BaseAdapter {
return view;
}
- public View onCreateView(ViewGroup parent) {
+ View onCreateView(ViewGroup parent) {
return mInflater.inflate(
com.android.internal.R.layout.resolve_list_item, parent, false);
}
diff --git a/core/java/com/android/internal/car/ICarStatsService.aidl b/core/java/com/android/internal/car/ICarStatsService.aidl
new file mode 100644
index 000000000000..170b448ba33f
--- /dev/null
+++ b/core/java/com/android/internal/car/ICarStatsService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.car;
+
+import android.os.StatsLogEventWrapper;
+
+/**
+ * Interface for pulling statsd atoms from automotive devices.
+ *
+ * @hide
+ */
+interface ICarStatsService {
+ /**
+ * Pull the specified atom. Results will be sent to statsd when complete.
+ */
+ StatsLogEventWrapper[] pullData(int atomId);
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 5857642cbd4e..045a6800bb13 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -133,7 +133,7 @@ interface IPlatformCompat
boolean isChangeEnabledByUid(long changeId, int uid);
/**
- * Add overrides to compatibility changes.
+ * Add overrides to compatibility changes. Kills the app to allow the changes to take effect.
*
* @param overrides Parcelable containing the compat change overrides to be applied.
* @param packageName The package name of the app whose changes will be overridden.
@@ -142,7 +142,28 @@ interface IPlatformCompat
void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
/**
- * Revert overrides to compatibility changes.
+ * Add overrides to compatibility changes. Doesn't kill the app, to be only used in tests.
+ *
+ * @param overrides Parcelable containing the compat change overrides to be applied.
+ * @param packageName The package name of the app whose changes will be overridden.
+ *
+ */
+ void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName);
+
+ /**
+ * Removes an override previously added via {@link #setOverrides(CompatibilityChangeConfig,
+ * String)}. This restores the default behaviour for the given change and app, once any app
+ * processes have been restarted.
+ * Kills the app to allow the changes to take effect.
+ *
+ * @param changeId The ID of the change that was overridden.
+ * @param packageName The app package name that was overridden.
+ * @return {@code true} if an override existed;
+ */
+ boolean clearOverride(long changeId, String packageName);
+
+ /**
+ * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect.
*
* @param packageName The package name of the app whose overrides will be cleared.
*
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index da43eddeb0bf..fd3cd42b07a1 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -277,7 +277,7 @@ public class RuntimeInit {
result.append(System.getProperty("java.vm.version")); // such as 1.1.0
result.append(" (Linux; U; Android ");
- String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5"
+ String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
result.append(version.length() > 0 ? version : "1.0");
// add the model for the release build
diff --git a/core/java/com/android/internal/package-info.java b/core/java/com/android/internal/package-info.java
new file mode 100644
index 000000000000..8a226dbdc9fe
--- /dev/null
+++ b/core/java/com/android/internal/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package com.android.internal;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 73f549a31bac..20706bb4b21f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -188,4 +188,14 @@ oneway interface IStatusBar
* @param types the internal insets types of the bars are about to abort the transient state.
*/
void abortTransient(int displayId, in int[] types);
+
+ /**
+ * Show a warning that the device is about to go to sleep due to user inactivity.
+ */
+ void showInattentiveSleepWarning();
+
+ /**
+ * Dismiss the warning that the device is about to go to sleep due to user inactivity.
+ */
+ void dismissInattentiveSleepWarning();
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 3f08710bb17f..76235a4a892e 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -113,4 +113,14 @@ interface IStatusBarService
void onBiometricError(int modality, int error, int vendorCode);
// Used to hide the authentication dialog, e.g. when the application cancels authentication
void hideAuthenticationDialog();
+
+ /**
+ * Show a warning that the device is about to go to sleep due to user inactivity.
+ */
+ void showInattentiveSleepWarning();
+
+ /**
+ * Dismiss the warning that the device is about to go to sleep due to user inactivity.
+ */
+ void dismissInattentiveSleepWarning();
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index d7a7af1d530f..9ae0ba555bdf 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,16 +32,22 @@ import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
interface ITelephonyRegistry {
- void addOnSubscriptionsChangedListener(String pkg,
+ void addOnSubscriptionsChangedListener(String pkg, String featureId,
IOnSubscriptionsChangedListener callback);
- void addOnOpportunisticSubscriptionsChangedListener(String pkg,
+ void addOnOpportunisticSubscriptionsChangedListener(String pkg, String featureId,
IOnSubscriptionsChangedListener callback);
void removeOnSubscriptionsChangedListener(String pkg,
IOnSubscriptionsChangedListener callback);
+ /**
+ * @deprecated Use {@link #listenWithFeature(String, String, IPhoneStateListener, int,
+ * boolean) instead
+ */
@UnsupportedAppUsage
void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
- void listenForSubscriber(in int subId, String pkg, IPhoneStateListener callback, int events,
+ void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, int events,
boolean notifyNow);
+ void listenForSubscriber(in int subId, String pkg, String featureId,
+ IPhoneStateListener callback, int events, boolean notifyNow);
@UnsupportedAppUsage
void notifyCallStateForAllSubs(int state, String incomingNumber);
void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber);
diff --git a/core/java/com/android/internal/widget/GridLayoutManager.java b/core/java/com/android/internal/widget/GridLayoutManager.java
new file mode 100644
index 000000000000..e0502f129f7f
--- /dev/null
+++ b/core/java/com/android/internal/widget/GridLayoutManager.java
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseIntArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.util.Arrays;
+
+/**
+ * Note: This GridLayoutManager widget may lack of latest fix because it is ported from
+ * oc-dr1-release version of android.support.v7.widget.GridLayoutManager due to compatibility
+ * concern with other internal widgets, like {@link RecyclerView} and {@link LinearLayoutManager},
+ * and is merely used for {@link com.android.internal.app.ChooserActivity}.
+ *
+ * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid.
+ * <p>
+ * By default, each item occupies 1 span. You can change it by providing a custom
+ * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}.
+ */
+public class GridLayoutManager extends LinearLayoutManager {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "GridLayoutManager";
+ public static final int DEFAULT_SPAN_COUNT = -1;
+ /**
+ * Span size have been changed but we've not done a new layout calculation.
+ */
+ boolean mPendingSpanCountChange = false;
+ int mSpanCount = DEFAULT_SPAN_COUNT;
+ /**
+ * Right borders for each span.
+ * <p>For <b>i-th</b> item start is {@link #mCachedBorders}[i-1] + 1
+ * and end is {@link #mCachedBorders}[i].
+ */
+ int[] mCachedBorders;
+ /**
+ * Temporary array to keep views in layoutChunk method
+ */
+ View[] mSet;
+ final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray();
+ final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray();
+ SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();
+ // re-used variable to acquire decor insets from RecyclerView
+ final Rect mDecorInsets = new Rect();
+
+ /**
+ * Constructor used when layout manager is set in XML by RecyclerView attribute
+ * "layoutManager". If spanCount is not specified in the XML, it defaults to a
+ * single column.
+ *
+ */
+ public GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
+ setSpanCount(properties.spanCount);
+ }
+
+ /**
+ * Creates a vertical GridLayoutManager
+ *
+ * @param context Current context, will be used to access resources.
+ * @param spanCount The number of columns in the grid
+ */
+ public GridLayoutManager(Context context, int spanCount) {
+ super(context);
+ setSpanCount(spanCount);
+ }
+
+ /**
+ * @param context Current context, will be used to access resources.
+ * @param spanCount The number of columns or rows in the grid
+ * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
+ * #VERTICAL}.
+ * @param reverseLayout When set to true, layouts from end to start.
+ */
+ public GridLayoutManager(Context context, int spanCount, int orientation,
+ boolean reverseLayout) {
+ super(context, orientation, reverseLayout);
+ setSpanCount(spanCount);
+ }
+
+ /**
+ * stackFromEnd is not supported by GridLayoutManager. Consider using
+ * {@link #setReverseLayout(boolean)}.
+ */
+ @Override
+ public void setStackFromEnd(boolean stackFromEnd) {
+ if (stackFromEnd) {
+ throw new UnsupportedOperationException(
+ "GridLayoutManager does not support stack from end."
+ + " Consider using reverse layout");
+ }
+ super.setStackFromEnd(false);
+ }
+
+ @Override
+ public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ if (mOrientation == HORIZONTAL) {
+ return mSpanCount;
+ }
+ if (state.getItemCount() < 1) {
+ return 0;
+ }
+ // Row count is one more than the last item's row index.
+ return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
+ }
+
+ @Override
+ public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ if (mOrientation == VERTICAL) {
+ return mSpanCount;
+ }
+ if (state.getItemCount() < 1) {
+ return 0;
+ }
+ // Column count is one more than the last item's column index.
+ return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
+ RecyclerView.State state, View host, AccessibilityNodeInfo info) {
+ ViewGroup.LayoutParams lp = host.getLayoutParams();
+ if (!(lp instanceof LayoutParams)) {
+ super.onInitializeAccessibilityNodeInfoForItem(host, info);
+ return;
+ }
+ LayoutParams glp = (LayoutParams) lp;
+ int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition());
+ if (mOrientation == HORIZONTAL) {
+ info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(
+ glp.getSpanIndex(), glp.getSpanSize(),
+ spanGroupIndex, 1,
+ mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
+ } else { // VERTICAL
+ info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(
+ spanGroupIndex, 1,
+ glp.getSpanIndex(), glp.getSpanSize(),
+ mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
+ }
+ }
+
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ if (state.isPreLayout()) {
+ cachePreLayoutSpanMapping();
+ }
+ super.onLayoutChildren(recycler, state);
+ if (DEBUG) {
+ validateChildOrder();
+ }
+ clearPreLayoutSpanMappingCache();
+ }
+
+ @Override
+ public void onLayoutCompleted(RecyclerView.State state) {
+ super.onLayoutCompleted(state);
+ mPendingSpanCountChange = false;
+ }
+
+ private void clearPreLayoutSpanMappingCache() {
+ mPreLayoutSpanSizeCache.clear();
+ mPreLayoutSpanIndexCache.clear();
+ }
+
+ private void cachePreLayoutSpanMapping() {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
+ final int viewPosition = lp.getViewLayoutPosition();
+ mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize());
+ mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex());
+ }
+ }
+
+ @Override
+ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
+ mSpanSizeLookup.invalidateSpanIndexCache();
+ }
+
+ @Override
+ public void onItemsChanged(RecyclerView recyclerView) {
+ mSpanSizeLookup.invalidateSpanIndexCache();
+ }
+
+ @Override
+ public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
+ mSpanSizeLookup.invalidateSpanIndexCache();
+ }
+
+ @Override
+ public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
+ Object payload) {
+ mSpanSizeLookup.invalidateSpanIndexCache();
+ }
+
+ @Override
+ public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
+ mSpanSizeLookup.invalidateSpanIndexCache();
+ }
+
+ @Override
+ public RecyclerView.LayoutParams generateDefaultLayoutParams() {
+ if (mOrientation == HORIZONTAL) {
+ return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ } else {
+ return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ @Override
+ public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
+ return new LayoutParams(c, attrs);
+ }
+
+ @Override
+ public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+ if (lp instanceof ViewGroup.MarginLayoutParams) {
+ return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
+ } else {
+ return new LayoutParams(lp);
+ }
+ }
+
+ @Override
+ public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
+ return lp instanceof LayoutParams;
+ }
+
+ /**
+ * Sets the source to get the number of spans occupied by each item in the adapter.
+ *
+ * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans
+ * occupied by each item
+ */
+ public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
+ mSpanSizeLookup = spanSizeLookup;
+ }
+
+ /**
+ * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager.
+ *
+ * @return The current {@link SpanSizeLookup} used by the GridLayoutManager.
+ */
+ public SpanSizeLookup getSpanSizeLookup() {
+ return mSpanSizeLookup;
+ }
+
+ private void updateMeasurements() {
+ int totalSpace;
+ if (getOrientation() == VERTICAL) {
+ totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
+ } else {
+ totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
+ }
+ calculateItemBorders(totalSpace);
+ }
+
+ @Override
+ public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
+ if (mCachedBorders == null) {
+ super.setMeasuredDimension(childrenBounds, wSpec, hSpec);
+ }
+ final int width, height;
+ final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
+ if (mOrientation == VERTICAL) {
+ final int usedHeight = childrenBounds.height() + verticalPadding;
+ height = chooseSize(hSpec, usedHeight, getMinimumHeight());
+ width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding,
+ getMinimumWidth());
+ } else {
+ final int usedWidth = childrenBounds.width() + horizontalPadding;
+ width = chooseSize(wSpec, usedWidth, getMinimumWidth());
+ height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding,
+ getMinimumHeight());
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ /**
+ * @param totalSpace Total available space after padding is removed
+ */
+ private void calculateItemBorders(int totalSpace) {
+ mCachedBorders = calculateItemBorders(mCachedBorders, mSpanCount, totalSpace);
+ }
+
+ /**
+ * @param cachedBorders The out array
+ * @param spanCount number of spans
+ * @param totalSpace total available space after padding is removed
+ * @return The updated array. Might be the same instance as the provided array if its size
+ * has not changed.
+ */
+ static int[] calculateItemBorders(int[] cachedBorders, int spanCount, int totalSpace) {
+ if (cachedBorders == null || cachedBorders.length != spanCount + 1
+ || cachedBorders[cachedBorders.length - 1] != totalSpace) {
+ cachedBorders = new int[spanCount + 1];
+ }
+ cachedBorders[0] = 0;
+ int sizePerSpan = totalSpace / spanCount;
+ int sizePerSpanRemainder = totalSpace % spanCount;
+ int consumedPixels = 0;
+ int additionalSize = 0;
+ for (int i = 1; i <= spanCount; i++) {
+ int itemSize = sizePerSpan;
+ additionalSize += sizePerSpanRemainder;
+ if (additionalSize > 0 && (spanCount - additionalSize) < sizePerSpanRemainder) {
+ itemSize += 1;
+ additionalSize -= spanCount;
+ }
+ consumedPixels += itemSize;
+ cachedBorders[i] = consumedPixels;
+ }
+ return cachedBorders;
+ }
+
+ int getSpaceForSpanRange(int startSpan, int spanSize) {
+ if (mOrientation == VERTICAL && isLayoutRTL()) {
+ return mCachedBorders[mSpanCount - startSpan]
+ - mCachedBorders[mSpanCount - startSpan - spanSize];
+ } else {
+ return mCachedBorders[startSpan + spanSize] - mCachedBorders[startSpan];
+ }
+ }
+
+ @Override
+ void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
+ AnchorInfo anchorInfo, int itemDirection) {
+ super.onAnchorReady(recycler, state, anchorInfo, itemDirection);
+ updateMeasurements();
+ if (state.getItemCount() > 0 && !state.isPreLayout()) {
+ ensureAnchorIsInCorrectSpan(recycler, state, anchorInfo, itemDirection);
+ }
+ ensureViewSet();
+ }
+
+ private void ensureViewSet() {
+ if (mSet == null || mSet.length != mSpanCount) {
+ mSet = new View[mSpanCount];
+ }
+ }
+
+ @Override
+ public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ updateMeasurements();
+ ensureViewSet();
+ return super.scrollHorizontallyBy(dx, recycler, state);
+ }
+
+ @Override
+ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ updateMeasurements();
+ ensureViewSet();
+ return super.scrollVerticallyBy(dy, recycler, state);
+ }
+
+ private void ensureAnchorIsInCorrectSpan(RecyclerView.Recycler recycler,
+ RecyclerView.State state, AnchorInfo anchorInfo, int itemDirection) {
+ final boolean layingOutInPrimaryDirection =
+ itemDirection == LayoutState.ITEM_DIRECTION_TAIL;
+ int span = getSpanIndex(recycler, state, anchorInfo.mPosition);
+ if (layingOutInPrimaryDirection) {
+ // choose span 0
+ while (span > 0 && anchorInfo.mPosition > 0) {
+ anchorInfo.mPosition--;
+ span = getSpanIndex(recycler, state, anchorInfo.mPosition);
+ }
+ } else {
+ // choose the max span we can get. hopefully last one
+ final int indexLimit = state.getItemCount() - 1;
+ int pos = anchorInfo.mPosition;
+ int bestSpan = span;
+ while (pos < indexLimit) {
+ int next = getSpanIndex(recycler, state, pos + 1);
+ if (next > bestSpan) {
+ pos += 1;
+ bestSpan = next;
+ } else {
+ break;
+ }
+ }
+ anchorInfo.mPosition = pos;
+ }
+ }
+
+ @Override
+ View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,
+ int start, int end, int itemCount) {
+ ensureLayoutState();
+ View invalidMatch = null;
+ View outOfBoundsMatch = null;
+ final int boundsStart = mOrientationHelper.getStartAfterPadding();
+ final int boundsEnd = mOrientationHelper.getEndAfterPadding();
+ final int diff = end > start ? 1 : -1;
+ for (int i = start; i != end; i += diff) {
+ final View view = getChildAt(i);
+ final int position = getPosition(view);
+ if (position >= 0 && position < itemCount) {
+ final int span = getSpanIndex(recycler, state, position);
+ if (span != 0) {
+ continue;
+ }
+ if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {
+ if (invalidMatch == null) {
+ invalidMatch = view; // removed item, least preferred
+ }
+ } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd
+ || mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
+ if (outOfBoundsMatch == null) {
+ outOfBoundsMatch = view; // item is not visible, less preferred
+ }
+ } else {
+ return view;
+ }
+ }
+ }
+ return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
+ }
+
+ private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state,
+ int viewPosition) {
+ if (!state.isPreLayout()) {
+ return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount);
+ }
+ final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition);
+ if (adapterPosition == -1) {
+ if (DEBUG) {
+ throw new RuntimeException("Cannot find span group index for position "
+ + viewPosition);
+ }
+ Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition);
+ return 0;
+ }
+ return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount);
+ }
+
+ private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
+ if (!state.isPreLayout()) {
+ return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);
+ }
+ final int cached = mPreLayoutSpanIndexCache.get(pos, -1);
+ if (cached != -1) {
+ return cached;
+ }
+ final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
+ if (adapterPosition == -1) {
+ if (DEBUG) {
+ throw new RuntimeException("Cannot find span index for pre layout position. It is"
+ + " not cached, not in the adapter. Pos:" + pos);
+ }
+ Log.w(TAG, "Cannot find span size for pre layout position. It is"
+ + " not cached, not in the adapter. Pos:" + pos);
+ return 0;
+ }
+ return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);
+ }
+
+ private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
+ if (!state.isPreLayout()) {
+ return mSpanSizeLookup.getSpanSize(pos);
+ }
+ final int cached = mPreLayoutSpanSizeCache.get(pos, -1);
+ if (cached != -1) {
+ return cached;
+ }
+ final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
+ if (adapterPosition == -1) {
+ if (DEBUG) {
+ throw new RuntimeException("Cannot find span size for pre layout position. It is"
+ + " not cached, not in the adapter. Pos:" + pos);
+ }
+ Log.w(TAG, "Cannot find span size for pre layout position. It is"
+ + " not cached, not in the adapter. Pos:" + pos);
+ return 1;
+ }
+ return mSpanSizeLookup.getSpanSize(adapterPosition);
+ }
+
+ @Override
+ void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState,
+ LayoutPrefetchRegistry layoutPrefetchRegistry) {
+ int remainingSpan = mSpanCount;
+ int count = 0;
+ while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
+ final int pos = layoutState.mCurrentPosition;
+ layoutPrefetchRegistry.addPosition(pos, Math.max(0, layoutState.mScrollingOffset));
+ final int spanSize = mSpanSizeLookup.getSpanSize(pos);
+ remainingSpan -= spanSize;
+ layoutState.mCurrentPosition += layoutState.mItemDirection;
+ count++;
+ }
+ }
+
+ @Override
+ void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
+ LayoutState layoutState, LayoutChunkResult result) {
+ final int otherDirSpecMode = mOrientationHelper.getModeInOther();
+ final boolean flexibleInOtherDir = otherDirSpecMode != View.MeasureSpec.EXACTLY;
+ final int currentOtherDirSize = getChildCount() > 0 ? mCachedBorders[mSpanCount] : 0;
+ // if grid layout's dimensions are not specified, let the new row change the measurements
+ // This is not perfect since we not covering all rows but still solves an important case
+ // where they may have a header row which should be laid out according to children.
+ if (flexibleInOtherDir) {
+ updateMeasurements(); // reset measurements
+ }
+ final boolean layingOutInPrimaryDirection =
+ layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
+ int count = 0;
+ int consumedSpanCount = 0;
+ int remainingSpan = mSpanCount;
+ if (!layingOutInPrimaryDirection) {
+ int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition);
+ int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition);
+ remainingSpan = itemSpanIndex + itemSpanSize;
+ }
+ while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
+ int pos = layoutState.mCurrentPosition;
+ final int spanSize = getSpanSize(recycler, state, pos);
+ if (spanSize > mSpanCount) {
+ throw new IllegalArgumentException("Item at position " + pos + " requires "
+ + spanSize + " spans but GridLayoutManager has only " + mSpanCount
+ + " spans.");
+ }
+ remainingSpan -= spanSize;
+ if (remainingSpan < 0) {
+ break; // item did not fit into this row or column
+ }
+ View view = layoutState.next(recycler);
+ if (view == null) {
+ break;
+ }
+ consumedSpanCount += spanSize;
+ mSet[count] = view;
+ count++;
+ }
+ if (count == 0) {
+ result.mFinished = true;
+ return;
+ }
+ int maxSize = 0;
+ float maxSizeInOther = 0; // use a float to get size per span
+ // we should assign spans before item decor offsets are calculated
+ assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection);
+ for (int i = 0; i < count; i++) {
+ View view = mSet[i];
+ if (layoutState.mScrapList == null) {
+ if (layingOutInPrimaryDirection) {
+ addView(view);
+ } else {
+ addView(view, 0);
+ }
+ } else {
+ if (layingOutInPrimaryDirection) {
+ addDisappearingView(view);
+ } else {
+ addDisappearingView(view, 0);
+ }
+ }
+ calculateItemDecorationsForChild(view, mDecorInsets);
+ measureChild(view, otherDirSpecMode, false);
+ final int size = mOrientationHelper.getDecoratedMeasurement(view);
+ if (size > maxSize) {
+ maxSize = size;
+ }
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view)
+ / lp.mSpanSize;
+ if (otherSize > maxSizeInOther) {
+ maxSizeInOther = otherSize;
+ }
+ }
+ if (flexibleInOtherDir) {
+ // re-distribute columns
+ guessMeasurement(maxSizeInOther, currentOtherDirSize);
+ // now we should re-measure any item that was match parent.
+ maxSize = 0;
+ for (int i = 0; i < count; i++) {
+ View view = mSet[i];
+ measureChild(view, View.MeasureSpec.EXACTLY, true);
+ final int size = mOrientationHelper.getDecoratedMeasurement(view);
+ if (size > maxSize) {
+ maxSize = size;
+ }
+ }
+ }
+ // Views that did not measure the maxSize has to be re-measured
+ // We will stop doing this once we introduce Gravity in the GLM layout params
+ for (int i = 0; i < count; i++) {
+ final View view = mSet[i];
+ if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ final Rect decorInsets = lp.mDecorInsets;
+ final int verticalInsets = decorInsets.top + decorInsets.bottom
+ + lp.topMargin + lp.bottomMargin;
+ final int horizontalInsets = decorInsets.left + decorInsets.right
+ + lp.leftMargin + lp.rightMargin;
+ final int totalSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize);
+ final int wSpec;
+ final int hSpec;
+ if (mOrientation == VERTICAL) {
+ wSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY,
+ horizontalInsets, lp.width, false);
+ hSpec = View.MeasureSpec.makeMeasureSpec(maxSize - verticalInsets,
+ View.MeasureSpec.EXACTLY);
+ } else {
+ wSpec = View.MeasureSpec.makeMeasureSpec(maxSize - horizontalInsets,
+ View.MeasureSpec.EXACTLY);
+ hSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY,
+ verticalInsets, lp.height, false);
+ }
+ measureChildWithDecorationsAndMargin(view, wSpec, hSpec, true);
+ }
+ }
+ result.mConsumed = maxSize;
+ int left = 0, right = 0, top = 0, bottom = 0;
+ if (mOrientation == VERTICAL) {
+ if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
+ bottom = layoutState.mOffset;
+ top = bottom - maxSize;
+ } else {
+ top = layoutState.mOffset;
+ bottom = top + maxSize;
+ }
+ } else {
+ if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
+ right = layoutState.mOffset;
+ left = right - maxSize;
+ } else {
+ left = layoutState.mOffset;
+ right = left + maxSize;
+ }
+ }
+ for (int i = 0; i < count; i++) {
+ View view = mSet[i];
+ LayoutParams params = (LayoutParams) view.getLayoutParams();
+ if (mOrientation == VERTICAL) {
+ if (isLayoutRTL()) {
+ right = getPaddingLeft() + mCachedBorders[mSpanCount - params.mSpanIndex];
+ left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
+ } else {
+ left = getPaddingLeft() + mCachedBorders[params.mSpanIndex];
+ right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
+ }
+ } else {
+ top = getPaddingTop() + mCachedBorders[params.mSpanIndex];
+ bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
+ }
+ // We calculate everything with View's bounding box (which includes decor and margins)
+ // To calculate correct layout position, we subtract margins.
+ layoutDecoratedWithMargins(view, left, top, right, bottom);
+ if (DEBUG) {
+ Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
+ + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
+ + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)
+ + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize);
+ }
+ // Consume the available space if the view is not removed OR changed
+ if (params.isItemRemoved() || params.isItemChanged()) {
+ result.mIgnoreConsumed = true;
+ }
+ result.mFocusable |= view.hasFocusable();
+ }
+ Arrays.fill(mSet, null);
+ }
+
+ /**
+ * Measures a child with currently known information. This is not necessarily the child's final
+ * measurement. (see fillChunk for details).
+ *
+ * @param view The child view to be measured
+ * @param otherDirParentSpecMode The RV measure spec that should be used in the secondary
+ * orientation
+ * @param alreadyMeasured True if we've already measured this view once
+ */
+ private void measureChild(View view, int otherDirParentSpecMode, boolean alreadyMeasured) {
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ final Rect decorInsets = lp.mDecorInsets;
+ final int verticalInsets = decorInsets.top + decorInsets.bottom
+ + lp.topMargin + lp.bottomMargin;
+ final int horizontalInsets = decorInsets.left + decorInsets.right
+ + lp.leftMargin + lp.rightMargin;
+ final int availableSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize);
+ final int wSpec;
+ final int hSpec;
+ if (mOrientation == VERTICAL) {
+ wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
+ horizontalInsets, lp.width, false);
+ hSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getHeightMode(),
+ verticalInsets, lp.height, true);
+ } else {
+ hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
+ verticalInsets, lp.height, false);
+ wSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getWidthMode(),
+ horizontalInsets, lp.width, true);
+ }
+ measureChildWithDecorationsAndMargin(view, wSpec, hSpec, alreadyMeasured);
+ }
+
+ /**
+ * This is called after laying out a row (if vertical) or a column (if horizontal) when the
+ * RecyclerView does not have exact measurement specs.
+ * <p>
+ * Here we try to assign a best guess width or height and re-do the layout to update other
+ * views that wanted to MATCH_PARENT in the non-scroll orientation.
+ *
+ * @param maxSizeInOther The maximum size per span ratio from the measurement of the
+ * children.
+ * @param currentOtherDirSize The size before this layout chunk. There is no reason to go below.
+ */
+ private void guessMeasurement(float maxSizeInOther, int currentOtherDirSize) {
+ final int contentSize = Math.round(maxSizeInOther * mSpanCount);
+ // always re-calculate because borders were stretched during the fill
+ calculateItemBorders(Math.max(contentSize, currentOtherDirSize));
+ }
+
+ private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec,
+ boolean alreadyMeasured) {
+ RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
+ final boolean measure;
+ if (alreadyMeasured) {
+ measure = shouldReMeasureChild(child, widthSpec, heightSpec, lp);
+ } else {
+ measure = shouldMeasureChild(child, widthSpec, heightSpec, lp);
+ }
+ if (measure) {
+ child.measure(widthSpec, heightSpec);
+ }
+ }
+
+ private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,
+ int consumedSpanCount, boolean layingOutInPrimaryDirection) {
+ // spans are always assigned from 0 to N no matter if it is RTL or not.
+ // RTL is used only when positioning the view.
+ int span, start, end, diff;
+ // make sure we traverse from min position to max position
+ if (layingOutInPrimaryDirection) {
+ start = 0;
+ end = count;
+ diff = 1;
+ } else {
+ start = count - 1;
+ end = -1;
+ diff = -1;
+ }
+ span = 0;
+ for (int i = start; i != end; i += diff) {
+ View view = mSet[i];
+ LayoutParams params = (LayoutParams) view.getLayoutParams();
+ params.mSpanSize = getSpanSize(recycler, state, getPosition(view));
+ params.mSpanIndex = span;
+ span += params.mSpanSize;
+ }
+ }
+
+ /**
+ * Returns the number of spans laid out by this grid.
+ *
+ * @return The number of spans
+ * @see #setSpanCount(int)
+ */
+ public int getSpanCount() {
+ return mSpanCount;
+ }
+
+ /**
+ * Sets the number of spans to be laid out.
+ * <p>
+ * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns.
+ * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows.
+ *
+ * @param spanCount The total number of spans in the grid
+ * @see #getSpanCount()
+ */
+ public void setSpanCount(int spanCount) {
+ if (spanCount == mSpanCount) {
+ return;
+ }
+ mPendingSpanCountChange = true;
+ if (spanCount < 1) {
+ throw new IllegalArgumentException("Span count should be at least 1. Provided "
+ + spanCount);
+ }
+ mSpanCount = spanCount;
+ mSpanSizeLookup.invalidateSpanIndexCache();
+ requestLayout();
+ }
+
+ /**
+ * A helper class to provide the number of spans each item occupies.
+ * <p>
+ * Default implementation sets each item to occupy exactly 1 span.
+ *
+ * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
+ */
+ public abstract static class SpanSizeLookup {
+ final SparseIntArray mSpanIndexCache = new SparseIntArray();
+ private boolean mCacheSpanIndices = false;
+
+ /**
+ * Returns the number of span occupied by the item at <code>position</code>.
+ *
+ * @param position The adapter position of the item
+ * @return The number of spans occupied by the item at the provided position
+ */
+ public abstract int getSpanSize(int position);
+
+ /**
+ * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
+ * not. By default these values are not cached. If you are not overriding
+ * {@link #getSpanIndex(int, int)}, you should set this to true for better performance.
+ *
+ * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.
+ */
+ public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
+ mCacheSpanIndices = cacheSpanIndices;
+ }
+
+ /**
+ * Clears the span index cache. GridLayoutManager automatically calls this method when
+ * adapter changes occur.
+ */
+ public void invalidateSpanIndexCache() {
+ mSpanIndexCache.clear();
+ }
+
+ /**
+ * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.
+ *
+ * @return True if results of {@link #getSpanIndex(int, int)} are cached.
+ */
+ public boolean isSpanIndexCacheEnabled() {
+ return mCacheSpanIndices;
+ }
+
+ int getCachedSpanIndex(int position, int spanCount) {
+ if (!mCacheSpanIndices) {
+ return getSpanIndex(position, spanCount);
+ }
+ final int existing = mSpanIndexCache.get(position, -1);
+ if (existing != -1) {
+ return existing;
+ }
+ final int value = getSpanIndex(position, spanCount);
+ mSpanIndexCache.put(position, value);
+ return value;
+ }
+
+ /**
+ * Returns the final span index of the provided position.
+ * <p>
+ * If you have a faster way to calculate span index for your items, you should override
+ * this method. Otherwise, you should enable span index cache
+ * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is
+ * disabled, default implementation traverses all items from 0 to
+ * <code>position</code>. When caching is enabled, it calculates from the closest cached
+ * value before the <code>position</code>.
+ * <p>
+ * If you override this method, you need to make sure it is consistent with
+ * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for
+ * each item. It is called only for the reference item and rest of the items
+ * are assigned to spans based on the reference item. For example, you cannot assign a
+ * position to span 2 while span 1 is empty.
+ * <p>
+ * Note that span offsets always start with 0 and are not affected by RTL.
+ *
+ * @param position The position of the item
+ * @param spanCount The total number of spans in the grid
+ * @return The final span position of the item. Should be between 0 (inclusive) and
+ * <code>spanCount</code>(exclusive)
+ */
+ public int getSpanIndex(int position, int spanCount) {
+ int positionSpanSize = getSpanSize(position);
+ if (positionSpanSize == spanCount) {
+ return 0; // quick return for full-span items
+ }
+ int span = 0;
+ int startPos = 0;
+ // If caching is enabled, try to jump
+ if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {
+ int prevKey = findReferenceIndexFromCache(position);
+ if (prevKey >= 0) {
+ span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);
+ startPos = prevKey + 1;
+ }
+ }
+ for (int i = startPos; i < position; i++) {
+ int size = getSpanSize(i);
+ span += size;
+ if (span == spanCount) {
+ span = 0;
+ } else if (span > spanCount) {
+ // did not fit, moving to next row / column
+ span = size;
+ }
+ }
+ if (span + positionSpanSize <= spanCount) {
+ return span;
+ }
+ return 0;
+ }
+
+ int findReferenceIndexFromCache(int position) {
+ int lo = 0;
+ int hi = mSpanIndexCache.size() - 1;
+ while (lo <= hi) {
+ final int mid = (lo + hi) >>> 1;
+ final int midVal = mSpanIndexCache.keyAt(mid);
+ if (midVal < position) {
+ lo = mid + 1;
+ } else {
+ hi = mid - 1;
+ }
+ }
+ int index = lo - 1;
+ if (index >= 0 && index < mSpanIndexCache.size()) {
+ return mSpanIndexCache.keyAt(index);
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index of the group this position belongs.
+ * <p>
+ * For example, if grid has 3 columns and each item occupies 1 span, span group index
+ * for item 1 will be 0, item 5 will be 1.
+ *
+ * @param adapterPosition The position in adapter
+ * @param spanCount The total number of spans in the grid
+ * @return The index of the span group including the item at the given adapter position
+ */
+ public int getSpanGroupIndex(int adapterPosition, int spanCount) {
+ int span = 0;
+ int group = 0;
+ int positionSpanSize = getSpanSize(adapterPosition);
+ for (int i = 0; i < adapterPosition; i++) {
+ int size = getSpanSize(i);
+ span += size;
+ if (span == spanCount) {
+ span = 0;
+ group++;
+ } else if (span > spanCount) {
+ // did not fit, moving to next row / column
+ span = size;
+ group++;
+ }
+ }
+ if (span + positionSpanSize > spanCount) {
+ group++;
+ }
+ return group;
+ }
+ }
+
+ @Override
+ public boolean supportsPredictiveItemAnimations() {
+ return mPendingSavedState == null && !mPendingSpanCountChange;
+ }
+
+ /**
+ * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span.
+ */
+ public static final class DefaultSpanSizeLookup extends SpanSizeLookup {
+ @Override
+ public int getSpanSize(int position) {
+ return 1;
+ }
+
+ @Override
+ public int getSpanIndex(int position, int spanCount) {
+ return position % spanCount;
+ }
+ }
+
+ /**
+ * LayoutParams used by GridLayoutManager.
+ * <p>
+ * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the
+ * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is
+ * expected to fill all of the space given to it.
+ */
+ public static class LayoutParams extends RecyclerView.LayoutParams {
+ /**
+ * Span Id for Views that are not laid out yet.
+ */
+ public static final int INVALID_SPAN_ID = -1;
+ int mSpanIndex = INVALID_SPAN_ID;
+ int mSpanSize = 0;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(RecyclerView.LayoutParams source) {
+ super(source);
+ }
+
+ /**
+ * Returns the current span index of this View. If the View is not laid out yet, the return
+ * value is <code>undefined</code>.
+ * <p>
+ * Starting with RecyclerView <b>24.2.0</b>, span indices are always indexed from position 0
+ * even if the layout is RTL. In a vertical GridLayoutManager, <b>leftmost</b> span is span
+ * 0 if the layout is <b>LTR</b> and <b>rightmost</b> span is span 0 if the layout is
+ * <b>RTL</b>. Prior to 24.2.0, it was the opposite which was conflicting with
+ * {@link SpanSizeLookup#getSpanIndex(int, int)}.
+ * <p>
+ * If the View occupies multiple spans, span with the minimum index is returned.
+ *
+ * @return The span index of the View.
+ */
+ public int getSpanIndex() {
+ return mSpanIndex;
+ }
+
+ /**
+ * Returns the number of spans occupied by this View. If the View not laid out yet, the
+ * return value is <code>undefined</code>.
+ *
+ * @return The number of spans occupied by this View.
+ */
+ public int getSpanSize() {
+ return mSpanSize;
+ }
+ }
+}
diff --git a/core/java/com/android/server/pm/UserTypeDetails.java b/core/java/com/android/server/pm/UserTypeDetails.java
new file mode 100644
index 000000000000..5fc3ba1fc538
--- /dev/null
+++ b/core/java/com/android/server/pm/UserTypeDetails.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.ColorRes;
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.res.Resources;
+import android.os.UserManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Contains the details about a multiuser "user type", such as a
+ * {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ *
+ * Tests are located in UserManagerServiceUserTypeTest.java.
+ * @hide
+ */
+public final class UserTypeDetails {
+
+ /** Indicates that there is no limit to the number of users allowed. */
+ public static final int UNLIMITED_NUMBER_OF_USERS = -1;
+
+ /** Name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}. */
+ private final @NonNull String mName;
+
+ // TODO(b/142482943): Currently unused. Hook this up.
+ private final boolean mEnabled;
+
+ // TODO(b/142482943): Currently unused and not set. Hook this up.
+ private final int mLabel;
+
+ /**
+ * Maximum number of this user type allowed on the device.
+ * Use {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+ */
+ private final int mMaxAllowed;
+
+ /**
+ * Maximum number of this user type allowed per parent (for user types, like profiles, that
+ * have parents).
+ * Use {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+ */
+ // TODO(b/142482943): Should this also apply to restricted profiles?
+ private final int mMaxAllowedPerParent;
+
+ // TODO(b/143784345): Update doc when we clean up UserInfo.
+ /** The {@link UserInfo.UserInfoFlag} representing the base type of this user. */
+ private final @UserInfoFlag int mBaseType;
+
+ // TODO(b/143784345): Update doc/name when we clean up UserInfo.
+ /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+ private final @UserInfoFlag int mDefaultUserInfoPropertyFlags;
+
+ // TODO(b/142482943): Hook these up to something and set them for each type.
+ private final List<String> mDefaultRestrictions;
+
+
+ // Fields for profiles only, controlling the nature of their badges.
+ // All badge information should be set if {@link #hasBadge()} is true.
+
+ /** Resource ID of the badge put on icons. */
+ private @DrawableRes final int mIconBadge;
+ /** Resource ID of the badge. Should be set if mIconBadge is set. */
+ private @DrawableRes final int mBadgePlain;
+ /** Resource ID of the badge without a background. Should be set if mIconBadge is set. */
+ private @DrawableRes final int mBadgeNoBackground;
+
+ /**
+ * Resource ID ({@link StringRes}) of the of the labels to describe badged apps; should be the
+ * same format as com.android.internal.R.color.profile_badge_1. These are used for accessibility
+ * services.
+ *
+ * <p>This is an array because, in general, there may be multiple users of the same user type.
+ * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
+ *
+ * <p>Must be set if mIconBadge is set.
+ */
+ private final int[] mBadgeLabels;
+
+ /**
+ * Resource ID ({@link ColorRes}) of the colors badge put on icons.
+ * (The value is a resource ID referring to the color; it is not the color value itself).
+ *
+ * <p>This is an array because, in general, there may be multiple users of the same user type.
+ * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
+ *
+ * <p>Must be set if mIconBadge is set.
+ */
+ private final int[] mBadgeColors;
+
+ private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
+ @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
+ int maxAllowedPerParent,
+ int iconBadge, int badgePlain, int badgeNoBackground,
+ int[] badgeLabels, int[] badgeColors,
+ ArrayList<String> defaultRestrictions) {
+ this.mName = name;
+ this.mEnabled = enabled;
+ this.mMaxAllowed = maxAllowed;
+ this.mMaxAllowedPerParent = maxAllowedPerParent;
+ this.mBaseType = baseType;
+ this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
+ this.mDefaultRestrictions =
+ Collections.unmodifiableList(new ArrayList<>(defaultRestrictions));
+
+ this.mIconBadge = iconBadge;
+ this.mBadgePlain = badgePlain;
+ this.mBadgeNoBackground = badgeNoBackground;
+ this.mLabel = label;
+ this.mBadgeLabels = badgeLabels;
+ this.mBadgeColors = badgeColors;
+ }
+
+ /**
+ * Returns the name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ // TODO(b/142482943) Hook this up or delete it.
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Returns the maximum number of this user type allowed on the device.
+ * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+ */
+ public int getMaxAllowed() {
+ return mMaxAllowed;
+ }
+
+ /**
+ * Returns the maximum number of this user type allowed per parent (for user types, like
+ * profiles, that have parents).
+ * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+ */
+ public int getMaxAllowedPerParent() {
+ return mMaxAllowedPerParent;
+ }
+
+ // TODO(b/143784345): Update comment when UserInfo is reorganized.
+ /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+ public int getDefaultUserInfoFlags() {
+ return mDefaultUserInfoPropertyFlags | mBaseType;
+ }
+
+ // TODO(b/142482943) Hook this up; it is currently unused.
+ public int getLabel() {
+ return mLabel;
+ }
+
+ /** Returns whether users of this user type should be badged. */
+ public boolean hasBadge() {
+ return mIconBadge != Resources.ID_NULL;
+ }
+
+ /** Resource ID of the badge put on icons. */
+ public @DrawableRes int getIconBadge() {
+ return mIconBadge;
+ }
+
+ /** Resource ID of the badge. Used for {@link UserManager#getUserBadgeResId(int)}. */
+ public @DrawableRes int getBadgePlain() {
+ return mBadgePlain;
+ }
+
+ /** Resource ID of the badge without a background. */
+ public @DrawableRes int getBadgeNoBackground() {
+ return mBadgeNoBackground;
+ }
+
+ /**
+ * Returns the Resource ID of the badgeIndexth badge label, where the badgeIndex is expected
+ * to be the {@link UserInfo#profileBadge} of the user.
+ * If badgeIndex exceeds the number of labels, returns the label for the highest index.
+ */
+ public @StringRes int getBadgeLabel(int badgeIndex) {
+ if (mBadgeLabels == null || mBadgeLabels.length == 0 || badgeIndex < 0) {
+ return Resources.ID_NULL;
+ }
+ return mBadgeLabels[Math.min(badgeIndex, mBadgeLabels.length - 1)];
+ }
+
+ /**
+ * Returns the Resource ID of the badgeIndexth badge color, where the badgeIndex is expected
+ * to be the {@link UserInfo#profileBadge} of the user.
+ * If badgeIndex exceeds the number of colors, returns the color for the highest index.
+ */
+ public @ColorRes int getBadgeColor(int badgeIndex) {
+ if (mBadgeColors == null || mBadgeColors.length == 0 || badgeIndex < 0) {
+ return Resources.ID_NULL;
+ }
+ return mBadgeColors[Math.min(badgeIndex, mBadgeColors.length - 1)];
+ }
+
+ public boolean isProfile() {
+ return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+ }
+
+ // TODO(b/142482943): Hook this up and don't return the original.
+ public List<String> getDefaultRestrictions() {
+ return mDefaultRestrictions;
+ }
+
+ /** Dumps details of the UserTypeDetails. Do not parse this. */
+ public void dump(PrintWriter pw) {
+ final String prefix = " ";
+ pw.print(prefix); pw.print("mName: "); pw.println(mName);
+ pw.print(prefix); pw.print("mBaseType: "); pw.println(UserInfo.flagsToString(mBaseType));
+ pw.print(prefix); pw.print("mEnabled: "); pw.println(mEnabled);
+ pw.print(prefix); pw.print("mMaxAllowed: "); pw.println(mMaxAllowed);
+ pw.print(prefix); pw.print("mMaxAllowedPerParent: "); pw.println(mMaxAllowedPerParent);
+ pw.print(prefix); pw.print("mDefaultUserInfoFlags: ");
+ pw.println(UserInfo.flagsToString(mDefaultUserInfoPropertyFlags));
+ pw.print(prefix); pw.print("mLabel: "); pw.println(mLabel);
+ pw.print(prefix); pw.print("mDefaultRestrictions: "); pw.println(mDefaultRestrictions);
+ pw.print(prefix); pw.print("mIconBadge: "); pw.println(mIconBadge);
+ pw.print(prefix); pw.print("mBadgePlain: "); pw.println(mBadgePlain);
+ pw.print(prefix); pw.print("mBadgeNoBackground: "); pw.println(mBadgeNoBackground);
+ pw.print(prefix); pw.print("mBadgeLabels.length: ");
+ pw.println(mBadgeLabels != null ? mBadgeLabels.length : "0(null)");
+ pw.print(prefix); pw.print("mBadgeColors.length: ");
+ pw.println(mBadgeColors != null ? mBadgeColors.length : "0(null)");
+ }
+
+ /** Builder for a {@link UserTypeDetails}; see that class for documentation. */
+ public static final class Builder {
+ // UserTypeDetails properties and their default values.
+ private String mName; // This MUST be explicitly set.
+ private int mBaseType; // This MUST be explicitly set.
+ private int mMaxAllowed = UNLIMITED_NUMBER_OF_USERS;
+ private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
+ private int mDefaultUserInfoPropertyFlags = 0;
+ private ArrayList<String> mDefaultRestrictions = new ArrayList<>();
+ private boolean mEnabled = true;
+ private int mLabel = Resources.ID_NULL;
+ private int[] mBadgeLabels = null;
+ private int[] mBadgeColors = null;
+ private int mIconBadge = Resources.ID_NULL;
+ private int mBadgePlain = Resources.ID_NULL;
+ private int mBadgeNoBackground = Resources.ID_NULL;
+
+ public Builder setName(String name) {
+ mName = name;
+ return this;
+ }
+
+ public Builder setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ return this;
+ }
+
+ public Builder setMaxAllowed(int maxAllowed) {
+ mMaxAllowed = maxAllowed;
+ return this;
+ }
+
+ public Builder setMaxAllowedPerParent(int maxAllowedPerParent) {
+ mMaxAllowedPerParent = maxAllowedPerParent;
+ return this;
+ }
+
+ public Builder setBaseType(@UserInfoFlag int baseType) {
+ mBaseType = baseType;
+ return this;
+ }
+
+ public Builder setDefaultUserInfoPropertyFlags(@UserInfoFlag int flags) {
+ mDefaultUserInfoPropertyFlags = flags;
+ return this;
+ }
+
+ public Builder setBadgeLabels(int ... badgeLabels) {
+ mBadgeLabels = badgeLabels;
+ return this;
+ }
+
+ public Builder setBadgeColors(int ... badgeColors) {
+ mBadgeColors = badgeColors;
+ return this;
+ }
+
+ public Builder setIconBadge(int badgeIcon) {
+ mIconBadge = badgeIcon;
+ return this;
+ }
+
+ public Builder setBadgePlain(int badgePlain) {
+ mBadgePlain = badgePlain;
+ return this;
+ }
+
+ public Builder setBadgeNoBackground(int badgeNoBackground) {
+ mBadgeNoBackground = badgeNoBackground;
+ return this;
+ }
+
+ public Builder setLabel(int label) {
+ mLabel = label;
+ return this;
+ }
+
+ public Builder setDefaultRestrictions(ArrayList<String> restrictions) {
+ mDefaultRestrictions = restrictions;
+ return this;
+ }
+
+ public UserTypeDetails createUserTypeDetails() {
+ Preconditions.checkArgument(mName != null,
+ "Cannot create a UserTypeDetails with no name.");
+ Preconditions.checkArgument(hasValidBaseType(),
+ "UserTypeDetails " + mName + " has invalid baseType: " + mBaseType);
+ Preconditions.checkArgument(hasValidPropertyFlags(),
+ "UserTypeDetails " + mName + " has invalid flags: "
+ + Integer.toHexString(mDefaultUserInfoPropertyFlags));
+ if (hasBadge()) {
+ Preconditions.checkArgument(mBadgeLabels != null && mBadgeLabels.length != 0,
+ "UserTypeDetails " + mName + " has badge but no badgeLabels.");
+ Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
+ "UserTypeDetails " + mName + " has badge but no badgeColors.");
+ }
+
+ return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
+ mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
+ mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
+ mDefaultRestrictions);
+ }
+
+ private boolean hasBadge() {
+ return mIconBadge != Resources.ID_NULL;
+ }
+
+ // TODO(b/143784345): Refactor this when we clean up UserInfo.
+ private boolean hasValidBaseType() {
+ return mBaseType == UserInfo.FLAG_FULL
+ || mBaseType == UserInfo.FLAG_PROFILE
+ || mBaseType == UserInfo.FLAG_SYSTEM
+ || mBaseType == (UserInfo.FLAG_FULL | UserInfo.FLAG_SYSTEM);
+ }
+
+ // TODO(b/143784345): Refactor this when we clean up UserInfo.
+ private boolean hasValidPropertyFlags() {
+ final int forbiddenMask =
+ UserInfo.FLAG_PRIMARY |
+ UserInfo.FLAG_ADMIN |
+ UserInfo.FLAG_INITIALIZED |
+ UserInfo.FLAG_QUIET_MODE |
+ UserInfo.FLAG_FULL |
+ UserInfo.FLAG_SYSTEM |
+ UserInfo.FLAG_PROFILE;
+ return (mDefaultUserInfoPropertyFlags & forbiddenMask) == 0;
+ }
+ }
+
+ /**
+ * Returns whether the user type is a managed profile
+ * (i.e. {@link UserManager#USER_TYPE_PROFILE_MANAGED}).
+ */
+ public boolean isManagedProfile() {
+ return UserManager.isUserTypeManagedProfile(mName);
+ }
+}
diff --git a/core/java/com/android/server/pm/UserTypeFactory.java b/core/java/com/android/server/pm/UserTypeFactory.java
new file mode 100644
index 000000000000..43bbab192722
--- /dev/null
+++ b/core/java/com/android/server/pm/UserTypeFactory.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+import static android.os.UserManager.USER_TYPE_FULL_DEMO;
+import static android.os.UserManager.USER_TYPE_FULL_GUEST;
+import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
+import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
+import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
+
+import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS;
+
+import android.content.res.Resources;
+import android.os.UserManager;
+import android.util.ArrayMap;
+
+/**
+ * Class for creating all {@link UserTypeDetails} on the device.
+ *
+ * Tests are located in UserManagerServiceUserTypeTest.java.
+ * @hide
+ */
+public final class UserTypeFactory {
+
+ /** This is a utility class, so no instantiable constructor. */
+ private UserTypeFactory() {}
+
+ /**
+ * Obtains the user types (built-in and customized) for this device.
+ *
+ * @return mapping from the name of each user type to its {@link UserTypeDetails} object
+ */
+ public static ArrayMap<String, UserTypeDetails> getUserTypes() {
+ final ArrayMap<String, UserTypeDetails> map = new ArrayMap<>();
+ // TODO(b/142482943): Read an xml file for OEM customized types.
+ // Remember to disallow "android." namespace
+ // TODO(b/142482943): Read an xml file to get any overrides for the built-in types.
+ final int maxManagedProfiles = 1;
+ map.put(USER_TYPE_PROFILE_MANAGED,
+ getDefaultTypeProfileManaged().setMaxAllowedPerParent(maxManagedProfiles)
+ .createUserTypeDetails());
+ map.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeSystemFull().createUserTypeDetails());
+ map.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary().createUserTypeDetails());
+ map.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest().createUserTypeDetails());
+ map.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo().createUserTypeDetails());
+ map.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted().createUserTypeDetails());
+ map.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless().createUserTypeDetails());
+ return map;
+ }
+
+ /**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED}
+ * configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeProfileManaged() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_PROFILE_MANAGED)
+ .setBaseType(FLAG_PROFILE)
+ .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE)
+ .setMaxAllowedPerParent(1)
+ .setLabel(0)
+ .setIconBadge(com.android.internal.R.drawable.ic_corp_icon_badge_case)
+ .setBadgePlain(com.android.internal.R.drawable.ic_corp_badge_case)
+ .setBadgeNoBackground(com.android.internal.R.drawable.ic_corp_badge_no_background)
+ .setBadgeLabels(
+ com.android.internal.R.string.managed_profile_label_badge,
+ com.android.internal.R.string.managed_profile_label_badge_2,
+ com.android.internal.R.string.managed_profile_label_badge_3)
+ .setBadgeColors(
+ com.android.internal.R.color.profile_badge_1,
+ com.android.internal.R.color.profile_badge_2,
+ com.android.internal.R.color.profile_badge_3);
+ }
+
+ /**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SECONDARY}
+ * configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeFullSecondary() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_FULL_SECONDARY)
+ .setBaseType(FLAG_FULL)
+ .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
+ }
+
+ /**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_GUEST} configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeFullGuest() {
+ final boolean ephemeralGuests = Resources.getSystem()
+ .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+ final int flags = FLAG_GUEST | (ephemeralGuests ? FLAG_EPHEMERAL : 0);
+
+ // TODO(b/142482943): Put UMS.initDefaultGuestRestrictions() here; then fetch them from here
+
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_FULL_GUEST)
+ .setBaseType(FLAG_FULL)
+ .setDefaultUserInfoPropertyFlags(flags)
+ .setMaxAllowed(1);
+ }
+
+ /**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_DEMO} configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeFullDemo() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_FULL_DEMO)
+ .setBaseType(FLAG_FULL)
+ .setDefaultUserInfoPropertyFlags(FLAG_DEMO)
+ .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
+ }
+
+ /**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_RESTRICTED}
+ * configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeFullRestricted() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_FULL_RESTRICTED)
+ .setBaseType(FLAG_FULL)
+ .setDefaultUserInfoPropertyFlags(FLAG_RESTRICTED)
+ .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
+ }
+
+ /**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SYSTEM} configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeSystemFull() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_FULL_SYSTEM)
+ .setBaseType(FLAG_SYSTEM | FLAG_FULL);
+ }
+
+ /**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_SYSTEM_HEADLESS}
+ * configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeSystemHeadless() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_SYSTEM_HEADLESS)
+ .setBaseType(FLAG_SYSTEM);
+ }
+}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3c0971b9ec1a..5c4dc2335822 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -556,7 +556,9 @@ static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin
assetmanager->SetConfiguration(configuration);
}
-static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jboolean includeOverlays,
+ jboolean includeLoaders) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
jobject sparse_array =
@@ -567,6 +569,10 @@ static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/
return nullptr;
}
+ // Optionally exclude overlays and loaders.
+ uint64_t exclusion_flags = ((includeOverlays) ? 0U : PROPERTY_OVERLAY)
+ | ((includeLoaders) ? 0U : PROPERTY_LOADER);
+
assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
jstring jpackage_name = env->NewStringUTF(package_name.c_str());
if (jpackage_name == nullptr) {
@@ -577,7 +583,8 @@ static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/
env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
jpackage_name);
return true;
- });
+ }, exclusion_flags);
+
return sparse_array;
}
@@ -1591,7 +1598,7 @@ static const JNINativeMethod gAssetManagerMethods[] = {
{"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
{"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
(void*)NativeSetConfiguration},
- {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
+ {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
(void*)NativeGetAssignedPackageIdentifiers},
// AssetManager file methods.
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ce2717bb2779..8f084abe71a7 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -126,12 +126,11 @@ message ActivityRecordProto {
optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1 [deprecated=true];
optional .com.android.server.wm.IdentifierProto identifier = 2;
optional string state = 3;
- optional bool visible_requested = 4;
+ optional bool visible = 4;
optional bool front_of_task = 5;
optional int32 proc_id = 6;
optional bool translucent = 7;
optional .com.android.server.wm.AppWindowTokenProto app_window_token = 8;
- optional bool visible = 9;
}
message KeyguardControllerProto {
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 091e1c2bb05a..c03957087f26 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -172,6 +172,8 @@ message PowerManagerServiceDumpProto {
repeated SuspendBlockerProto suspend_blockers = 48;
optional WirelessChargerDetectorProto wireless_charger_detector = 49;
optional BatterySaverStateMachineProto battery_saver_state_machine = 50;
+ // Attentive timeout in ms. The timeout is disabled if it is set to -1.
+ optional sint32 attentive_timeout_ms = 51;
}
// A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
@@ -310,6 +312,12 @@ message PowerServiceSettingsAndConfigurationDumpProto {
optional bool is_vr_mode_enabled = 35;
// True if Sidekick is controlling the display and we shouldn't change its power mode.
optional bool draw_wake_lock_override_from_sidekick = 36;
+ // The attentive timeout setting value in milliseconds. Default value is -1.
+ optional sint32 attentive_timeout_setting_ms = 37;
+ // The attentive timeout config value in milliseconds.
+ optional sint32 attentive_timeout_config_ms = 38;
+ // The attentive warning duration config value in milliseconds.
+ optional sint32 attentive_warning_duration_config_ms = 39;
}
message BatterySaverStateMachineProto {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 23566a6df847..653d381e2960 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -235,8 +235,8 @@ message AppWindowTokenProto {
optional WindowContainerThumbnailProto thumbnail = 6;
optional bool fills_parent = 7;
optional bool app_stopped = 8;
- optional bool visible_requested = 9;
- optional bool client_visible = 10;
+ optional bool hidden_requested = 9;
+ optional bool client_hidden = 10;
optional bool defer_hiding_client = 11;
optional bool reported_drawn = 12;
optional bool reported_visible = 13;
@@ -248,9 +248,8 @@ message AppWindowTokenProto {
optional IdentifierProto starting_window = 19;
optional bool starting_displayed = 20;
optional bool starting_moved = 21;
- optional bool visible_set_from_transferred_starting_window = 22;
+ optional bool hidden_set_from_transferred_starting_window = 22;
repeated .android.graphics.RectProto frozen_bounds = 23;
- optional bool visible = 24;
}
/* represents WindowToken */
@@ -260,6 +259,7 @@ message WindowTokenProto {
optional WindowContainerProto window_container = 1;
optional int32 hash_code = 2;
repeated WindowStateProto windows = 3;
+ optional bool hidden = 4;
optional bool waiting_to_show = 5;
optional bool paused = 6;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9f77407b2391..545538be4ce1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -142,7 +142,7 @@
<protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.device.action.UUID" />
<protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" />
- <protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.ALIAS_CHANGED" />
<protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
<protected-broadcast android:name="android.bluetooth.device.action.CLASS_CHANGED" />
<protected-broadcast android:name="android.bluetooth.device.action.ACL_CONNECTED" />
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 17b5f50669a2..6807f9af3980 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -55,15 +55,14 @@
android:layout_centerHorizontal="true"/>
</RelativeLayout>
- <ListView
+ <com.android.internal.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layoutManager="com.android.internal.widget.GridLayoutManager"
android:id="@+id/resolver_list"
android:clipToPadding="false"
android:background="?attr/colorBackgroundFloating"
android:scrollbars="none"
- android:listSelector="@color/transparent"
- android:divider="@null"
android:elevation="1dp"
android:nestedScrollingEnabled="true"/>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 3ecb1dddd916..655d4dde5e57 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -42,4 +42,8 @@
<!-- Allow SystemUI to show the shutdown dialog -->
<bool name="config_showSysuiShutdown">true</bool>
+
+ <!-- The time in milliseconds of prolonged user inactivity after which device goes to sleep,
+ even if wakelocks are held. On TVs, this defaults to 4 hours. -->
+ <integer name="config_attentiveTimeout">14400000</integer>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cb0b5993c420..e6f038a2829c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -959,6 +959,14 @@
The default is false. -->
<bool name="config_suspendWhenScreenOffDueToProximity">false</bool>
+ <!-- The time in milliseconds of prolonged user inactivity after which device goes to sleep,
+ even if wakelocks are held. -->
+ <integer name="config_attentiveTimeout">-1</integer>
+
+ <!-- How long to show a warning message to user before the device goes to sleep after prolonged
+ user inactivity. -->
+ <integer name="config_attentiveWarningDuration">30000</integer>
+
<!-- Control the behavior when the user long presses the power button.
0 - Nothing
1 - Global actions menu
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2cab446f4053..42e62d740edd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -720,8 +720,8 @@
<!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupbackgroundrequest_location">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\u2019s location &lt;b>all the time&lt;/b>?</string>
- <!-- Subtitle of the message shown to the user when the apps requests permission to use the location while app is in foreground and background [CHAR LIMIT=150] -->
- <string name="permgroupbackgroundrequestdetail_location">App currently can access location only while you\u2019re using the app</string>
+ <!-- Subtitle of the message shown to the user when the apps requests permission to use the location while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
+ <string name="permgroupbackgroundrequestdetail_location">This app may want to access your location all the time, even when you\u2019re not using the app. Allow in <annotation id="link">settings</annotation>.</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_calendar">Calendar</string>
@@ -5238,13 +5238,6 @@
<!-- Application name displayed in notifications [CHAR LIMIT=60] -->
<string name="notification_app_name_settings">Settings</string>
- <!-- Title of the overlay warning the user to interact with the device or it will go into standby. [CHAR LIMIT=25] -->
- <string name="standby_warning_title">Standby</string>
- <!-- Message of the overlay warning the user to interact with the device or it will go into standby. [CHAR LIMIT=NONE] -->
- <string name="standby_warning_message" product="tv">The Android TV device will soon turn off; press a button to keep it on.</string>
- <!-- Message of the overlay warning the user to interact with the device or it will go into standby. [CHAR LIMIT=NONE] -->
- <string name="standby_warning_message" product="default">The device will soon turn off; press to keep it on.</string>
-
<!-- Active Permission - accessibility support -->
<!-- Content description of the camera icon in the notification. [CHAR LIMIT=NONE] -->
<string name="notification_appops_camera_active">Camera</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 28809daeaad8..e56bbf6fa08f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2117,6 +2117,8 @@
<java-symbol type="integer" name="config_minimumScreenOffTimeout" />
<java-symbol type="integer" name="config_maximumScreenDimDuration" />
<java-symbol type="fraction" name="config_maximumScreenDimRatio" />
+ <java-symbol type="integer" name="config_attentiveTimeout" />
+ <java-symbol type="integer" name="config_attentiveWarningDuration" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
index 22b23148cbcf..1bc46a79f1c0 100644
--- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
@@ -19,6 +19,7 @@ package android.content;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import androidx.test.filters.LargeTest;
@@ -40,6 +41,6 @@ public class ManagedUserContentResolverTest extends AbstractCrossUserContentReso
@Override
protected UserInfo createUser() throws RemoteException {
return mUm.createProfileForUser("Managed user",
- UserInfo.FLAG_MANAGED_PROFILE, UserHandle.myUserId());
+ UserManager.USER_TYPE_PROFILE_MANAGED, /* flags */ 0, UserHandle.myUserId());
}
}
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 2295eb989108..decc76869a53 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -60,7 +60,7 @@ public class BuildTest extends TestCase {
assertNotEmpty("BRAND", Build.BRAND);
assertNotEmpty("MODEL", Build.MODEL);
assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
- assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE_OR_CODENAME);
+ assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE);
assertNotEmpty("TYPE", Build.TYPE);
Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty.
assertNotEmpty("FINGERPRINT", Build.FINGERPRINT);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 3c89bfd43f17..753f8a05cbe3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -55,12 +55,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "-2006946193": {
- "message": "setClientVisible: %s clientVisible=%b Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-2002500255": {
"message": "Defer removing snapshot surface in %dms",
"level": "VERBOSE",
@@ -79,18 +73,18 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1976550065": {
- "message": "commitVisibility: %s: visible=%b visibleRequested=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1963461591": {
"message": "Removing %s from %s",
"level": "VERBOSE",
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1958209312": {
+ "message": "Clear freezing of %s: hidden=%b freezing=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1953668890": {
"message": "Can't start recents animation, nextAppTransition=%s",
"level": "DEBUG",
@@ -313,6 +307,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1456549051": {
+ "message": "setClientHidden: %s clientHidden=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1455600136": {
"message": "Attempted to add Dream window with unknown token %s. Aborting.",
"level": "WARN",
@@ -547,6 +547,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-931184679": {
+ "message": "Changing app %s hidden=%b performLayout=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-928291778": {
"message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
"level": "VERBOSE",
@@ -835,12 +841,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
- "-374767836": {
- "message": "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-371630969": {
"message": "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
"level": "VERBOSE",
@@ -1207,6 +1207,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "358613119": {
+ "message": "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"371641947": {
"message": "Window Manager Crash %s",
"level": "WTF",
@@ -1267,12 +1273,6 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "466506262": {
- "message": "Clear freezing of %s: visible=%b freezing=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"474000473": {
"message": "No stack above target stack=%s",
"level": "DEBUG",
@@ -1489,12 +1489,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "841702299": {
- "message": "Changing app %s visible=%b performLayout=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"845234215": {
"message": "App is requesting an orientation, return %d for display id=%d",
"level": "VERBOSE",
@@ -1507,6 +1501,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "857751535": {
+ "message": "commitVisibility: %s: hidden=%b hiddenRequested=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"873914452": {
"message": "goodToGo()",
"level": "DEBUG",
@@ -1903,12 +1903,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1746778201": {
- "message": "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
- "level": "INFO",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"1747941491": {
"message": "SURFACE controller=%s alpha=%f matrix=[%f*%f,%f*%f][%f*%f,%f*%f]: %s",
"level": "INFO",
@@ -1999,6 +1993,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "1966564525": {
+ "message": "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"1984470582": {
"message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
"level": "DEBUG",
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index 99dfd0a6d455..2392b333c587 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -32,7 +32,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
- $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \.
+ $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 3658f89abae1..c2e3c6419400 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -92,11 +92,17 @@ public final class Icon implements Parcelable {
* @see #getType
*/
public static final int TYPE_ADAPTIVE_BITMAP = 5;
+ /**
+ * An icon that was created using {@link Icon#createWithAdaptiveBitmapContentUri}.
+ * @see #getType
+ */
+ public static final int TYPE_URI_ADAPTIVE_BITMAP = 6;
/**
* @hide
*/
- @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
+ @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP,
+ TYPE_URI_ADAPTIVE_BITMAP})
public @interface IconType {
}
@@ -113,12 +119,14 @@ public final class Icon implements Parcelable {
// based on the value of mType.
// TYPE_BITMAP: Bitmap
+ // TYPE_ADAPTIVE_BITMAP: Bitmap
// TYPE_RESOURCE: Resources
// TYPE_DATA: DataBytes
private Object mObj1;
// TYPE_RESOURCE: package name
// TYPE_URI: uri string
+ // TYPE_URI_ADAPTIVE_BITMAP: uri string
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private String mString1;
@@ -141,7 +149,8 @@ public final class Icon implements Parcelable {
}
/**
- * @return The {@link android.graphics.Bitmap} held by this {@link #TYPE_BITMAP} Icon.
+ * @return The {@link android.graphics.Bitmap} held by this {@link #TYPE_BITMAP} or
+ * {@link #TYPE_ADAPTIVE_BITMAP} Icon.
* @hide
*/
@UnsupportedAppUsage
@@ -243,11 +252,12 @@ public final class Icon implements Parcelable {
}
/**
- * @return The URI (as a String) for this {@link #TYPE_URI} Icon.
+ * @return The URI (as a String) for this {@link #TYPE_URI} or {@link #TYPE_URI_ADAPTIVE_BITMAP}
+ * Icon.
* @hide
*/
public String getUriString() {
- if (mType != TYPE_URI) {
+ if (mType != TYPE_URI && mType != TYPE_URI_ADAPTIVE_BITMAP) {
throw new IllegalStateException("called getUriString() on " + this);
}
return mString1;
@@ -256,7 +266,7 @@ public final class Icon implements Parcelable {
/**
* Gets the uri used to create this icon.
* <p>
- * Only valid for icons of type {@link #TYPE_URI}.
+ * Only valid for icons of type {@link #TYPE_URI} and {@link #TYPE_URI_ADAPTIVE_BITMAP}.
* Note: This uri may not be available in the future, and it is
* up to the caller to ensure safety if this uri is re-used and/or persisted.
*/
@@ -272,6 +282,7 @@ public final class Icon implements Parcelable {
case TYPE_DATA: return "DATA";
case TYPE_RESOURCE: return "RESOURCE";
case TYPE_URI: return "URI";
+ case TYPE_URI_ADAPTIVE_BITMAP: return "URI_MASKABLE";
default: return "UNKNOWN";
}
}
@@ -380,28 +391,39 @@ public final class Icon implements Parcelable {
BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength())
);
case TYPE_URI:
- final Uri uri = getUri();
- final String scheme = uri.getScheme();
- InputStream is = null;
- if (ContentResolver.SCHEME_CONTENT.equals(scheme)
- || ContentResolver.SCHEME_FILE.equals(scheme)) {
- try {
- is = context.getContentResolver().openInputStream(uri);
- } catch (Exception e) {
- Log.w(TAG, "Unable to load image from URI: " + uri, e);
- }
- } else {
- try {
- is = new FileInputStream(new File(mString1));
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Unable to load image from path: " + uri, e);
- }
- }
+ InputStream is = getUriInputStream(context);
if (is != null) {
return new BitmapDrawable(context.getResources(),
BitmapFactory.decodeStream(is));
}
break;
+ case TYPE_URI_ADAPTIVE_BITMAP:
+ is = getUriInputStream(context);
+ if (is != null) {
+ return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(),
+ BitmapFactory.decodeStream(is)));
+ }
+ break;
+ }
+ return null;
+ }
+
+ private InputStream getUriInputStream(Context context) {
+ final Uri uri = getUri();
+ final String scheme = uri.getScheme();
+ if (ContentResolver.SCHEME_CONTENT.equals(scheme)
+ || ContentResolver.SCHEME_FILE.equals(scheme)) {
+ try {
+ return context.getContentResolver().openInputStream(uri);
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load image from URI: " + uri, e);
+ }
+ } else {
+ try {
+ return new FileInputStream(new File(mString1));
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Unable to load image from path: " + uri, e);
+ }
}
return null;
}
@@ -475,6 +497,7 @@ public final class Icon implements Parcelable {
dataStream.writeInt(getResId());
break;
case TYPE_URI:
+ case TYPE_URI_ADAPTIVE_BITMAP:
dataStream.writeUTF(getUriString());
break;
}
@@ -513,6 +536,9 @@ public final class Icon implements Parcelable {
case TYPE_URI:
final String uriOrPath = inputStream.readUTF();
return createWithContentUri(uriOrPath);
+ case TYPE_URI_ADAPTIVE_BITMAP:
+ final String uri = inputStream.readUTF();
+ return createWithAdaptiveBitmapContentUri(uri);
}
}
return null;
@@ -545,6 +571,7 @@ public final class Icon implements Parcelable {
return getResId() == otherIcon.getResId()
&& Objects.equals(getResPackage(), otherIcon.getResPackage());
case TYPE_URI:
+ case TYPE_URI_ADAPTIVE_BITMAP:
return Objects.equals(getUriString(), otherIcon.getUriString());
}
return false;
@@ -665,12 +692,40 @@ public final class Icon implements Parcelable {
if (uri == null) {
throw new IllegalArgumentException("Uri must not be null.");
}
- final Icon rep = new Icon(TYPE_URI);
- rep.mString1 = uri.toString();
+ return createWithContentUri(uri.toString());
+ }
+
+ /**
+ * Create an Icon pointing to an image file specified by URI. Image file should follow the icon
+ * design guideline defined by {@link AdaptiveIconDrawable}.
+ *
+ * @param uri A uri referring to local content:// or file:// image data.
+ */
+ @NonNull
+ public static Icon createWithAdaptiveBitmapContentUri(@NonNull String uri) {
+ if (uri == null) {
+ throw new IllegalArgumentException("Uri must not be null.");
+ }
+ final Icon rep = new Icon(TYPE_URI_ADAPTIVE_BITMAP);
+ rep.mString1 = uri;
return rep;
}
/**
+ * Create an Icon pointing to an image file specified by URI. Image file should follow the icon
+ * design guideline defined by {@link AdaptiveIconDrawable}.
+ *
+ * @param uri A uri referring to local content:// or file:// image data.
+ */
+ @NonNull
+ public static Icon createWithAdaptiveBitmapContentUri(@NonNull Uri uri) {
+ if (uri == null) {
+ throw new IllegalArgumentException("Uri must not be null.");
+ }
+ return createWithAdaptiveBitmapContentUri(uri.toString());
+ }
+
+ /**
* Store a color to use whenever this Icon is drawn.
*
* @param tint a color, as in {@link Drawable#setTint(int)}
@@ -758,6 +813,7 @@ public final class Icon implements Parcelable {
}
break;
case TYPE_URI:
+ case TYPE_URI_ADAPTIVE_BITMAP:
sb.append(" uri=").append(getUriString());
break;
}
@@ -809,6 +865,7 @@ public final class Icon implements Parcelable {
mObj1 = a;
break;
case TYPE_URI:
+ case TYPE_URI_ADAPTIVE_BITMAP:
final String uri = in.readString();
mString1 = uri;
break;
@@ -840,6 +897,7 @@ public final class Icon implements Parcelable {
dest.writeBlob(getDataBytes(), getDataOffset(), getDataLength());
break;
case TYPE_URI:
+ case TYPE_URI_ADAPTIVE_BITMAP:
dest.writeString(getUriString());
break;
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 16dbbf61351a..18934fd55bad 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -43,20 +43,22 @@ static const std::string kResourcesArsc("resources.arsc");
ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
const std::string& path,
time_t last_mod_time,
- bool for_loader)
+ package_property_t property_flags)
: zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
- for_loader_(for_loader) {
+ property_flags_(property_flags) {
}
std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
bool for_loader) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/,
- for_loader);
+ package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
+ (for_loader ? PROPERTY_LOADER : 0U);
+ return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
bool system) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/);
+ package_property_t flags = PROPERTY_DYNAMIC | (system ? PROPERTY_SYSTEM : 0U);
+ return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
@@ -74,27 +76,33 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
LOG(ERROR) << "failed to load IDMAP " << idmap_path;
return {};
}
- return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset),
- std::move(loaded_idmap), system, true /*load_as_shared_library*/);
+
+ return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(),
+ std::move(idmap_asset),
+ std::move(loaded_idmap),
+ PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U));
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
const std::string& friendly_name,
bool system, bool force_shared_lib,
bool for_loader) {
+ package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
+ (force_shared_lib ? PROPERTY_DYNAMIC : 0U) |
+ (for_loader ? PROPERTY_LOADER : 0U);
return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
- system, force_shared_lib, for_loader);
+ flags);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
bool for_loader) {
- return LoadArscImpl({} /*fd*/, path, for_loader);
+ return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(unique_fd fd,
const std::string& friendly_name,
bool for_loader) {
- return LoadArscImpl(std::move(fd), friendly_name, for_loader);
+ return LoadArscImpl(std::move(fd), friendly_name, for_loader ? PROPERTY_LOADER : 0U);
}
std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
@@ -120,8 +128,7 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library,
- bool for_loader) {
+ std::unique_ptr<const LoadedIdmap> loaded_idmap, package_property_t property_flags) {
::ZipArchiveHandle unmanaged_handle;
int32_t result;
if (fd >= 0) {
@@ -141,7 +148,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
// Wrap the handle in a unique_ptr so it gets automatically closed.
std::unique_ptr<ApkAssets>
- loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, for_loader));
+ loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags));
// Find the resource table.
::ZipEntry entry;
@@ -170,9 +177,8 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
const StringPiece data(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
- loaded_apk->loaded_arsc_ =
- LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library,
- for_loader);
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
+ property_flags);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
@@ -184,7 +190,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
const std::string& path,
- bool for_loader) {
+ package_property_t property_flags) {
std::unique_ptr<Asset> resources_asset;
if (fd >= 0) {
@@ -201,13 +207,14 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
time_t last_mod_time = getFileModDate(path.c_str());
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, path, last_mod_time, for_loader));
+ std::unique_ptr<ApkAssets> loaded_apk(
+ new ApkAssets(nullptr, path, last_mod_time, property_flags));
loaded_apk->resources_asset_ = std::move(resources_asset);
const StringPiece data(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, false, false, for_loader);
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
return {};
@@ -320,8 +327,8 @@ bool ApkAssets::ForEachFile(const std::string& root_path,
}
bool ApkAssets::IsUpToDate() const {
- // Loaders are invalidated by the app, not the system, so assume up to date
- if (for_loader_) {
+ if (IsLoader()) {
+ // Loaders are invalidated by the app, not the system, so assume up to date.
return true;
}
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 2b69c923597f..773353d32d51 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -44,8 +44,8 @@ static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_
}
OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
- : data_header_(loaded_idmap->data_header_),
- idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
+ : data_header_(loaded_idmap->data_header_),
+ idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
OverlayStringPool::~OverlayStringPool() {
uninit();
@@ -188,11 +188,12 @@ LoadedIdmap::LoadedIdmap(const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_overlay_entry* overlay_entries,
- ResStringPool* string_pool) : header_(header),
- data_header_(data_header),
- target_entries_(target_entries),
- overlay_entries_(overlay_entries),
- string_pool_(string_pool) {
+ ResStringPool* string_pool)
+ : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ overlay_entries_(overlay_entries),
+ string_pool_(string_pool) {
size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
arraysize(header_->overlay_path));
@@ -264,7 +265,7 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_da
}
}
- // Can't use make_unique because LoadedImpl constructor is private.
+ // Can't use make_unique because LoadedIdmap constructor is private.
std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
new LoadedIdmap(header, data_header, target_entries, overlay_entries,
idmap_string_pool.release()));
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c8962416d082..e35c0249fbdf 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -397,9 +397,7 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
}
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
- bool system,
- bool load_as_shared_library,
- bool for_loader) {
+ package_property_t property_flags) {
ATRACE_NAME("LoadedPackage::Load");
std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
@@ -413,17 +411,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
- loaded_package->system_ = system;
+ if ((property_flags & PROPERTY_SYSTEM) != 0) {
+ loaded_package->property_flags_ |= PROPERTY_SYSTEM;
+ }
- loaded_package->package_id_ = dtohl(header->id);
- if (loaded_package->package_id_ == 0 ||
- (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) {
- // Package ID of 0 means this is a shared library.
- loaded_package->dynamic_ = true;
+ if ((property_flags & PROPERTY_LOADER) != 0) {
+ loaded_package->property_flags_ |= PROPERTY_LOADER;
}
- if (for_loader) {
- loaded_package->custom_loader_ = true;
+ if ((property_flags & PROPERTY_OVERLAY) != 0) {
+ // Overlay resources must have an exclusive resource id space for referencing internal
+ // resources.
+ loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC;
+ }
+
+ loaded_package->package_id_ = dtohl(header->id);
+ if (loaded_package->package_id_ == 0 ||
+ (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) {
+ loaded_package->property_flags_ |= PROPERTY_DYNAMIC;
}
if (header->header.headerSize >= sizeof(ResTable_package)) {
@@ -677,7 +682,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
- bool load_as_shared_library, bool for_loader) {
+ package_property_t property_flags) {
const ResTable_header* header = chunk.header<ResTable_header>();
if (header == nullptr) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
@@ -720,7 +725,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
packages_seen++;
std::unique_ptr<const LoadedPackage> loaded_package =
- LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader);
+ LoadedPackage::Load(child_chunk, property_flags);
if (!loaded_package) {
return false;
}
@@ -744,24 +749,18 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap,
- bool system,
- bool load_as_shared_library,
- bool for_loader) {
+ package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
- loaded_arsc->system_ = system;
ChunkIterator iter(data.data(), data.size());
while (iter.HasNext()) {
const Chunk chunk = iter.Next();
switch (chunk.type()) {
case RES_TABLE_TYPE:
- if (!loaded_arsc->LoadTable(chunk,
- loaded_idmap,
- load_as_shared_library,
- for_loader)) {
+ if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) {
return {};
}
break;
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 20472872263e..af802b0e50b9 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -76,10 +76,10 @@ class ApkAssets {
// Takes ownership of the file descriptor.
static std::unique_ptr<const ApkAssets> LoadArsc(base::unique_fd fd,
const std::string& friendly_name,
- bool resource_loader = false);
+ bool for_loader = false);
// Creates a totally empty ApkAssets with no resources table and no file entries.
- static std::unique_ptr<const ApkAssets> LoadEmpty(bool resource_loader = false);
+ static std::unique_ptr<const ApkAssets> LoadEmpty(bool for_loader = false);
std::unique_ptr<Asset> Open(const std::string& path,
Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -100,12 +100,12 @@ class ApkAssets {
return loaded_idmap_.get();
}
- inline bool IsOverlay() const {
- return idmap_asset_.get() != nullptr;
+ inline bool IsLoader() const {
+ return (property_flags_ & PROPERTY_LOADER) != 0;
}
- inline bool IsLoader() const {
- return for_loader_;
+ inline bool IsOverlay() const {
+ return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
bool IsUpToDate() const;
@@ -119,24 +119,23 @@ class ApkAssets {
static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<const LoadedIdmap> loaded_idmap,
- bool system, bool load_as_shared_library,
- bool resource_loader = false);
+ package_property_t property_flags);
static std::unique_ptr<const ApkAssets> LoadArscImpl(base::unique_fd fd,
const std::string& path,
- bool resource_loader = false);
+ package_property_t property_flags);
ApkAssets(ZipArchiveHandle unmanaged_handle,
const std::string& path,
time_t last_mod_time,
- bool for_loader = false);
+ package_property_t property_flags);
using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
ZipArchivePtr zip_handle_;
const std::string path_;
time_t last_mod_time_;
- bool for_loader_;
+ package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 20e40234b418..00cbbcad56e6 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -263,10 +263,13 @@ class AssetManager2 {
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
- void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
+ void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags = 0U) const {
for (const PackageGroup& package_group : package_groups_) {
- if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
- package_group.dynamic_ref_table->mAssignedPackageId)) {
+ const auto loaded_package = package_group.packages_.front().loaded_package_;
+ if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
+ && !func(loaded_package->GetPackageName(),
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
return;
}
}
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index ba1beaa7827c..6cbda07b6950 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -69,6 +69,14 @@ struct TypeSpec {
}
};
+using package_property_t = uint32_t;
+enum : package_property_t {
+ PROPERTY_DYNAMIC = 1,
+ PROPERTY_LOADER = 2,
+ PROPERTY_OVERLAY = 4,
+ PROPERTY_SYSTEM = 8,
+};
+
// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
// ResTable_type pointers.
// TypeSpecPtr is a managed pointer that knows how to delete itself.
@@ -131,9 +139,8 @@ class LoadedPackage {
return iterator(this, resource_ids_.size() + 1, 0);
}
- static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, bool system,
- bool load_as_shared_library,
- bool load_as_custom_loader);
+ static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
+ package_property_t property_flags);
~LoadedPackage();
@@ -170,17 +177,26 @@ class LoadedPackage {
// Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
inline bool IsDynamic() const {
- return dynamic_;
+ return (property_flags_ & PROPERTY_DYNAMIC) != 0;
+ }
+
+ // Returns true if this package is a Runtime Resource Overlay.
+ inline bool IsOverlay() const {
+ return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
// Returns true if this package originates from a system provided resource.
inline bool IsSystem() const {
- return system_;
+ return (property_flags_ & PROPERTY_SYSTEM) != 0;
}
- // Returns true if this package is a custom loader and should behave like an overlay
+ // Returns true if this package is a custom loader and should behave like an overlay.
inline bool IsCustomLoader() const {
- return custom_loader_;
+ return (property_flags_ & PROPERTY_LOADER) != 0;
+ }
+
+ inline package_property_t GetPropertyFlags() const {
+ return property_flags_;
}
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
@@ -248,12 +264,10 @@ class LoadedPackage {
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
std::string package_name_;
+ bool defines_overlayable_ = false;
int package_id_ = -1;
int type_id_offset_ = 0;
- bool dynamic_ = false;
- bool system_ = false;
- bool custom_loader_ = false;
- bool defines_overlayable_ = false;
+ package_property_t property_flags_ = 0U;
ByteBucketArray<TypeSpecPtr> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
@@ -274,9 +288,7 @@ class LoadedArsc {
// ID.
static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap = nullptr,
- bool system = false,
- bool load_as_shared_library = false,
- bool for_loader = false);
+ package_property_t property_flags = 0U);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
static std::unique_ptr<const LoadedArsc> CreateEmpty();
@@ -296,28 +308,15 @@ class LoadedArsc {
return packages_;
}
- // Returns true if this is a system provided resource.
- inline bool IsSystem() const {
- return system_;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
LoadedArsc() = default;
- bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library,
- bool for_loader);
-
- static std::unique_ptr<const LoadedArsc> LoadData(std::unique_ptr<LoadedArsc>& loaded_arsc,
- const char* data,
- size_t length,
- const LoadedIdmap* loaded_idmap = nullptr,
- bool load_as_shared_library = false,
- bool for_loader = false);
+ bool LoadTable(
+ const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags);
std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
- bool system_ = false;
};
} // namespace android
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 82dd33523c75..8615069e98dd 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -144,8 +144,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
- true /*load_as_shared_library*/);
+ LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC);
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -227,9 +226,7 @@ TEST(LoadedArscTest, LoadOverlayable) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
- false /*load_as_shared_library*/);
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package = loaded_arsc->GetPackageById(
@@ -346,7 +343,7 @@ TEST(LoadedArscTest, LoadCustomLoader) {
asset->getLength());
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(data, nullptr, false, false, true);
+ LoadedArsc::Load(data, nullptr, PROPERTY_LOADER);
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index b363686c2df5..2f1eeda9c4cf 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -16,15 +16,21 @@
package android.location;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
/**
- * This class represents the current state of the GNSS engine.
- * This class is used in conjunction with the {@link GnssStatus.Callback}.
+ * This class represents the current state of the GNSS engine and is used in conjunction with
+ * {@link GnssStatus.Callback}.
+ *
+ * @see LocationManager#registerGnssStatusCallback
+ * @see GnssStatus.Callback
*/
public final class GnssStatus {
@@ -52,75 +58,88 @@ public final class GnssStatus {
/** @hide */
public static final int CONSTELLATION_COUNT = 8;
- /** @hide */
- public static final int GNSS_SV_FLAGS_NONE = 0;
- /** @hide */
- public static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA = (1 << 0);
- /** @hide */
- public static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA = (1 << 1);
- /** @hide */
- public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
- /** @hide */
- public static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY = (1 << 3);
+ private static final int SVID_FLAGS_NONE = 0;
+ private static final int SVID_FLAGS_HAS_EPHEMERIS_DATA = (1 << 0);
+ private static final int SVID_FLAGS_HAS_ALMANAC_DATA = (1 << 1);
+ private static final int SVID_FLAGS_USED_IN_FIX = (1 << 2);
+ private static final int SVID_FLAGS_HAS_CARRIER_FREQUENCY = (1 << 3);
- /** @hide */
- public static final int SVID_SHIFT_WIDTH = 8;
- /** @hide */
- public static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
- /** @hide */
- public static final int CONSTELLATION_TYPE_MASK = 0xf;
+ private static final int SVID_SHIFT_WIDTH = 8;
+ private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
+ private static final int CONSTELLATION_TYPE_MASK = 0xf;
/**
* Used for receiving notifications when GNSS events happen.
+ *
+ * @see LocationManager#registerGnssStatusCallback
*/
public static abstract class Callback {
/**
* Called when GNSS system has started.
*/
- public void onStarted() {}
+ public void onStarted() {
+ }
/**
* Called when GNSS system has stopped.
*/
- public void onStopped() {}
+ public void onStopped() {
+ }
/**
* Called when the GNSS system has received its first fix since starting.
+ *
* @param ttffMillis the time from start to first fix in milliseconds.
*/
- public void onFirstFix(int ttffMillis) {}
+ public void onFirstFix(int ttffMillis) {
+ }
/**
* Called periodically to report GNSS satellite status.
+ *
* @param status the current status of all satellites.
*/
- public void onSatelliteStatusChanged(GnssStatus status) {}
+ public void onSatelliteStatusChanged(@NonNull GnssStatus status) {
+ }
}
/**
* Constellation type.
+ *
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({CONSTELLATION_UNKNOWN, CONSTELLATION_GPS, CONSTELLATION_SBAS, CONSTELLATION_GLONASS,
CONSTELLATION_QZSS, CONSTELLATION_BEIDOU, CONSTELLATION_GALILEO, CONSTELLATION_IRNSS})
- public @interface ConstellationType {}
-
- final int[] mSvidWithFlags;
- final float[] mCn0DbHz;
- final float[] mElevations;
- final float[] mAzimuths;
- final int mSvCount;
- final float[] mCarrierFrequencies;
+ public @interface ConstellationType {
+ }
/**
+ * Create a GnssStatus that wraps the given arguments without any additional overhead. Callers
+ * are responsible for guaranteeing that the arguments are never modified after calling this
+ * method.
+ *
* @hide
*/
- public GnssStatus(int svCount, int[] svidWithFlags, float[] cn0s, float[] elevations,
+ @NonNull
+ public static GnssStatus wrap(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
+ float[] elevations, float[] azimuths, float[] carrierFrequencies) {
+ return new GnssStatus(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
+ carrierFrequencies);
+ }
+
+ private final int mSvCount;
+ private final int[] mSvidWithFlags;
+ private final float[] mCn0DbHzs;
+ private final float[] mElevations;
+ private final float[] mAzimuths;
+ private final float[] mCarrierFrequencies;
+
+ private GnssStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs, float[] elevations,
float[] azimuths, float[] carrierFrequencies) {
mSvCount = svCount;
mSvidWithFlags = svidWithFlags;
- mCn0DbHz = cn0s;
+ mCn0DbHzs = cn0DbHzs;
mElevations = elevations;
mAzimuths = azimuths;
mCarrierFrequencies = carrierFrequencies;
@@ -129,6 +148,7 @@ public final class GnssStatus {
/**
* Gets the total number of satellites in satellite list.
*/
+ @IntRange(from = 0)
public int getSatelliteCount() {
return mSvCount;
}
@@ -136,11 +156,11 @@ public final class GnssStatus {
/**
* Retrieves the constellation type of the satellite at the specified index.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
@ConstellationType
- public int getConstellationType(int satIndex) {
- return ((mSvidWithFlags[satIndex] >> CONSTELLATION_TYPE_SHIFT_WIDTH)
+ public int getConstellationType(@IntRange(from = 0) int satelliteIndex) {
+ return ((mSvidWithFlags[satelliteIndex] >> CONSTELLATION_TYPE_SHIFT_WIDTH)
& CONSTELLATION_TYPE_MASK);
}
@@ -158,110 +178,113 @@ public final class GnssStatus {
* <li>SBAS: 120-151, 183-192</li>
* <li>GLONASS: One of: OSN, or FCN+100
* <ul>
- * <li>1-24 as the orbital slot number (OSN) (preferred, if known)</li>
- * <li>93-106 as the frequency channel number (FCN) (-7 to +6) plus 100.
- * i.e. encode FCN of -7 as 93, 0 as 100, and +6 as 106</li>
+ * <li>1-24 as the orbital slot number (OSN) (preferred, if known)</li>
+ * <li>93-106 as the frequency channel number (FCN) (-7 to +6) plus 100.
+ * i.e. encode FCN of -7 as 93, 0 as 100, and +6 as 106</li>
* </ul></li>
* <li>QZSS: 193-200</li>
* <li>Galileo: 1-36</li>
* <li>Beidou: 1-37</li>
* </ul>
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public int getSvid(int satIndex) {
- return mSvidWithFlags[satIndex] >> SVID_SHIFT_WIDTH;
+ @IntRange(from = 1, to = 200)
+ public int getSvid(@IntRange(from = 0) int satelliteIndex) {
+ return mSvidWithFlags[satelliteIndex] >> SVID_SHIFT_WIDTH;
}
/**
* Retrieves the carrier-to-noise density at the antenna of the satellite at the specified index
* in dB-Hz.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public float getCn0DbHz(int satIndex) {
- return mCn0DbHz[satIndex];
+ @FloatRange(from = 0, to = 63)
+ public float getCn0DbHz(@IntRange(from = 0) int satelliteIndex) {
+ return mCn0DbHzs[satelliteIndex];
}
/**
* Retrieves the elevation of the satellite at the specified index.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public float getElevationDegrees(int satIndex) {
- return mElevations[satIndex];
+ @FloatRange(from = -90, to = 90)
+ public float getElevationDegrees(@IntRange(from = 0) int satelliteIndex) {
+ return mElevations[satelliteIndex];
}
/**
* Retrieves the azimuth the satellite at the specified index.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public float getAzimuthDegrees(int satIndex) {
- return mAzimuths[satIndex];
+ @FloatRange(from = 0, to = 360)
+ public float getAzimuthDegrees(@IntRange(from = 0) int satelliteIndex) {
+ return mAzimuths[satelliteIndex];
}
/**
* Reports whether the satellite at the specified index has ephemeris data.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public boolean hasEphemerisData(int satIndex) {
- return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ public boolean hasEphemerisData(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_HAS_EPHEMERIS_DATA) != 0;
}
/**
* Reports whether the satellite at the specified index has almanac data.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public boolean hasAlmanacData(int satIndex) {
- return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ public boolean hasAlmanacData(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_HAS_ALMANAC_DATA) != 0;
}
/**
* Reports whether the satellite at the specified index was used in the calculation of the most
* recent position fix.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public boolean usedInFix(int satIndex) {
- return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ public boolean usedInFix(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_USED_IN_FIX) != 0;
}
/**
- * Reports whether a valid {@link #getCarrierFrequencyHz(int satIndex)} is available.
+ * Reports whether a valid {@link #getCarrierFrequencyHz(int satelliteIndex)} is available.
*
- * @param satIndex the index of the satellite in the list.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public boolean hasCarrierFrequencyHz(int satIndex) {
- return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0;
+ public boolean hasCarrierFrequencyHz(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_HAS_CARRIER_FREQUENCY) != 0;
}
/**
* Gets the carrier frequency of the signal tracked.
*
- * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
- * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
+ * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60
+ * MHz, L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
* common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
*
* For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two measurements
- * will be reported for this same satellite, in one all the values related to L1 will be filled,
- * and in the other all of the values related to L5 will be filled.
- *
- * <p>The value is only available if {@link #hasCarrierFrequencyHz(int satIndex)} is {@code true}.
+ * will be reported for this same satellite, in one all the values related to L1 will be
+ * filled, and in the other all of the values related to L5 will be filled.
*
- * @param satIndex the index of the satellite in the list.
+ * <p>The value is only available if {@link #hasCarrierFrequencyHz(int satelliteIndex)} is
+ * {@code true}.
*
- * @return the carrier frequency of the signal tracked in Hz.
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
*/
- public float getCarrierFrequencyHz(int satIndex) {
- return mCarrierFrequencies[satIndex];
+ @FloatRange(from = 0)
+ public float getCarrierFrequencyHz(@IntRange(from = 0) int satelliteIndex) {
+ return mCarrierFrequencies[satelliteIndex];
}
/**
- * Returns the string representation of a constellation type. For example,
- * {@link #CONSTELLATION_GPS} is represented by the string GPS.
+ * Returns the string representation of a constellation type.
*
* @param constellationType the constellation type.
* @return the string representation.
@@ -290,4 +313,108 @@ public final class GnssStatus {
return Integer.toString(constellationType);
}
}
+
+ /**
+ * Builder class to help create new GnssStatus instances.
+ */
+ public static final class Builder {
+
+ private final ArrayList<GnssSvInfo> mSatellites = new ArrayList<>();
+
+ /**
+ * Adds a new satellite to the Builder.
+ *
+ * @param constellationType one of the CONSTELLATION_* constants
+ * @param svid the space vehicle identifier
+ * @param cn0DbHz carrier-to-noise density at the antenna in dB-Hz
+ * @param elevation satellite elevation in degrees
+ * @param azimuth satellite azimuth in degrees
+ * @param hasEphemeris whether the satellite has ephemeris data
+ * @param hasAlmanac whether the satellite has almanac data
+ * @param usedInFix whether the satellite was used in the most recent location fix
+ * @param hasCarrierFrequency whether carrier frequency data is available
+ * @param carrierFrequency satellite carrier frequency in Hz
+ */
+ @NonNull
+ public Builder addSatellite(@ConstellationType int constellationType,
+ @IntRange(from = 1, to = 200) int svid,
+ @FloatRange(from = 0, to = 63) float cn0DbHz,
+ @FloatRange(from = -90, to = 90) float elevation,
+ @FloatRange(from = 0, to = 360) float azimuth,
+ boolean hasEphemeris,
+ boolean hasAlmanac,
+ boolean usedInFix,
+ boolean hasCarrierFrequency,
+ @FloatRange(from = 0) float carrierFrequency) {
+ mSatellites.add(new GnssSvInfo(constellationType, svid, cn0DbHz, elevation, azimuth,
+ hasEphemeris, hasAlmanac, usedInFix, hasCarrierFrequency, carrierFrequency));
+ return this;
+ }
+
+ /**
+ * Clears all satellites in the Builder.
+ */
+ @NonNull
+ public Builder clearSatellites() {
+ mSatellites.clear();
+ return this;
+ }
+
+ /**
+ * Builds a new GnssStatus based on the satellite information in the Builder.
+ */
+ @NonNull
+ public GnssStatus build() {
+ int svCount = mSatellites.size();
+ int[] svidWithFlags = new int[svCount];
+ float[] cn0DbHzs = new float[svCount];
+ float[] elevations = new float[svCount];
+ float[] azimuths = new float[svCount];
+ float[] carrierFrequencies = new float[svCount];
+
+ for (int i = 0; i < svidWithFlags.length; i++) {
+ svidWithFlags[i] = mSatellites.get(i).mSvidWithFlags;
+ }
+ for (int i = 0; i < cn0DbHzs.length; i++) {
+ cn0DbHzs[i] = mSatellites.get(i).mCn0DbHz;
+ }
+ for (int i = 0; i < elevations.length; i++) {
+ elevations[i] = mSatellites.get(i).mElevation;
+ }
+ for (int i = 0; i < azimuths.length; i++) {
+ azimuths[i] = mSatellites.get(i).mAzimuth;
+ }
+ for (int i = 0; i < carrierFrequencies.length; i++) {
+ carrierFrequencies[i] = mSatellites.get(i).mCarrierFrequency;
+ }
+
+ return wrap(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
+ carrierFrequencies);
+ }
+ }
+
+ private static class GnssSvInfo {
+
+ private final int mSvidWithFlags;
+ private final float mCn0DbHz;
+ private final float mElevation;
+ private final float mAzimuth;
+ private final float mCarrierFrequency;
+
+ private GnssSvInfo(int constellationType, int svid, float cn0DbHz,
+ float elevation, float azimuth, boolean hasEphemeris, boolean hasAlmanac,
+ boolean usedInFix, boolean hasCarrierFrequency, float carrierFrequency) {
+ mSvidWithFlags = (svid << SVID_SHIFT_WIDTH)
+ | ((constellationType & CONSTELLATION_TYPE_MASK)
+ << CONSTELLATION_TYPE_SHIFT_WIDTH)
+ | (hasEphemeris ? SVID_FLAGS_HAS_EPHEMERIS_DATA : SVID_FLAGS_NONE)
+ | (hasAlmanac ? SVID_FLAGS_HAS_ALMANAC_DATA : SVID_FLAGS_NONE)
+ | (usedInFix ? SVID_FLAGS_USED_IN_FIX : SVID_FLAGS_NONE)
+ | (hasCarrierFrequency ? SVID_FLAGS_HAS_CARRIER_FREQUENCY : SVID_FLAGS_NONE);
+ mCn0DbHz = cn0DbHz;
+ mElevation = elevation;
+ mAzimuth = azimuth;
+ mCarrierFrequency = carrierFrequency;
+ }
+ }
}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 609a15e1be7e..496885cd1f37 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -16,8 +16,7 @@
package android.location;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
+import android.annotation.NonNull;
import android.util.SparseArray;
import java.util.Iterator;
@@ -25,20 +24,18 @@ import java.util.NoSuchElementException;
/**
- * This class represents the current state of the GPS engine.
+ * This class represents the current state of the GPS engine and is used in conjunction with {@link
+ * GpsStatus.Listener}.
*
- * <p>This class is used in conjunction with the {@link Listener} interface.
- *
- * @deprecated use {@link GnssStatus} and {@link GnssStatus.Callback}.
+ * @deprecated Use {@link GnssStatus} instead.
*/
@Deprecated
public final class GpsStatus {
- private static final int NUM_SATELLITES = 255;
+ private static final int MAX_SATELLITES = 255;
private static final int GLONASS_SVID_OFFSET = 64;
private static final int BEIDOU_SVID_OFFSET = 200;
private static final int SBAS_SVID_OFFSET = -87;
- /* These package private values are modified by the LocationManager class */
private int mTimeToFirstFix;
private final SparseArray<GpsSatellite> mSatellites = new SparseArray<>();
@@ -80,12 +77,7 @@ public final class GpsStatus {
}
}
- private Iterable<GpsSatellite> mSatelliteList = new Iterable<GpsSatellite>() {
- @Override
- public Iterator<GpsSatellite> iterator() {
- return new SatelliteIterator();
- }
- };
+ private Iterable<GpsSatellite> mSatelliteList = SatelliteIterator::new;
/**
* Event sent when the GPS system has started.
@@ -111,7 +103,8 @@ public final class GpsStatus {
/**
* Used for receiving notifications when GPS status has changed.
- * @deprecated use {@link GnssStatus.Callback} instead.
+ *
+ * @deprecated Use {@link GnssStatus.Callback} instead.
*/
@Deprecated
public interface Listener {
@@ -148,17 +141,31 @@ public final class GpsStatus {
void onNmeaReceived(long timestamp, String nmea);
}
- // For API-compat a public ctor() is not available
- GpsStatus() {}
-
- private void setStatus(int svCount, int[] svidWithFlags, float[] cn0s, float[] elevations,
- float[] azimuths) {
- clearSatellites();
- for (int i = 0; i < svCount; i++) {
- final int constellationType =
- (svidWithFlags[i] >> GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH)
- & GnssStatus.CONSTELLATION_TYPE_MASK;
- int prn = svidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH;
+ /**
+ * Builds a GpsStatus from the given GnssStatus.
+ */
+ @NonNull
+ public static GpsStatus create(@NonNull GnssStatus gnssStatus, int timeToFirstFix) {
+ GpsStatus status = new GpsStatus();
+ status.setStatus(gnssStatus, timeToFirstFix);
+ return status;
+ }
+
+ private GpsStatus() {
+ }
+
+ /**
+ * @hide
+ */
+ void setStatus(GnssStatus status, int timeToFirstFix) {
+ for (int i = 0; i < mSatellites.size(); i++) {
+ mSatellites.valueAt(i).mValid = false;
+ }
+
+ mTimeToFirstFix = timeToFirstFix;
+ for (int i = 0; i < status.getSatelliteCount(); i++) {
+ int constellationType = status.getConstellationType(i);
+ int prn = status.getSvid(i);
// Other satellites passed through these APIs before GnssSvStatus was availble.
// GPS, SBAS & QZSS can pass through at their nominally
// assigned prn number (as long as it fits in the valid 0-255 range below.)
@@ -175,42 +182,24 @@ public final class GpsStatus {
(constellationType != GnssStatus.CONSTELLATION_QZSS)) {
continue;
}
- if (prn > 0 && prn <= NUM_SATELLITES) {
- GpsSatellite satellite = mSatellites.get(prn);
- if (satellite == null) {
- satellite = new GpsSatellite(prn);
- mSatellites.put(prn, satellite);
- }
-
- satellite.mValid = true;
- satellite.mSnr = cn0s[i];
- satellite.mElevation = elevations[i];
- satellite.mAzimuth = azimuths[i];
- satellite.mHasEphemeris =
- (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
- satellite.mHasAlmanac =
- (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
- satellite.mUsedInFix =
- (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ if (prn <= 0 || prn > MAX_SATELLITES) {
+ continue;
}
- }
- }
- /**
- * Copies GPS satellites information from GnssStatus object.
- * Since this method is only used within {@link LocationManager#getGpsStatus},
- * it does not need to be synchronized.
- * @hide
- */
- void setStatus(GnssStatus status, int timeToFirstFix) {
- mTimeToFirstFix = timeToFirstFix;
- setStatus(status.mSvCount, status.mSvidWithFlags, status.mCn0DbHz, status.mElevations,
- status.mAzimuths);
- }
+ GpsSatellite satellite = mSatellites.get(prn);
+ if (satellite == null) {
+ satellite = new GpsSatellite(prn);
+ mSatellites.put(prn, satellite);
+ }
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- void setTimeToFirstFix(int ttff) {
- mTimeToFirstFix = ttff;
+ satellite.mValid = true;
+ satellite.mSnr = status.getCn0DbHz(i);
+ satellite.mElevation = status.getElevationDegrees(i);
+ satellite.mAzimuth = status.getAzimuthDegrees(i);
+ satellite.mHasEphemeris = status.hasEphemerisData(i);
+ satellite.mHasAlmanac = status.hasAlmanacData(i);
+ satellite.mUsedInFix = status.usedInFix(i);
+ }
}
/**
@@ -240,14 +229,7 @@ public final class GpsStatus {
* @return the maximum number of satellites
*/
public int getMaxSatellites() {
- return NUM_SATELLITES;
+ return mSatellites.size();
}
- private void clearSatellites() {
- int satellitesCount = mSatellites.size();
- for (int i = 0; i < satellitesCount; i++) {
- GpsSatellite satellite = mSatellites.valueAt(i);
- satellite.mValid = false;
- }
- }
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 39a7e25cb68b..75e1cd45689c 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1818,15 +1818,14 @@ public class LocationManager {
Log.w(TAG, ex);
}
- if (status == null) {
- status = new GpsStatus();
- }
- // When mGnssStatus is null, that means that this method is called outside
- // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus();
int ttff = mGnssStatusListenerManager.getTtff();
if (gnssStatus != null) {
- status.setStatus(gnssStatus, ttff);
+ if (status == null) {
+ status = GpsStatus.create(gnssStatus, ttff);
+ } else {
+ status.setStatus(gnssStatus, ttff);
+ }
}
return status;
}
@@ -2642,10 +2641,47 @@ public class LocationManager {
@Override
public void onRemoved() {
- unregister();
- synchronized (mListeners) {
- mListeners.remove(mListener, this);
+ // TODO: onRemoved is necessary to GC hanging listeners, but introduces some interesting
+ // broken edge cases. luckily these edge cases are quite unlikely. consider the
+ // following interleaving for instance:
+ // 1) client adds single shot location request (A)
+ // 2) client gets removal callback, and schedules it for execution
+ // 3) client replaces single shot request with a different location request (B)
+ // 4) prior removal callback is executed, removing location request (B) incorrectly
+ // what's needed is a way to identify which listener a callback belongs to. currently
+ // we reuse the same transport object for the same listeners (so that we don't leak
+ // transport objects on the server side). there seem to be two solutions:
+ // 1) when reregistering a request, first unregister the current transport, then
+ // register with a new transport object (never reuse transport objects) - the
+ // downside is that this breaks the server's knowledge that the request is the
+ // same object, and thus breaks optimizations such as reusing the same transport
+ // state.
+ // 2) pass some other type of marker in addition to the transport (for example an
+ // incrementing integer representing the transport "version"), and pass this
+ // marker back into callbacks so that each callback knows which transport
+ // "version" it belongs to and can not execute itself if the version does not
+ // match.
+ // (1) seems like the preferred solution as it's simpler to implement and the above
+ // mentioned server optimizations are not terribly important (they can be bypassed by
+ // clients that use a new listener every time anyways).
+
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ // we've already been unregistered, no work to do anyways
+ return;
}
+
+ // must be executed on the same executor so callback execution cannot be reordered
+ currentExecutor.execute(() -> {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ unregister();
+ synchronized (mListeners) {
+ mListeners.remove(mListener, this);
+ }
+ });
}
private void locationCallbackFinished() {
@@ -2783,8 +2819,8 @@ public class LocationManager {
@Override
public void onSvStatusChanged(int svCount, int[] svidWithFlags, float[] cn0s,
float[] elevations, float[] azimuths, float[] carrierFreqs) {
- GnssStatus localStatus = new GnssStatus(svCount, svidWithFlags, cn0s, elevations,
- azimuths, carrierFreqs);
+ GnssStatus localStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0s,
+ elevations, azimuths, carrierFreqs);
mGnssStatus = localStatus;
execute((callback) -> callback.onSatelliteStatusChanged(localStatus));
}
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 07bfe0242509..e0bff74fd588 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -188,22 +188,18 @@ public class GnssMetrics {
/**
* Logs sv status data
- *
- * @param svCount
- * @param svidWithFlags
- * @param svCarrierFreqs
*/
- public void logSvStatus(int svCount, int[] svidWithFlags, float[] svCarrierFreqs) {
+ public void logSvStatus(GnssStatus status) {
boolean isL5 = false;
// Calculate SvStatus Information
- for (int i = 0; i < svCount; i++) {
- if ((svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0) {
+ for (int i = 0; i < status.getSatelliteCount(); i++) {
+ if (status.hasCarrierFrequencyHz(i)) {
mNumSvStatus++;
- isL5 = isL5Sv(svCarrierFreqs[i]);
+ isL5 = isL5Sv(status.getCarrierFrequencyHz(i));
if (isL5) {
mNumL5SvStatus++;
}
- if ((svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+ if (status.usedInFix(i)) {
mNumSvStatusUsedInFix++;
if (isL5) {
mNumL5SvStatusUsedInFix++;
@@ -211,15 +207,10 @@ public class GnssMetrics {
}
}
}
- return;
}
/**
* Logs CN0 when at least 4 SVs are available L5 Only
- *
- * @param svCount
- * @param cn0s
- * @param svCarrierFreqs
*/
private void logCn0L5(int svCount, float[] cn0s, float[] svCarrierFreqs) {
if (svCount == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < svCount
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index da8e402ffb9f..5ac6f7f1b03f 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -506,6 +506,26 @@ public class ExifInterface {
public static final int WHITEBALANCE_AUTO = 0;
public static final int WHITEBALANCE_MANUAL = 1;
+ /**
+ * Constant used to indicate that the input stream contains the full image data.
+ * <p>
+ * The format of the image data should follow one of the image formats supported by this class.
+ */
+ public static final int STREAM_TYPE_FULL_IMAGE_DATA = 0;
+ /**
+ * Constant used to indicate that the input stream contains only Exif data.
+ * <p>
+ * The format of the Exif-only data must follow the below structure:
+ * Exif Identifier Code ("Exif\0\0") + TIFF header + IFD data
+ * See JEITA CP-3451C Section 4.5.2 and 4.5.4 specifications for more details.
+ */
+ public static final int STREAM_TYPE_EXIF_DATA_ONLY = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STREAM_TYPE_FULL_IMAGE_DATA, STREAM_TYPE_EXIF_DATA_ONLY})
+ public @interface ExifStreamType {}
+
// Maximum size for checking file type signature (see image_type_recognition_lite.cc)
private static final int SIGNATURE_CHECK_SIZE = 5000;
@@ -1416,7 +1436,7 @@ public class ExifInterface {
private AssetManager.AssetInputStream mAssetInputStream;
private boolean mIsInputStream;
private int mMimeType;
- private boolean mIsStandalone;
+ private boolean mIsExifDataOnly;
@UnsupportedAppUsage
private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
private Set<Integer> mHandledIfdOffsets = new HashSet<>(EXIF_TAGS.length);
@@ -1444,6 +1464,11 @@ public class ExifInterface {
/**
* Reads Exif tags from the specified image file.
+ *
+ * @param file the file of the image data
+ * @throws NullPointerException if file is null
+ * @throws IOException if an I/O error occurs while retrieving file descriptor via
+ * {@link FileInputStream#getFD()}.
*/
public ExifInterface(@NonNull File file) throws IOException {
if (file == null) {
@@ -1454,6 +1479,11 @@ public class ExifInterface {
/**
* Reads Exif tags from the specified image file.
+ *
+ * @param filename the name of the file of the image data
+ * @throws NullPointerException if file name is null
+ * @throws IOException if an I/O error occurs while retrieving file descriptor via
+ * {@link FileInputStream#getFD()}.
*/
public ExifInterface(@NonNull String filename) throws IOException {
if (filename == null) {
@@ -1466,6 +1496,11 @@ public class ExifInterface {
* Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
* for writable and seekable file descriptors only. This constructor will not rewind the offset
* of the given file descriptor. Developers should close the file descriptor after use.
+ *
+ * @param fileDescriptor the file descriptor of the image data
+ * @throws NullPointerException if file descriptor is null
+ * @throws IOException if an error occurs while duplicating the file descriptor via
+ * {@link Os#dup(FileDescriptor)}.
*/
public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
if (fileDescriptor == null) {
@@ -1504,45 +1539,46 @@ public class ExifInterface {
/**
* Reads Exif tags from the specified image input stream. Attribute mutation is not supported
* for input streams. The given input stream will proceed from its current position. Developers
- * should close the input stream after use.
+ * should close the input stream after use. This constructor is not intended to be used with an
+ * input stream that performs any networking operations.
+ *
+ * @param inputStream the input stream that contains the image data
+ * @throws NullPointerException if the input stream is null
*/
public ExifInterface(@NonNull InputStream inputStream) throws IOException {
this(inputStream, false);
}
/**
- * Reads Exif tags from the specified standalone input stream. Standalone data refers to Exif
- * data that exists by itself and is not contained in a file format such as jpeg or png.
- * The format of the standalone data must follow the below structure:
- * Exif Identifier Code ("Exif\0\0") + TIFF header + IFD data
- * See JEITA CP-3451C Section 4.5.2 and 4.5.4 specifications for more details.
- * <p>
- * Attribute mutation is not supported for this constructor. The given input stream will proceed
- * from its current position. Developers should close the input stream after use. This
- * constructor is not intended to be used with an input stream that performs any networking
- * operations.
+ * Reads Exif tags from the specified image input stream based on the stream type. Attribute
+ * mutation is not supported for input streams. The given input stream will proceed from its
+ * current position. Developers should close the input stream after use. This constructor is not
+ * intended to be used with an input stream that performs any networking operations.
*
- * @throws IOException if the data does not follow the aforementioned structure.
+ * @param inputStream the input stream that contains the image data
+ * @param streamType the type of input stream
+ * @throws NullPointerException if the input stream is null
+ * @throws IOException if an I/O error occurs while retrieving file descriptor via
+ * {@link FileInputStream#getFD()}.
*/
- @NonNull
- public static ExifInterface fromStandalone(@NonNull InputStream inputStream)
+ public ExifInterface(@NonNull InputStream inputStream, @ExifStreamType int streamType)
throws IOException {
- if (isStandalone(inputStream)) {
- return new ExifInterface(inputStream, true);
- }
- throw new IOException("Given data does not follow the structure of a standalone exif "
- + "data.");
+ this(inputStream, (streamType == STREAM_TYPE_EXIF_DATA_ONLY) ? true : false);
}
- private ExifInterface(@NonNull InputStream inputStream, boolean isFromStandalone)
+ private ExifInterface(@NonNull InputStream inputStream, boolean shouldBeExifDataOnly)
throws IOException {
if (inputStream == null) {
throw new NullPointerException("inputStream cannot be null");
}
mFilename = null;
- if (isFromStandalone) {
- mIsStandalone = true;
+ if (shouldBeExifDataOnly) {
+ if (!isExifDataOnly(inputStream)) {
+ Log.w(TAG, "Given data does not follow the structure of an Exif-only data.");
+ return;
+ }
+ mIsExifDataOnly = true;
mAssetInputStream = null;
mSeekableFileDescriptor = null;
} else {
@@ -1880,10 +1916,9 @@ public class ExifInterface {
/**
* This function decides which parser to read the image data according to the given input stream
- * type and the content of the input stream. In each case, it reads the first three bytes to
- * determine whether the image data format is JPEG or not.
+ * type and the content of the input stream.
*/
- private void loadAttributes(@NonNull InputStream in) throws IOException {
+ private void loadAttributes(@NonNull InputStream in) {
if (in == null) {
throw new NullPointerException("inputstream shouldn't be null");
}
@@ -1894,7 +1929,7 @@ public class ExifInterface {
}
// Check file type
- if (!mIsStandalone) {
+ if (!mIsExifDataOnly) {
in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
mMimeType = getMimeType((BufferedInputStream) in);
}
@@ -1902,7 +1937,7 @@ public class ExifInterface {
// Create byte-ordered input stream
ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in);
- if (!mIsStandalone) {
+ if (!mIsExifDataOnly) {
switch (mMimeType) {
case IMAGE_TYPE_JPEG: {
getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
@@ -1969,11 +2004,14 @@ public class ExifInterface {
}
}
- private static boolean isSeekableFD(FileDescriptor fd) throws IOException {
+ private static boolean isSeekableFD(FileDescriptor fd) {
try {
Os.lseek(fd, 0, OsConstants.SEEK_CUR);
return true;
} catch (ErrnoException e) {
+ if (DEBUG) {
+ Log.d(TAG, "The file descriptor for the given input is not seekable");
+ }
return false;
}
}
@@ -2239,7 +2277,7 @@ public class ExifInterface {
}
if (mHasThumbnail) {
- if (mIsStandalone) {
+ if (mIsExifDataOnly) {
return new long[] { mThumbnailOffset + mExifOffset, mThumbnailLength };
}
return new long[] { mThumbnailOffset, mThumbnailLength };
@@ -2703,16 +2741,20 @@ public class ExifInterface {
return true;
}
- private static boolean isStandalone(InputStream inputStream) throws IOException {
- byte[] signatureCheckBytes = new byte[IDENTIFIER_EXIF_APP1.length];
- inputStream.read(signatureCheckBytes);
-
- for (int i = 0; i < IDENTIFIER_EXIF_APP1.length; i++) {
- if (signatureCheckBytes[i] != IDENTIFIER_EXIF_APP1[i]) {
- return false;
+ private static boolean isExifDataOnly(InputStream inputStream) {
+ try {
+ byte[] signatureCheckBytes = new byte[IDENTIFIER_EXIF_APP1.length];
+ inputStream.read(signatureCheckBytes);
+ if (Arrays.equals(signatureCheckBytes, IDENTIFIER_EXIF_APP1)) {
+ return true;
+ }
+ } catch (IOException e) {
+ if (DEBUG) {
+ Log.w(TAG,
+ "Encountered error while checking whether input stream is Exif data only");
}
}
- return true;
+ return false;
}
/**
diff --git a/media/java/android/media/tv/OWNER b/media/java/android/media/tv/OWNER
deleted file mode 100644
index 64c0bb53e894..000000000000
--- a/media/java/android/media/tv/OWNER
+++ /dev/null
@@ -1,5 +0,0 @@
-amyjojo@google.com
-nchalko@google.com
-shubang@google.com
-quxiangfang@google.com
-
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 4a03b3c6852e..64c0bb53e894 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -1,2 +1,5 @@
+amyjojo@google.com
nchalko@google.com
+shubang@google.com
+quxiangfang@google.com
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 8805b7be9afd..c91325aa9a09 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner;
import android.annotation.Nullable;
+import android.media.tv.tuner.TunerConstants.DemuxPidType;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -83,6 +84,10 @@ public final class Tuner implements AutoCloseable {
private native List<Integer> nativeGetLnbIds();
private native Lnb nativeOpenLnbById(int id);
+ private native Descrambler nativeOpenDescrambler();
+
+ private native Dvr nativeOpenDvr(int type, int bufferSize);
+
/**
* Frontend Callback.
*/
@@ -114,6 +119,20 @@ public final class Tuner implements AutoCloseable {
void onFilterStatus(int status);
}
+ /**
+ * DVR Callback.
+ */
+ public interface DvrCallback {
+ /**
+ * Invoked when record status changed.
+ */
+ void onRecordStatus(int status);
+ /**
+ * Invoked when playback status changed.
+ */
+ void onPlaybackStatus(int status);
+ }
+
@Nullable
private EventHandler createEventHandler() {
Looper looper;
@@ -294,4 +313,62 @@ public final class Tuner implements AutoCloseable {
mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LNB_EVENT, eventType, 0));
}
}
+
+ protected class Descrambler {
+ private long mNativeContext;
+
+ private native boolean nativeAddPid(int pidType, int pid, Filter filter);
+ private native boolean nativeRemovePid(int pidType, int pid, Filter filter);
+
+ private Descrambler() {}
+
+ private boolean addPid(@DemuxPidType int pidType, int pid, Filter filter) {
+ return nativeAddPid(pidType, pid, filter);
+ }
+
+ private boolean removePid(@DemuxPidType int pidType, int pid, Filter filter) {
+ return nativeRemovePid(pidType, pid, filter);
+ }
+
+ }
+
+ private Descrambler openDescrambler() {
+ Descrambler descrambler = nativeOpenDescrambler();
+ return descrambler;
+ }
+
+ // TODO: consider splitting Dvr to Playback and Recording
+ protected class Dvr {
+ private long mNativeContext;
+ private DvrCallback mCallback;
+
+ private native boolean nativeAttachFilter(Filter filter);
+ private native boolean nativeDetachFilter(Filter filter);
+ private native boolean nativeStartDvr();
+ private native boolean nativeStopDvr();
+ private native boolean nativeFlushDvr();
+
+ private Dvr() {}
+
+ public boolean attachFilter(Filter filter) {
+ return nativeAttachFilter(filter);
+ }
+ public boolean detachFilter(Filter filter) {
+ return nativeDetachFilter(filter);
+ }
+ public boolean start() {
+ return nativeStartDvr();
+ }
+ public boolean stop() {
+ return nativeStopDvr();
+ }
+ public boolean flush() {
+ return nativeFlushDvr();
+ }
+ }
+
+ private Dvr openDvr(int type, int bufferSize) {
+ Dvr dvr = nativeOpenDvr(type, bufferSize);
+ return dvr;
+ }
}
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 411882ed13bd..01f9367dd4f3 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -67,6 +67,14 @@ final class TunerConstants {
public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DEMUX_T_PID, DEMUX_MMPT_PID})
+ public @interface DemuxPidType {}
+
+ public static final int DEMUX_T_PID = 1;
+ public static final int DEMUX_MMPT_PID = 2;
+
private TunerConstants() {
}
}
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index 3cfa65d1117f..b1f544cb2dbe 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -47,7 +47,7 @@ public:
const ListenerArgs *args) = 0;
};
-struct JDrm : public BnDrmClient {
+struct JDrm : public IDrmClient {
static status_t IsCryptoSchemeSupported(const uint8_t uuid[16],
const String8 &mimeType,
DrmPlugin::SecurityLevel level,
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 35607ac6d5e9..f5202fc17f71 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -28,19 +28,25 @@
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
+using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
using ::android::hardware::tv::tuner::V1_0::ITuner;
using ::android::hardware::tv::tuner::V1_0::Result;
struct fields_t {
- jfieldID context;
+ jfieldID tunerContext;
jfieldID filterContext;
+ jfieldID descramblerContext;
+ jfieldID dvrContext;
jmethodID frontendInitID;
jmethodID filterInitID;
+ jmethodID dvrInitID;
jmethodID onFrontendEventID;
jmethodID onFilterStatusID;
jmethodID lnbInitID;
jmethodID onLnbEventID;
+ jmethodID descramblerInitID;
};
static fields_t gFields;
@@ -63,6 +69,23 @@ Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& /*diseqcMessa
return Void();
}
+/////////////// DvrCallback ///////////////////////
+Return<void> DvrCallback::onRecordStatus(RecordStatus /*status*/) {
+ ALOGD("DvrCallback::onRecordStatus");
+ return Void();
+}
+
+Return<void> DvrCallback::onPlaybackStatus(PlaybackStatus /*status*/) {
+ ALOGD("DvrCallback::onPlaybackStatus");
+ return Void();
+}
+
+void DvrCallback::setDvr(const jobject dvr) {
+ ALOGD("FilterCallback::setDvr");
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ mDvr = env->NewWeakGlobalRef(dvr);
+}
+
/////////////// FilterCallback ///////////////////////
//TODO: implement filter callback
Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
@@ -258,6 +281,33 @@ bool JTuner::openDemux() {
return true;
}
+jobject JTuner::openDescrambler() {
+ ALOGD("JTuner::openDescrambler");
+ if (mTuner == nullptr) {
+ return NULL;
+ }
+ sp<IDescrambler> descramblerSp;
+ mTuner->openDescrambler([&](Result, const sp<IDescrambler>& descrambler) {
+ descramblerSp = descrambler;
+ });
+
+ if (descramblerSp == NULL) {
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jobject descramblerObj =
+ env->NewObject(
+ env->FindClass("android/media/tv/tuner/Tuner$Descrambler"),
+ gFields.descramblerInitID,
+ mObject);
+
+ descramblerSp->incStrong(descramblerObj);
+ env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerSp.get());
+
+ return descramblerObj;
+}
+
jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
if (mDemux == NULL) {
if (!openDemux()) {
@@ -296,6 +346,39 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
return filterObj;
}
+jobject JTuner::openDvr(DvrType type, int bufferSize) {
+ ALOGD("JTuner::openDvr");
+ if (mDemux == NULL) {
+ if (!openDemux()) {
+ return NULL;
+ }
+ }
+ sp<IDvr> dvrSp;
+ sp<DvrCallback> callback = new DvrCallback();
+ mDemux->openDvr(type, bufferSize, callback,
+ [&](Result, const sp<IDvr>& dvr) {
+ dvrSp = dvr;
+ });
+
+ if (dvrSp == NULL) {
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jobject dvrObj =
+ env->NewObject(
+ env->FindClass("android/media/tv/tuner/Tuner$Dvr"),
+ gFields.dvrInitID,
+ mObject);
+
+ dvrSp->incStrong(dvrObj);
+ env->SetLongField(dvrObj, gFields.dvrContext, (jlong)dvrSp.get());
+
+ callback->setDvr(dvrObj);
+
+ return dvrObj;
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -303,7 +386,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
using namespace android;
static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
- sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.context);
+ sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
if (tuner != NULL) {
tuner->incStrong(thiz);
@@ -311,25 +394,43 @@ static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
if (old != NULL) {
old->decStrong(thiz);
}
- env->SetLongField(thiz, gFields.context, (jlong)tuner.get());
+ env->SetLongField(thiz, gFields.tunerContext, (jlong)tuner.get());
return old;
}
static sp<JTuner> getTuner(JNIEnv *env, jobject thiz) {
- return (JTuner *)env->GetLongField(thiz, gFields.context);
+ return (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
+}
+
+static sp<IDescrambler> getDescrambler(JNIEnv *env, jobject descrambler) {
+ return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext);
+}
+
+static DemuxPid getDemuxPid(int pidType, int pid) {
+ DemuxPid demuxPid;
+ if ((int)pidType == 1) {
+ demuxPid.tPid(static_cast<DemuxTpid>(pid));
+ } else if ((int)pidType == 2) {
+ demuxPid.mmtpPid(static_cast<DemuxMmtpPid>(pid));
+ }
+ return demuxPid;
}
static sp<IFilter> getFilter(JNIEnv *env, jobject filter) {
return (IFilter *)env->GetLongField(filter, gFields.filterContext);
}
+static sp<IDvr> getDvr(JNIEnv *env, jobject dvr) {
+ return (IDvr *)env->GetLongField(dvr, gFields.dvrContext);
+}
+
static void android_media_tv_Tuner_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
CHECK(clazz != NULL);
- gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
- CHECK(gFields.context != NULL);
+ gFields.tunerContext = env->GetFieldID(clazz, "mNativeContext", "J");
+ CHECK(gFields.tunerContext != NULL);
gFields.onFrontendEventID = env->GetMethodID(clazz, "onFrontendEvent", "(I)V");
@@ -349,6 +450,15 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) {
env->GetMethodID(filterClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
gFields.onFilterStatusID =
env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
+
+ jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Tuner$Descrambler");
+ gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J");
+ gFields.descramblerInitID =
+ env->GetMethodID(descramblerClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
+
+ jclass dvrClazz = env->FindClass("android/media/tv/tuner/Tuner$Dvr");
+ gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J");
+ gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
}
static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
@@ -416,6 +526,85 @@ static bool android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
return filterSp->flush() == Result::SUCCESS;
}
+static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openDescrambler();
+}
+
+static bool android_media_tv_Tuner_add_pid(
+ JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
+ sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
+ if (descramblerSp == NULL) {
+ return false;
+ }
+ sp<IFilter> filterSp = getFilter(env, filter);
+ Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), filterSp);
+ return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_remove_pid(
+ JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
+ sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
+ if (descramblerSp == NULL) {
+ return false;
+ }
+ sp<IFilter> filterSp = getFilter(env, filter);
+ Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), filterSp);
+ return result == Result::SUCCESS;
+}
+
+static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint type, jint bufferSize) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openDvr(static_cast<DvrType>(type), bufferSize);
+}
+
+static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
+ sp<IDvr> dvrSp = getDvr(env, dvr);
+ sp<IFilter> filterSp = getFilter(env, filter);
+ if (dvrSp == NULL || filterSp == NULL) {
+ return false;
+ }
+ Result result = dvrSp->attachFilter(filterSp);
+ return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) {
+ sp<IDvr> dvrSp = getDvr(env, dvr);
+ sp<IFilter> filterSp = getFilter(env, filter);
+ if (dvrSp == NULL || filterSp == NULL) {
+ return false;
+ }
+ Result result = dvrSp->detachFilter(filterSp);
+ return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
+ sp<IDvr> dvrSp = getDvr(env, dvr);
+ if (dvrSp == NULL) {
+ ALOGD("Failed to start dvr: dvr not found");
+ return false;
+ }
+ return dvrSp->start() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) {
+ sp<IDvr> dvrSp = getDvr(env, dvr);
+ if (dvrSp == NULL) {
+ ALOGD("Failed to stop dvr: dvr not found");
+ return false;
+ }
+ return dvrSp->stop() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) {
+ sp<IDvr> dvrSp = getDvr(env, dvr);
+ if (dvrSp == NULL) {
+ ALOGD("Failed to flush dvr: dvr not found");
+ return false;
+ }
+ return dvrSp->flush() == Result::SUCCESS;
+}
+
static const JNINativeMethod gTunerMethods[] = {
{ "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
@@ -429,6 +618,10 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_get_lnb_ids },
{ "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Tuner$Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_id },
+ { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Tuner$Descrambler;",
+ (void *)android_media_tv_Tuner_open_descrambler },
+ { "nativeOpenDvr", "(II)Landroid/media/tv/tuner/Tuner$Dvr;",
+ (void *)android_media_tv_Tuner_open_dvr },
};
static const JNINativeMethod gFilterMethods[] = {
@@ -437,6 +630,23 @@ static const JNINativeMethod gFilterMethods[] = {
{ "nativeFlushFilter", "()Z", (void *)android_media_tv_Tuner_flush_filter },
};
+static const JNINativeMethod gDescramblerMethods[] = {
+ { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+ (void *)android_media_tv_Tuner_add_pid },
+ { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+ (void *)android_media_tv_Tuner_remove_pid },
+};
+
+static const JNINativeMethod gDvrMethods[] = {
+ { "nativeAttachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
+ (void *)android_media_tv_Tuner_attach_filter },
+ { "nativeDetachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
+ (void *)android_media_tv_Tuner_detach_filter },
+ { "nativeStartDvr", "()Z", (void *)android_media_tv_Tuner_start_dvr },
+ { "nativeStopDvr", "()Z", (void *)android_media_tv_Tuner_stop_dvr },
+ { "nativeFlushDvr", "()Z", (void *)android_media_tv_Tuner_flush_dvr },
+};
+
static bool register_android_media_tv_Tuner(JNIEnv *env) {
if (AndroidRuntime::registerNativeMethods(
env, "android/media/tv/tuner/Tuner", gTunerMethods, NELEM(gTunerMethods)) != JNI_OK) {
@@ -450,6 +660,20 @@ static bool register_android_media_tv_Tuner(JNIEnv *env) {
ALOGE("Failed to register filter native methods");
return false;
}
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Tuner$Descrambler",
+ gDescramblerMethods,
+ NELEM(gDescramblerMethods)) != JNI_OK) {
+ ALOGE("Failed to register descrambler native methods");
+ return false;
+ }
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Tuner$Dvr",
+ gDvrMethods,
+ NELEM(gDvrMethods)) != JNI_OK) {
+ ALOGE("Failed to register dvr native methods");
+ return false;
+ }
return true;
}
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index f42f032cacc2..3bf6ec84c8f7 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -28,11 +28,16 @@ using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxPid;
+using ::android::hardware::tv::tuner::V1_0::DvrType;
using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
using ::android::hardware::tv::tuner::V1_0::FrontendId;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IDescrambler;
+using ::android::hardware::tv::tuner::V1_0::IDvr;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
using ::android::hardware::tv::tuner::V1_0::IFilter;
using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
using ::android::hardware::tv::tuner::V1_0::IFrontend;
@@ -42,6 +47,8 @@ using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
using ::android::hardware::tv::tuner::V1_0::ITuner;
using ::android::hardware::tv::tuner::V1_0::LnbEventType;
using ::android::hardware::tv::tuner::V1_0::LnbId;
+using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using ::android::hardware::tv::tuner::V1_0::RecordStatus;
namespace android {
@@ -53,6 +60,15 @@ struct LnbCallback : public ILnbCallback {
LnbId mId;
};
+struct DvrCallback : public IDvrCallback {
+ virtual Return<void> onRecordStatus(RecordStatus status);
+ virtual Return<void> onPlaybackStatus(PlaybackStatus status);
+
+ void setDvr(const jobject dvr);
+private:
+ jweak mDvr;
+};
+
struct FilterCallback : public IFilterCallback {
virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
@@ -82,6 +98,8 @@ struct JTuner : public RefBase {
jobject getLnbIds();
jobject openLnbById(int id);
jobject openFilter(DemuxFilterType type, int bufferSize);
+ jobject openDescrambler();
+ jobject openDvr(DvrType type, int bufferSize);
protected:
bool openDemux();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 87a59df19e8e..f979fddd7bda 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -284,11 +284,12 @@ public class CameraBinderTest extends AndroidTestCase {
ICameraDeviceCallbacks dummyCallbacks = new DummyCameraDeviceCallbacks();
String clientPackageName = getContext().getPackageName();
+ String clientFeatureId = getContext().getFeatureId();
ICameraDeviceUser cameraUser =
mUtils.getCameraService().connectDevice(
dummyCallbacks, String.valueOf(cameraId),
- clientPackageName,
+ clientPackageName, clientFeatureId,
ICameraService.USE_CALLING_UID);
assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 30561ba0f760..466c5f4ba980 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -238,11 +238,12 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
ICameraDeviceCallbacks.Stub dummyCallbacks = new DummyCameraDeviceCallbacks();
String clientPackageName = getContext().getPackageName();
+ String clientFeatureId = getContext().getFeatureId();
mMockCb = spy(dummyCallbacks);
mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId,
- clientPackageName, ICameraService.USE_CALLING_UID);
+ clientPackageName, clientFeatureId, ICameraService.USE_CALLING_UID);
assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 91297b0de02e..9d93c9b7b605 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -36,7 +36,6 @@ cc_library_shared {
srcs: [
"asset_manager.cpp",
- "choreographer.cpp",
"configuration.cpp",
"hardware_buffer_jni.cpp",
"input.cpp",
@@ -80,7 +79,7 @@ cc_library_shared {
"libarect",
],
- whole_static_libs: ["libnativewindow"],
+ whole_static_libs: ["libnativedisplay", "libnativewindow"],
export_static_lib_headers: ["libarect"],
@@ -142,4 +141,4 @@ filegroup {
"aidl/com/android/internal/compat/IPlatformCompatNative.aidl",
],
path: "aidl",
-} \ No newline at end of file
+}
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
deleted file mode 100644
index 63e073405fe0..000000000000
--- a/native/android/choreographer.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Choreographer"
-//#define LOG_NDEBUG 0
-
-#include <cinttypes>
-#include <queue>
-#include <thread>
-
-#include <android/choreographer.h>
-#include <androidfw/DisplayEventDispatcher.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <utils/Looper.h>
-#include <utils/Mutex.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-struct FrameCallback {
- AChoreographer_frameCallback callback;
- AChoreographer_frameCallback64 callback64;
- void* data;
- nsecs_t dueTime;
-
- inline bool operator<(const FrameCallback& rhs) const {
- // Note that this is intentionally flipped because we want callbacks due sooner to be at
- // the head of the queue
- return dueTime > rhs.dueTime;
- }
-};
-
-
-class Choreographer : public DisplayEventDispatcher, public MessageHandler {
-public:
- void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
-
- enum {
- MSG_SCHEDULE_CALLBACKS = 0,
- MSG_SCHEDULE_VSYNC = 1
- };
- virtual void handleMessage(const Message& message) override;
-
- static Choreographer* getForThread();
-
-protected:
- virtual ~Choreographer() = default;
-
-private:
- explicit Choreographer(const sp<Looper>& looper);
- Choreographer(const Choreographer&) = delete;
-
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
- void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
- void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId) override;
-
- void scheduleCallbacks();
-
- // Protected by mLock
- std::priority_queue<FrameCallback> mCallbacks;
-
- mutable Mutex mLock;
-
- const sp<Looper> mLooper;
- const std::thread::id mThreadId;
-};
-
-
-static thread_local Choreographer* gChoreographer;
-Choreographer* Choreographer::getForThread() {
- if (gChoreographer == nullptr) {
- sp<Looper> looper = Looper::getForThread();
- if (!looper.get()) {
- ALOGW("No looper prepared for thread");
- return nullptr;
- }
- gChoreographer = new Choreographer(looper);
- status_t result = gChoreographer->initialize();
- if (result != OK) {
- ALOGW("Failed to initialize");
- return nullptr;
- }
- }
- return gChoreographer;
-}
-
-Choreographer::Choreographer(const sp<Looper>& looper) :
- DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) {
-}
-
-void Choreographer::postFrameCallbackDelayed(
- AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, data, now + delay};
- {
- AutoMutex _l{mLock};
- mCallbacks.push(callback);
- }
- if (callback.dueTime <= now) {
- if (std::this_thread::get_id() != mThreadId) {
- Message m{MSG_SCHEDULE_VSYNC};
- mLooper->sendMessage(this, m);
- } else {
- scheduleVsync();
- }
- } else {
- Message m{MSG_SCHEDULE_CALLBACKS};
- mLooper->sendMessageDelayed(delay, this, m);
- }
-}
-
-void Choreographer::scheduleCallbacks() {
- AutoMutex _{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (mCallbacks.top().dueTime <= now) {
- ALOGV("choreographer %p ~ scheduling vsync", this);
- scheduleVsync();
- return;
- }
-}
-
-// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
-// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
-// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
- std::vector<FrameCallback> callbacks{};
- {
- AutoMutex _l{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) {
- callbacks.push_back(mCallbacks.top());
- mCallbacks.pop();
- }
- }
- for (const auto& cb : callbacks) {
- if (cb.callback64 != nullptr) {
- cb.callback64(timestamp, cb.data);
- } else if (cb.callback != nullptr) {
- cb.callback(timestamp, cb.data);
- }
- }
-}
-
-void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%"
- ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
- this, displayId, toString(connected));
-}
-
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
- int32_t configId) {
- ALOGV("choreographer %p ~ received config changed event (displayId=%"
- ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
- this, displayId, toString(configId));
-}
-
-void Choreographer::handleMessage(const Message& message) {
- switch (message.what) {
- case MSG_SCHEDULE_CALLBACKS:
- scheduleCallbacks();
- break;
- case MSG_SCHEDULE_VSYNC:
- scheduleVsync();
- break;
- }
-}
-
-}
-
-/* Glue for the NDK interface */
-
-using android::Choreographer;
-
-static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
- return reinterpret_cast<Choreographer*>(choreographer);
-}
-
-static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
- return reinterpret_cast<AChoreographer*>(choreographer);
-}
-
-AChoreographer* AChoreographer_getInstance() {
- return Choreographer_to_AChoreographer(Choreographer::getForThread());
-}
-
-void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, 0);
-}
-void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data, long delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, ms2ns(delayMillis));
-}
-void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, 0);
-}
-void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, ms2ns(delayMillis));
-}
diff --git a/packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml b/packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml
deleted file mode 100644
index 918abd962dc2..000000000000
--- a/packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.systemui.statusbar.car.privacy.OngoingPrivacyChip
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/car_privacy_chip"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_margin="@dimen/ongoing_appops_chip_margin"
- android:layout_toStartOf="@+id/clock_container"
- android:focusable="true"
- android:gravity="center_vertical|end"
- android:orientation="horizontal"
- android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
- android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
- android:visibility="visible">
-
- <LinearLayout
- android:id="@+id/icons_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical|start"/>
-</com.android.systemui.statusbar.car.privacy.OngoingPrivacyChip> \ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 7299ddfd8688..ce0d31c6cc47 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -65,8 +65,6 @@
/>
</FrameLayout>
- <include layout="@layout/car_ongoing_privacy_chip"/>
-
<FrameLayout
android:id="@+id/clock_container"
android:layout_width="wrap_content"
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index e2da91b6d746..ee79653d92ac 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -59,18 +59,6 @@
<dimen name="car_keyline_1">24dp</dimen>
<dimen name="car_keyline_2">96dp</dimen>
<dimen name="car_keyline_3">128dp</dimen>
- <dimen name="privacy_chip_icon_height">36dp</dimen>
- <dimen name="privacy_chip_icon_padding_left">0dp</dimen>
- <dimen name="privacy_chip_icon_padding_right">0dp</dimen>
- <dimen name="privacy_chip_icon_padding_top">0dp</dimen>
- <dimen name="privacy_chip_icon_padding_bottom">0dp</dimen>
-
- <dimen name="privacy_chip_text_padding_left">0dp</dimen>
- <dimen name="privacy_chip_text_padding_right">0dp</dimen>
- <dimen name="privacy_chip_text_padding_top">0dp</dimen>
- <dimen name="privacy_chip_text_padding_bottom">0dp</dimen>
-
- <dimen name="privacy_chip_icon_max_height">100dp</dimen>
<!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
<dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
@@ -86,16 +74,6 @@
<dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
- <!-- Start padding for the app icon displayed in the dialog -->
- <dimen name="privacy_dialog_app_icon_padding_start">40dp</dimen>
- <!-- End padding for the app opps icon displayed in the dialog -->
- <dimen name="privacy_dialog_app_ops_icon_padding_end">40dp</dimen>
- <!-- Top padding for the list of application displayed in the dialog -->
- <dimen name="privacy_dialog_app_list_padding_top">20dp</dimen>
- <!-- Top padding for the dialog container-->
- <dimen name="privacy_dialog_container_padding_top">10dp</dimen>
- <!-- Top padding for the dialog title-->
- <dimen name="privacy_dialog_title_padding_start">10dp</dimen>
<!-- Car volume dimens. -->
<dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 818fdeaef8db..bffc6b912146 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -26,6 +26,7 @@ import com.android.systemui.car.CarNotificationInterruptionStateProvider;
import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.recents.Recents;
@@ -38,10 +39,13 @@ import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.CarVolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogComponent;
@@ -84,6 +88,17 @@ abstract class CarSystemUIModule {
@Singleton
@Provides
+ static HeadsUpManagerPhone provideHeadsUpManagerPhone(Context context,
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController bypassController) {
+ return new HeadsUpManagerPhone(context, statusBarStateController, bypassController);
+ }
+
+ @Binds
+ abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
+
+ @Singleton
+ @Provides
@Named(LEAK_REPORT_EMAIL_NAME)
static String provideLeakReportEmail() {
return "buganizer-system+181579@google.com";
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 10527b231169..4e5a3a633c3e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -96,12 +96,12 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NewNotifPipeline;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 5418ebe5b8de..4813d6dfeb7e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -56,12 +56,12 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NewNotifPipeline;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
deleted file mode 100644
index 88d641e8bb36..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car.privacy;
-
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.appops.AppOpItem;
-import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.plugins.ActivityStarter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-/**
- * Layout defining the privacy chip that will be displayed in CarStatusRar with the information for
- * which applications are using AppOpps permission fpr camera, mic and location.
- */
-public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener {
-
- private Context mContext;
- private Handler mHandler;
-
- private LinearLayout mIconsContainer;
- private List<PrivacyItem> mPrivacyItems;
- private static AppOpsController sAppOpsController;
- private UserManager mUserManager;
- private int mCurrentUser;
- private List<Integer> mCurrentUserIds;
- private boolean mListening = false;
- PrivacyDialogBuilder mPrivacyDialogBuilder;
- private LinearLayout mPrivacyChip;
- private ActivityStarter mActivityStarter;
-
- protected static final int[] OPS = new int[]{
- AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION
- };
-
- public OngoingPrivacyChip(Context context) {
- super(context, null);
- init(context);
- }
-
- public OngoingPrivacyChip(Context context, AttributeSet attr) {
- super(context, attr);
- init(context);
- }
-
- public OngoingPrivacyChip(Context context, AttributeSet attr, int defStyle) {
- super(context, attr, defStyle);
- init(context);
- }
-
- public OngoingPrivacyChip(Context context, AttributeSet attr, int defStyle, int a) {
- super(context, attr, defStyle, a);
- init(context);
- }
-
- private void init(Context context) {
- mContext = context;
- mHandler = new Handler(Looper.getMainLooper());
- mPrivacyItems = new ArrayList<>();
- sAppOpsController = Dependency.get(AppOpsController.class);
- mUserManager = mContext.getSystemService(UserManager.class);
- mActivityStarter = Dependency.get(ActivityStarter.class);
- mCurrentUser = ActivityManager.getCurrentUser();
- mCurrentUserIds = mUserManager.getProfiles(mCurrentUser).stream().map(
- userInfo -> userInfo.id).collect(Collectors.toList());
-
- mPrivacyDialogBuilder = new PrivacyDialogBuilder(context, mPrivacyItems);
- }
-
- private AppOpsController.Callback mCallback = new AppOpsController.Callback() {
-
- @Override
- public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
- int userId = UserHandle.getUserId(uid);
- if (mCurrentUserIds.contains(userId)) {
- updatePrivacyList();
- }
- }
- };
-
- @Override
- public void onFinishInflate() {
- mIconsContainer = findViewById(R.id.icons_container);
- mPrivacyChip = (LinearLayout) findViewById(R.id.car_privacy_chip);
- if (mPrivacyChip != null) {
- mPrivacyChip.setOnClickListener(this);
- setListening(true);
- }
- }
-
- @Override
- public void onDetachedFromWindow() {
- if (mPrivacyChip != null) {
- setListening(false);
- }
- super.onDetachedFromWindow();
- }
-
- @Override
- public void onClick(View v) {
- updatePrivacyList();
- mHandler.post(() -> {
- mActivityStarter.postStartActivityDismissingKeyguard(
- new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
- });
- }
-
- private void setListening(boolean listen) {
- if (mListening == listen) {
- return;
- }
- mListening = listen;
- if (mListening) {
- sAppOpsController.addCallback(OPS, mCallback);
- updatePrivacyList();
- } else {
- sAppOpsController.removeCallback(OPS, mCallback);
- }
- }
-
- private void updatePrivacyList() {
- List<PrivacyItem> privacyItems = mCurrentUserIds.stream()
- .flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream())
- .filter(Objects::nonNull)
- .map(item -> toPrivacyItem(item))
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- if (!privacyItems.equals(mPrivacyItems)) {
- mPrivacyItems = privacyItems;
- mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
- mHandler.post(this::updateView);
- }
- }
-
- private PrivacyItem toPrivacyItem(AppOpItem appOpItem) {
- PrivacyType type;
- switch (appOpItem.getCode()) {
- case AppOpsManager.OP_CAMERA:
- type = PrivacyType.TYPE_CAMERA;
- break;
- case AppOpsManager.OP_COARSE_LOCATION:
- type = PrivacyType.TYPE_LOCATION;
- break;
- case AppOpsManager.OP_FINE_LOCATION:
- type = PrivacyType.TYPE_LOCATION;
- break;
- case AppOpsManager.OP_RECORD_AUDIO:
- type = PrivacyType.TYPE_MICROPHONE;
- break;
- default:
- return null;
- }
- PrivacyApplication app = new PrivacyApplication(appOpItem.getPackageName(), mContext);
- return new PrivacyItem(type, app, appOpItem.getTimeStarted());
- }
-
- // Should only be called if the mPrivacyDialogBuilder icons or app changed
- private void updateView() {
- if (mPrivacyItems.isEmpty()) {
- mPrivacyChip.setVisibility(GONE);
- return;
- }
- mPrivacyChip.setVisibility(VISIBLE);
- setIcons(mPrivacyDialogBuilder);
-
- requestLayout();
- }
-
- private void setIcons(PrivacyDialogBuilder dialogBuilder) {
- mIconsContainer.removeAllViews();
- dialogBuilder.generateIcons().forEach(item -> {
- int size = mContext.getResources().getDimensionPixelSize(
- R.dimen.privacy_chip_icon_height);
- ImageView image = new ImageView(mContext);
- image.setImageDrawable(item);
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(size, size);
-
- int leftPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.privacy_chip_icon_padding_left);
- int topPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.privacy_chip_icon_padding_top);
- int rightPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.privacy_chip_icon_padding_right);
- int bottomPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.privacy_chip_icon_padding_bottom);
- image.setLayoutParams(layoutParams);
- image.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
- mIconsContainer.addView(image);
- });
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
deleted file mode 100644
index a5d3bf7cc9c1..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car.privacy;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-import java.util.Objects;
-
-/**
- * Class to hold the data for the applications that are using the AppOps permissions.
- */
-public class PrivacyApplication {
- private static final String TAG = "PrivacyApplication";
-
- private String mPackageName;
- private Drawable mIcon;
- private String mApplicationName;
-
- public PrivacyApplication(String packageName, Context context) {
- mPackageName = packageName;
- try {
- ApplicationInfo app = context.getPackageManager()
- .getApplicationInfoAsUser(packageName, 0,
- ActivityManager.getCurrentUser());
- mIcon = context.getPackageManager().getApplicationIcon(app);
- mApplicationName = context.getPackageManager().getApplicationLabel(app).toString();
- } catch (PackageManager.NameNotFoundException e) {
- mApplicationName = packageName;
- Log.e(TAG, "Failed to to find package name", e);
- }
- }
-
- /**
- * Gets the application name.
- */
- public Drawable getIcon() {
- return mIcon;
- }
-
- /**
- * Gets the application name.
- */
- public String getApplicationName() {
- return mApplicationName;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PrivacyApplication that = (PrivacyApplication) o;
- return mPackageName.equals(that.mPackageName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mPackageName);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyDialogBuilder.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyDialogBuilder.java
deleted file mode 100644
index 3b83e7cc0623..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyDialogBuilder.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car.privacy;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-/**
- * Helper class to build the {@link OngoingPrivacyDialog}
- */
-public class PrivacyDialogBuilder {
-
- private Map<PrivacyType, List<PrivacyItem>> mItemsByType;
- private PrivacyApplication mApplication;
- private Context mContext;
-
- public PrivacyDialogBuilder(Context context, List<PrivacyItem> itemsList) {
- mContext = context;
- mItemsByType = itemsList.stream().filter(Objects::nonNull).collect(
- Collectors.groupingBy(PrivacyItem::getPrivacyType));
- List<PrivacyApplication> apps = itemsList.stream().filter(Objects::nonNull).map(
- PrivacyItem::getPrivacyApplication).distinct().collect(Collectors.toList());
- mApplication = apps.size() == 1 ? apps.get(0) : null;
- }
-
- /**
- * Gets the icon id for all the {@link PrivacyItem} in the same order as of itemList.
- */
- public List<Drawable> generateIcons() {
- return mItemsByType.keySet().stream().map(item -> item.getIconId(mContext)).collect(
- Collectors.toList());
- }
-
- /**
- * Gets the application object.
- */
- public PrivacyApplication getApplication() {
- return mApplication;
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
deleted file mode 100644
index d3e123ed8c25..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car.privacy;
-
-import java.util.Objects;
-
-/**
- * Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog}
- */
-public class PrivacyItem {
-
- private PrivacyType mPrivacyType;
- private PrivacyApplication mPrivacyApplication;
-
- public PrivacyItem(PrivacyType privacyType, PrivacyApplication privacyApplication,
- long timeStarted) {
- this.mPrivacyType = privacyType;
- this.mPrivacyApplication = privacyApplication;
- }
-
- /**
- * Gets the application object.
- */
- public PrivacyApplication getPrivacyApplication() {
- return mPrivacyApplication;
- }
-
- /**
- * Gets the privacy type for the application.
- */
- public PrivacyType getPrivacyType() {
- return mPrivacyType;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PrivacyItem that = (PrivacyItem) o;
- return mPrivacyType == that.mPrivacyType
- && mPrivacyApplication.equals(that.mPrivacyApplication);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mPrivacyType, mPrivacyApplication);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyType.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyType.java
deleted file mode 100644
index 8955c87bbff4..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyType.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car.privacy;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-
-import com.android.systemui.R;
-
-/**
- * Enum for storing data for camera, mic and location.
- */
-public enum PrivacyType {
- TYPE_CAMERA(R.string.privacy_type_camera, com.android.internal.R.drawable.ic_camera),
- TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
- TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.ic_mic_white);
-
- private int mNameId;
- private int mIconId;
-
- PrivacyType(int nameId, int iconId) {
- mNameId = nameId;
- mIconId = iconId;
- }
-
- /**
- * Get the icon Id.
- */
- public Drawable getIconId(Context context) {
- return context.getResources().getDrawable(mIconId, null);
- }
-
- /**
- * Get the name Id.
- */
- public String getNameId(Context context) {
- return context.getResources().getString(mNameId);
- }
-}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 738c4257d2c5..19ae97070188 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -102,9 +102,10 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
Thread thread =
new Thread(
() -> {
- mDynSystem.startInstallation("userdata", mUserdataSize, false);
+ mDynSystem.startInstallation();
+ mDynSystem.createPartition("userdata", mUserdataSize, false);
mInstallationSession =
- mDynSystem.startInstallation("system", mSystemSize, true);
+ mDynSystem.createPartition("system", mSystemSize, true);
});
thread.start();
@@ -157,6 +158,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
reportedInstalledSize = installedSize;
}
}
+ mDynSystem.finishInstallation();
return null;
} catch (Exception e) {
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 97b2cb5733b2..3ff5cf490811 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Middelmatig"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Vinnig"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Baie vinnig"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Verval"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Ontkoppel"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ontkoppel tans…"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 62d5dca4541c..264d8de17872 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"መካከለኛ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ፈጣን"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"እጅግ በጣም ፈጣን"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ጊዜው አልፏል"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ተለያይቷል"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"በመለያየት ላይ..."</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index c786e0900199..c23023671c73 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"متوسطة"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"سريعة"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"سريعة جدًا"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"منتهية الصلاحية"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"غير متصل"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"جارٍ قطع الاتصال..."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 552fbb871f51..ab0ed2976508 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"মধ্যমীয়া"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"দ্ৰুত"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"অতি দ্ৰুত"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ম্যাদ উকলিছে"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"সংযোগ বিচ্ছিন্ন কৰি থকা হৈছে…"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 3af2e2559969..fe62d3139b40 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Orta"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Sürətli"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Çox Sürətli"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vaxtı keçib"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Ayrıldı"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ayrılır..."</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 8a76242fd5f0..1619584883bd 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Srednja"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Brza"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veoma brza"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Veza je prekinuta"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prekidanje veze..."</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 5ed4495cd62f..0f2df493696a 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Сярэдняя"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Хуткая"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Вельмі хуткая"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Тэрмін скончыўся"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Адключана"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Адключэнне..."</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index f7a715343ee9..a3be2812ee2e 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Средна"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Бърза"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Много бърза"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Изтекло"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Изкл."</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Изключва се..."</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index f4c677fe69ec..743de50fbb44 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"মাঝারি"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"দ্রুত"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"খুব দ্রুত"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"মেয়াদ শেষ হয়ে গেছে"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ডিসকানেক্ট করা হয়েছে"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"ডিসকানেক্ট হচ্ছে..."</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index fc653b7b1ab5..75a4047bedab 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Srednja brzina"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Brzo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veoma brzo"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Istekao"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Isključen"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prekidanje veze…"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f79375e5d765..8627cf30f707 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Mitjana"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Ràpida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Molt ràpida"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducat"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconnectat"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"S\'està desconnectant..."</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 64400a4958dc..6fef07a566f2 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Střední"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rychlá"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Velmi rychlá"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Platnost vypršela"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Odpojeno"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Odpojování..."</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index de7a2289f083..80ee95e40a28 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Middel"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hurtig"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Meget hurtig"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Udløbet"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> – <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Afbrudt"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Afbryder ..."</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index eafe9d0b500d..7dee120826bd 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Μέτρια"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Γρήγορη"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Πολύ γρήγορη"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Έληξε"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Αποσυνδέθηκε"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Αποσύνδεση..."</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 840f5eede087..41c817e0f6db 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muy rápida"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vencido"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index c86eb34d94b2..897eaa3ffb7f 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muy rápida"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducado"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index f59b96aabc02..9b94222a8fa5 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Keskmine"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Kiire"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Väga kiire"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Aegunud"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Ühendus katkestatud"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ühenduse katkestamine ..."</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 9648139c9136..b0e4b2623a8c 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Tartekoa"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Bizkorra"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Oso bizkorra"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Iraungita"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Deskonektatuta"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Deskonektatzen…"</string>
@@ -455,7 +454,7 @@
<string name="cancel" msgid="5665114069455378395">"Utzi"</string>
<string name="okay" msgid="949938843324579502">"Ados"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktibatu"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktibatu \"Ez molestatu\" modua"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktibatu ez molestatzeko modua"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Inoiz ez"</string>
<string name="zen_interruption_level_priority" msgid="5392140786447823299">"Lehentasuna dutenak soilik"</string>
<string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 3e8de9795e95..7298826f2614 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"متوسط"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"سریع"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"خیلی سریع"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"منقضی‌شده"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"اتصال قطع شد"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"در حال قطع اتصال..."</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 68ddf7df9639..6f58f0352a0a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Kohtuullinen"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Nopea"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Hyvin nopea"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vanhentunut"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Yhteys katkaistu"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Katkaistaan yhteyttä..."</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index ef4800cda193..a5526f3a41b7 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Moyenne"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Élevée"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Très rapide"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expiré"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> : <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Déconnecté"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Déconnexion…"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 256ea03ac55a..eaab027a5659 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Moi rápida"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducou"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando..."</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index ed50f4b2a8cb..00fd024c1323 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"મધ્યમ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ઝડપી"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ખૂબ ઝડપી"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"સમય સમાપ્ત થયો"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ડિસ્કનેક્ટ કર્યું"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"ડિસ્કનેક્ટ થઈ રહ્યું છે..."</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 550eaf82d60f..96aec94808e3 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"मध्यम"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"तेज़"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"अत्‍यधिक तेज़"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"समयसीमा खत्म हो गई"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"डिसकनेक्ट किया गया"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"डिस्‍कनेक्‍ट हो रहा है..."</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 18ea1de4e92b..6df46ca407c0 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Srednje"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Brzo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Vrlo brzo"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Niste povezani"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Isključivanje…"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 7baa0442d9da..6b5b05e6f00d 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Közepes"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Gyors"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Nagyon gyors"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Lejárt"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Szétkapcsolva"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Szétkapcsolás..."</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index a5b03eafb965..c4002b9a1446 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Միջին"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Արագ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Շատ արագ"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Սպառվել է"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Անջատված է"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Անջատվում է..."</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index dea51a889520..753f225491db 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Sedang"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Cepat"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Cepat"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Sudah tidak berlaku"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Sambungan terputus"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Memutus sambungan..."</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 0a85d06e7ad2..b1d40bfbe923 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Miðlungshratt"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hratt"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Mjög hratt"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Útrunninn"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Aftengt"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Aftengist…"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 980bdf9446b6..7f2bd3e9198b 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Veloce"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Molto veloce"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Scaduto"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione..."</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 77fd156e6698..bdd1babefa32 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"בינונית"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"מהירה"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"מהירה מאוד"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"התוקף פג"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"מנותק"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"מתנתק..."</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 1d6b3d596c36..a6192a458ca8 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"普通"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"速い"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"非常に速い"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"期限切れ"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"切断"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"切断中..."</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index a69713a7a955..a3633f64a9f8 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"საშუალო"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"სწრაფი"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ძალიან სწრაფი"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ვადაგასულია"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"კავშირი გაწყვეტილია"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"მიმდინარეობს გათიშვა…"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 380d648e24ab..0962e55e2dda 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Орташа"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Жылдам"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Өте жылдам"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Мерзімі өтті."</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Ажыратылған"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ажыратылуда…"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 478e036204eb..45620e831f9c 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"មធ្យម"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"លឿន"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"លឿន​ណាស់"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ផុតកំណត់"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"បាន​ផ្ដាច់"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"កំពុង​ផ្ដាច់…"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 73aa99851c5e..1fc2e4a91081 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"ಮಧ್ಯಮ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ವೇಗ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ತುಂಬಾ ವೇಗವಾಗಿದೆ"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ಅವಧಿ ಮುಕ್ತಾಯವಾಗಿದೆ"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗುತ್ತಿದೆ..."</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 3ecde32aaa36..8e7c6977245f 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"보통"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"빠름"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"매우 빠름"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"만료됨"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"연결 끊김"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"연결을 끊는 중…"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index ca8992f63f0b..18a1468f2d75 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Орто"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Ылдам"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Абдан ылдам"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Мөөнөтү бүткөн"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Ажыратылган"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ажыратылууда…"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index b8a93229af06..808d5a796a48 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"ປານກາງ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ໄວ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ໄວຫຼາຍ"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ໝົດອາຍຸແລ້ວ"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"ກຳລັງຢຸດການເຊື່ອມຕໍ່..."</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 3d6dfbb4aaa7..78d1b1bf0c5c 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Vidutinis"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Greitas"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Labai greitas"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Baigėsi galiojimo laikas"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Atsijungęs (-usi)"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Atjungiama..."</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index bf0c97d6d3d5..0ae10aa6c43c 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Vidējs"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Ātrs"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Ļoti ātrs"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Beidzies derīguma termiņš"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Atvienots"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Notiek atvienošana..."</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 53b0d4c35090..9850ac5b4ed9 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Средна"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Брза"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Многу брза"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Истечено"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Исклучено"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Се исклучува..."</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 66ff005ee286..2692a30a85d2 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"ഇടത്തരം"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"വേഗത്തിൽ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"വളരെ വേഗത്തിൽ"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"കാലഹരണപ്പെട്ടത്"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"വിച്ഛേദിച്ചു"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"വിച്‌ഛേദിക്കുന്നു..."</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 75633ab01803..966cdffe729b 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"मध्‍यम"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"जलद"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"खूप जलद"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"मुदत संपली"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"डिस्कनेक्ट केले"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"डिस्कनेक्ट करत आहे..."</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index fad37fe13d06..88d7c7d6fd6f 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Sederhana"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Laju"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Laju"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Tamat tempoh"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Diputuskan sambungan"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Memutuskan sambungan..."</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index fd626f6c1536..6dfd2909efd5 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"အတော်အသင့်"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"မြန်"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"အလွန်မြန်"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"သက်တမ်းကုန်သွားပါပြီ"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ချိတ်ဆက်မှုပြတ်တောက်သည်"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"အဆက်အသွယ်ဖြတ်တောက်သည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index baf3c64b5c28..760c409c0048 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Middels"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rask"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veldig rask"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Utløpt"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Frakoblet"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Kobler fra…"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 00a30220acdb..77f87ffc4ea8 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"मध्यम"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"छिटो"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"धेरै छिटो"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"म्याद सकियो"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"विच्छेदन गरियो"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"जडान हटाइँदै ..."</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 533b0f23c28f..55f56d98483b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Gemiddeld"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Snel"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Zeer snel"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Verlopen"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Verbinding verbroken"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Verbinding verbreken..."</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 4a2ab808fc7e..98c302db0c7d 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"ମଧ୍ୟମ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ଦ୍ରୁତ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ଅତି ଦ୍ରୁତ"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ମିଆଦ ଶେଷ ହୋଇଯାଇଛି"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ବିଛିନ୍ନ ହେଲା"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"ବିଚ୍ଛିନ୍ନ କରୁଛି…"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 62da49b0bc4d..525ad2e9a8fb 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"ਔਸਤ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ਤੇਜ਼"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ਬਹੁਤ ਤੇਜ਼"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ਮਿਆਦ ਮੁੱਕ ਗਈ"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ਡਿਸਕਨੈਕਟ ਕੀਤਾ"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"ਡਿਸਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 027afc31d28a..69ebc595f9b6 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Średnia"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Szybka"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Bardzo szybka"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Ważność wygasła"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Rozłączona"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Rozłączanie..."</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 9259c035985b..62017f103241 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Média"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirado"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 0961b27d5512..8304768462f6 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Média"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirado"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desligado"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"A desligar..."</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 9259c035985b..62017f103241 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Média"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirado"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 1fc1d35a5b71..50ea4a00e5cd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Medie"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rapidă"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Foarte rapidă"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirat"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Deconectat"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"În curs de deconectare..."</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index c3baea2dc665..0582a0cde4f7 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Средняя"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Быстрая"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Очень быстрая"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Срок действия истек"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Нет подключения"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Отключение..."</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index f8d2d08abea1..43a49781eeca 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"මධ්‍යම"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"වේගවත්"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ඉතා වේගවත්"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"කල් ඉකුත් විය"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"විසන්ධි වුණි"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"විසන්ධි වෙමින්…"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 0d576b4d5456..3a3958f3a441 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Stredná"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Vysoká"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veľmi vysoká"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Platnosť vypršala"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Odpojený"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prebieha odpájanie..."</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 7c62d52e816b..068f2214659c 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Srednje hitra"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hitra"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Zelo hitra"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Poteklo"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Prekinjena povezava"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prekinjanje povezave ..."</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 7a1bdb55084b..54d463775535 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Mesatare"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"E shpejtë"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Shumë e shpejtë"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Skaduar"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Shkëputur"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Po shkëputet..."</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index e809ab9a9091..ab081ae21a9b 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Средња"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Брза"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Веома брза"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Истекло"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Веза је прекинута"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Прекидање везе..."</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 52a82dbfae02..cab971f8ae1e 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Medelsnabb"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Snabb"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Mycket snabb"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Har upphört att gälla"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Kopplas ifrån"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Kopplar ifrån…"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index c83388a083f9..df71b826f4b9 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Wastani"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Haraka"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Haraka Sana"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Muda umeisha"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Haijaunganishwa"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Inatenganisha..."</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 856fd4f6eafb..084ec16ec05b 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"நடுத்தரம்"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"வேகம்"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"மிகவும் வேகமானது"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"காலாவதியாகிவிட்டது"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"தொடர்பு துண்டிக்கப்பட்டது"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"துண்டிக்கிறது..."</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index cc172473b3b2..cbcaf1a8ddfc 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"మధ్యస్థం"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"వేగవంతం"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"చాలా వేగవంతం"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"గడువు ముగిసింది"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"డిస్‌కనెక్ట్ చేయబడింది"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"డిస్‌కనెక్ట్ చేస్తోంది..."</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 4feb0d6862e8..fa8caa17b824 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"ปานกลาง"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"เร็ว"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"เร็วมาก"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"หมดอายุแล้ว"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"ตัดการเชื่อมต่อ"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"กำลังตัดการเชื่อมต่อ..."</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index e6963b96f851..21fe58c2db28 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Katamtaman"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Mabilis"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Napakabilis"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Nag-expire na"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Hindi nakakonekta"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Nadidiskonekta..."</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index bd457d3ef9e2..4e7f38dca25b 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Orta"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hızlı"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Çok Hızlı"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Süresi sona erdi"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Bağlantı kesildi"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Bağlantı kesiliyor…"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 36a82ea1e92f..98074173f5c9 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Середня"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Швидка"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Дуже швидка"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Термін дії минув"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>: <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Роз’єднано"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Відключення..."</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 50dc72e2218e..e17b0b05838f 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"متوسط"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"تیز"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"بہت تیز"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"میعاد ختم ہو گئی"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"منقطع"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"منقطع کیا جارہا ہے…"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 38edb6d0dcdb..ea51eba747a3 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"O‘rtacha"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Tez"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Juda tez"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Muddati tugagan"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Uzildi"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Uzilyapti…"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index e0798aa54d14..0b71a8c6243a 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Trung bình"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Nhanh"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Rất nhanh"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Đã hết hạn"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Đã ngắt kết nối"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Đang ngắt kết nối…"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 4303615591b9..1805694c3b1f 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"适中"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"快"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"很快"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"已失效"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"已断开连接"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"正在断开连接..."</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index f3b8653cf4e9..1d217b500cc9 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"適中"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"快"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"非常快"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"已過期"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"已中斷連線"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"正在中斷連線..."</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 53b54c4092b6..ee6866505bdb 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"適中"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"快"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"非常快"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"已失效"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"已中斷連線"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"正在中斷連線…"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 249e73ccfa0d..820aa772e7e6 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -61,8 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Okumaphakathi"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Sheshayo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Kushesha kakhulu"</string>
- <!-- no translation found for wifi_passpoint_expired (6540867261754427561) -->
- <skip />
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Iphelelwe isikhathi"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Ayixhunyiwe"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Inqamula uxhumano kwi-inthanethi..."</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a1ef831523b1..80240affc1c1 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -609,6 +609,9 @@
<string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
<!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Sample Rate -->
<string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Sample Rate</string>
+ <!-- UI debug setting: Label for unsupported Bluetooth Audio Codec. [CHAR LIMIT=none] -->
+ <string name="bluetooth_select_a2dp_codec_type_help_info">Gray-out means not supported by phone
+ or headset</string>
<!-- UI debug setting: Trigger Bluetooth Audio Bits Per Sample Selection -->
<string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 2507a3486f2b..0095692336e7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -442,12 +442,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
/**
* Get name from remote device
- * @return {@link BluetoothDevice#getAliasName()} if
- * {@link BluetoothDevice#getAliasName()} is not null otherwise return
+ * @return {@link BluetoothDevice#getAlias()} if
+ * {@link BluetoothDevice#getAlias()} is not null otherwise return
* {@link BluetoothDevice#getAddress()}
*/
public String getName() {
- final String aliasName = mDevice.getAliasName();
+ final String aliasName = mDevice.getAlias();
return TextUtils.isEmpty(aliasName) ? getAddress() : aliasName;
}
@@ -505,7 +505,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
* @return true if device's alias name is not null nor empty, false otherwise
*/
public boolean hasHumanReadableName() {
- return !TextUtils.isEmpty(mDevice.getAliasName());
+ return !TextUtils.isEmpty(mDevice.getAlias());
}
/**
@@ -652,7 +652,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
if (BluetoothUtils.D) {
- Log.e(TAG, "updating profiles for " + mDevice.getAliasName() + ", " + mDevice);
+ Log.e(TAG, "updating profiles for " + mDevice.getAlias() + ", " + mDevice);
BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 9f71033de758..cca9cfac2d22 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -168,7 +168,7 @@ public class CachedBluetoothDeviceManager {
return cachedDevice.getName();
}
- String name = device.getAliasName();
+ String name = device.getAlias();
if (name != null) {
return name;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 274696bfec0e..69487d508443 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -39,6 +39,7 @@ import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
+import android.os.UserManager;
import com.android.settingslib.R;
@@ -176,8 +177,8 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback {
boolean isManaged = context.getSystemService(DevicePolicyManager.class)
.getProfileOwnerAsUser(userId) != null;
if (isManaged) {
- badge = getDrawableForDisplayDensity(
- context, com.android.internal.R.drawable.ic_corp_badge_case);
+ badge = getDrawableForDisplayDensity(context,
+ context.getSystemService(UserManager.class).getUserBadgeResId(userId));
}
}
return setBadge(badge);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index d5d2e9e8c146..f506b7c12d81 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -1,7 +1,7 @@
# Default reviewers for this and subdirectories.
+qal@google.com
+arcwang@google.com
+govenliu@google.com
asapperstein@google.com
-asargent@google.com
-dling@google.com
-zhfan@google.com
# Emergency approvers in case the above are not available \ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index aef7fae8ee0b..2c4f57fa77be 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -92,9 +92,9 @@ public class CachedBluetoothDeviceManagerTest {
when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
when(mDevice3.getName()).thenReturn(DEVICE_NAME_3);
- when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
- when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
- when(mDevice3.getAliasName()).thenReturn(DEVICE_ALIAS_3);
+ when(mDevice1.getAlias()).thenReturn(DEVICE_ALIAS_1);
+ when(mDevice2.getAlias()).thenReturn(DEVICE_ALIAS_2);
+ when(mDevice3.getAlias()).thenReturn(DEVICE_ALIAS_3);
when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS_1);
when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
when(mDevice3.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
@@ -240,7 +240,7 @@ public class CachedBluetoothDeviceManagerTest {
assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
final String newAliasName = "NewAliasName";
- when(mDevice1.getAliasName()).thenReturn(newAliasName);
+ when(mDevice1.getAlias()).thenReturn(newAliasName);
mCachedDeviceManager.onDeviceNameUpdated(mDevice1);
assertThat(cachedDevice1.getName()).isEqualTo(newAliasName);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5c89a019bf82..53ff1a10b6ff 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -702,7 +702,7 @@ public class CachedBluetoothDeviceTest {
@Test
public void deviceName_testAliasNameAvailable() {
- when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS);
+ when(mDevice.getAlias()).thenReturn(DEVICE_ALIAS);
when(mDevice.getName()).thenReturn(DEVICE_NAME);
CachedBluetoothDevice cachedBluetoothDevice =
new CachedBluetoothDevice(mContext, mProfileManager, mDevice);
@@ -725,7 +725,7 @@ public class CachedBluetoothDeviceTest {
@Test
public void deviceName_testRenameDevice() {
final String[] alias = {DEVICE_ALIAS};
- doAnswer(invocation -> alias[0]).when(mDevice).getAliasName();
+ doAnswer(invocation -> alias[0]).when(mDevice).getAlias();
doAnswer(invocation -> {
alias[0] = (String) invocation.getArguments()[0];
return true;
@@ -842,14 +842,14 @@ public class CachedBluetoothDeviceTest {
@Test
public void getName_aliasNameNotNull_returnAliasName() {
- when(mDevice.getAliasName()).thenReturn(DEVICE_NAME);
+ when(mDevice.getAlias()).thenReturn(DEVICE_NAME);
assertThat(mCachedDevice.getName()).isEqualTo(DEVICE_NAME);
}
@Test
public void getName_aliasNameIsNull_returnAddress() {
- when(mDevice.getAliasName()).thenReturn(null);
+ when(mDevice.getAlias()).thenReturn(null);
assertThat(mCachedDevice.getName()).isEqualTo(DEVICE_ADDRESS);
}
@@ -857,7 +857,7 @@ public class CachedBluetoothDeviceTest {
@Test
public void setName_setDeviceNameIsNotNull() {
final String name = "test name";
- when(mDevice.getAliasName()).thenReturn(DEVICE_NAME);
+ when(mDevice.getAlias()).thenReturn(DEVICE_NAME);
mCachedDevice.setName(name);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 2b5466c4161f..7be176a37bb4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -75,8 +75,8 @@ public class HearingAidDeviceManagerTest {
when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
- when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
- when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
+ when(mDevice1.getAlias()).thenReturn(DEVICE_ALIAS_1);
+ when(mDevice2.getAlias()).thenReturn(DEVICE_ALIAS_2);
when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS);
when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS);
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 10d990a3d07f..289ac802ccb0 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -605,6 +605,7 @@ public class SettingsBackupTest {
Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+ Settings.Secure.ATTENTIVE_TIMEOUT,
Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT,
Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index a4dbf38d8782..f170a11edd7e 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -24,12 +24,6 @@ the main path for onConfigurationChanged, now also happens through
ConfigurationController). They also receive a callback for onBootCompleted
since these objects may be started before the device has finished booting.
-SystemUI and SystemUIApplication also have methods for putComponent and
-getComponent which were existing systems to get a hold of other parts of
-sysui before Dependency existed. Generally new things should not be added
-to putComponent, instead Dependency and other refactoring is preferred to
-make sysui structure cleaner.
-
Each SystemUI service is expected to be a major part of system ui and the
goal is to minimize communication between them. So in general they should be
relatively silo'd.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
new file mode 100644
index 000000000000..bd86407222bc
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.graphics.Point;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/** Plugin to handle navigation edge gestures for Back. */
+@ProvidesInterface(
+ action = NavigationEdgeBackPlugin.ACTION,
+ version = NavigationEdgeBackPlugin.VERSION)
+public interface NavigationEdgeBackPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_NAVIGATION_EDGE_BACK_ACTION";
+ int VERSION = 1;
+
+
+ /** Specifies if the UI should be rendered on the left side of the screen. */
+ void setIsLeftPanel(boolean isLeftPanel);
+
+ /** Sets the insets for the gesture handling area. */
+ void setInsets(int leftInset, int rightInset);
+
+ /** Sets the display size. */
+ void setDisplaySize(Point displaySize);
+
+ /** Sets the callback that should be invoked when a Back gesture is detected. */
+ void setBackCallback(BackCallback callback);
+
+ /** Sets the base LayoutParams for the UI. */
+ void setLayoutParams(WindowManager.LayoutParams layoutParams);
+
+ /** Updates the UI based on the motion events passed in device coordinates. */
+ void onMotionEvent(MotionEvent motionEvent);
+
+ /** Callback to let the system react to the detected back gestures. */
+ interface BackCallback {
+ /** Indicates that a Back gesture was recognized and the system should go back. */
+ void triggerBack();
+
+ /** Indicates that the gesture was cancelled and the system should not go back. */
+ void cancelBack();
+ }
+}
diff --git a/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml b/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml
new file mode 100644
index 000000000000..eb21c43aaa1c
--- /dev/null
+++ b/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sleep_warning_dialog_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:theme="@android:style/Theme.DeviceDefault.Dialog"
+ android:focusable="true">
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:alpha="?android:backgroundDimAmount" />
+ <LinearLayout
+ android:layout_width="380dp"
+ android:layout_height="wrap_content"
+ android:background="@drawable/rounded_bg_full"
+ android:padding="16dp"
+ android:layout_margin="32dp"
+ android:layout_gravity="bottom|right"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inattentive_sleep_warning_title"
+ android:layout_marginBottom="8dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inattentive_sleep_warning_message"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/inattentive_sleep_warning.xml b/packages/SystemUI/res/layout/inattentive_sleep_warning.xml
new file mode 100644
index 000000000000..f1f9b1fcf74e
--- /dev/null
+++ b/packages/SystemUI/res/layout/inattentive_sleep_warning.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sleep_warning_dialog_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:theme="@android:style/Theme.Material.Dialog"
+ android:focusable="true">
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:alpha="?android:backgroundDimAmount" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/rounded_bg_full"
+ android:layout_margin="8dp"
+ android:padding="16dp"
+ android:layout_gravity="top"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inattentive_sleep_warning_title"
+ android:layout_marginBottom="8dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="@android:style/TextAppearance.Material.Large"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inattentive_sleep_warning_message"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@android:style/TextAppearance.Material"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index dff21cfd2c22..c3f410ea4e55 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -281,6 +281,7 @@
<item>com.android.systemui.statusbar.phone.StatusBar</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
+ <item>com.android.systemui.power.InattentiveSleepWarningController</item>
<item>com.android.systemui.media.RingtonePlayer</item>
<item>com.android.systemui.keyboard.KeyboardUI</item>
<item>com.android.systemui.pip.PipUI</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 19daa9039f55..313854701d69 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2507,4 +2507,11 @@
<!-- Notification content text when switching to a default launcher that supports gesture navigation [CHAR LIMIT=NONE] -->
<string name="notification_content_gesture_nav_available">Go to Settings to update system navigation</string>
+ <!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] -->
+ <string name="inattentive_sleep_warning_title">Standby</string>
+ <!-- Message of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=NONE] -->
+ <string name="inattentive_sleep_warning_message" product="tv">The Android TV device will soon turn off; press a button to keep it on.</string>
+ <!-- Message of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=NONE] -->
+ <string name="inattentive_sleep_warning_message" product="default">The device will soon turn off; press to keep it on.</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java b/packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java
deleted file mode 100644
index ff4b7cb0ac71..000000000000
--- a/packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java
+++ /dev/null
@@ -1,29 +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.content.Context;
-
-/**
- * The interface for getting core components of SysUI. Exists for Testability
- * since tests don't have SystemUIApplication as their ApplicationContext.
- */
-public interface SysUiServiceProvider {
- <T> T getComponent(Class<T> interfaceType);
-
- public static <T> T getComponent(Context context, Class<T> interfaceType) {
- return ((SysUiServiceProvider) context.getApplicationContext()).getComponent(interfaceType);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 75700379caca..f795faf30603 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -23,11 +23,9 @@ import android.os.Bundle;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Map;
-public abstract class SystemUI implements SysUiServiceProvider {
+public abstract class SystemUI {
protected final Context mContext;
- public Map<Class<?>, Object> mComponents;
public SystemUI(Context context) {
mContext = context;
@@ -44,17 +42,6 @@ public abstract class SystemUI implements SysUiServiceProvider {
protected void onBootCompleted() {
}
- @SuppressWarnings("unchecked")
- public <T> T getComponent(Class<T> interfaceType) {
- return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
- }
-
- public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
- if (mComponents != null) {
- mComponents.put(interfaceType, component);
- }
- }
-
public static void overrideNotificationAppName(Context context, Notification.Builder n,
boolean system) {
final Bundle extras = new Bundle();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 4b28e4af7d8b..f0317b4a02d0 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -36,13 +36,11 @@ import com.android.systemui.util.NotificationChannels;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
-import java.util.Map;
/**
* Application class for SystemUI.
*/
-public class SystemUIApplication extends Application implements SysUiServiceProvider,
+public class SystemUIApplication extends Application implements
SystemUIAppComponentFactory.ContextInitializer {
public static final String TAG = "SystemUIService";
@@ -56,7 +54,6 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
private SystemUI[] mServices;
private boolean mServicesStarted;
private boolean mBootCompleted;
- private final Map<Class<?>, Object> mComponents = new HashMap<>();
private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
public SystemUIApplication() {
@@ -199,7 +196,6 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
throw new RuntimeException(ex);
}
- mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
log.traceEnd();
@@ -232,11 +228,6 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
}
}
- @SuppressWarnings("unchecked")
- public <T> T getComponent(Class<T> interfaceType) {
- return (T) mComponents.get(interfaceType);
- }
-
public SystemUI[] getServices() {
return mServices;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1052a991dff8..db1185fb96f9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -47,6 +47,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -81,6 +82,7 @@ import com.android.systemui.statusbar.notification.NotificationInterruptionState
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -1006,8 +1008,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey());
return false;
}
+ PackageManager packageManager = StatusBar.getPackageManagerForUser(
+ context, entry.getSbn().getUser().getIdentifier());
ActivityInfo info =
- intent.getIntent().resolveActivityInfo(context.getPackageManager(), 0);
+ intent.getIntent().resolveActivityInfo(packageManager, 0);
if (info == null) {
Log.w(TAG, "Unable to send as bubble, "
+ entry.getKey() + " couldn't find activity info for intent: "
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
index 7007f9defee6..6744d74004f0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -35,7 +35,6 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -51,7 +50,6 @@ import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -259,7 +257,4 @@ public abstract class DependencyBinder {
@Binds
public abstract VolumeComponent provideVolumeComponent(
VolumeDialogComponent volumeDialogComponent);
- /** */
- @Binds
- public abstract HeadsUpManager bindHeadsUpManager(HeadsUpManagerPhone headsUpManagerPhone);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 3cf14d65e5b8..25986c5f4160 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -25,6 +25,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.pip.PipUI;
+import com.android.systemui.power.InattentiveSleepWarningController;
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
@@ -102,6 +103,13 @@ public abstract class SystemUIBinder {
@ClassKey(PowerUI.class)
public abstract SystemUI bindPowerUI(PowerUI sysui);
+ /** Inject into InattentiveSleepWarningController. */
+ @Binds
+ @IntoMap
+ @ClassKey(InattentiveSleepWarningController.class)
+ public abstract SystemUI bindInattentiveSleepWarningController(
+ InattentiveSleepWarningController sysui);
+
/** Inject into Recents. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index f1d02bb61ef9..f44eae76ec50 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -25,6 +25,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.recents.Recents;
@@ -34,9 +35,12 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.Optional;
@@ -93,6 +97,17 @@ abstract class SystemUIDefaultModule {
return new Divider(context, recentsOptionalLazy);
}
+ @Singleton
+ @Provides
+ static HeadsUpManagerPhone provideHeadsUpManagerPhone(Context context,
+ StatusBarStateController statusBarStateController,
+ KeyguardBypassController bypassController) {
+ return new HeadsUpManagerPhone(context, statusBarStateController, bypassController);
+ }
+
+ @Binds
+ abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
+
@Provides
@Singleton
static Recents provideRecents(Context context, RecentsImplementation recentsImplementation,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 6ae21b39f331..0ac158d1b38a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -28,10 +28,15 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.util.time.SystemClockImpl;
import javax.inject.Singleton;
@@ -80,8 +85,19 @@ public abstract class SystemUIModule {
abstract Divider optionalDivider();
@BindsOptionalOf
+ abstract HeadsUpManager optionalHeadsUpManager();
+
+ @BindsOptionalOf
abstract Recents optionalRecents();
@BindsOptionalOf
abstract StatusBar optionalStatusBar();
+
+ @Singleton
+ @Binds
+ abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
+
+ @Singleton
+ @Binds
+ abstract NotifListBuilder bindNotifListBuilder(NotifListBuilderImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningController.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningController.java
new file mode 100644
index 000000000000..716943164b61
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.power;
+
+import android.content.Context;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Receives messages sent from {@link com.android.server.power.InattentiveSleepWarningController}
+ * and shows the appropriate inattentive sleep UI (e.g. {@link InattentiveSleepWarningView}).
+ */
+@Singleton
+public class InattentiveSleepWarningController extends SystemUI implements CommandQueue.Callbacks {
+ private final CommandQueue mCommandQueue;
+ private InattentiveSleepWarningView mOverlayView;
+
+ @Inject
+ public InattentiveSleepWarningController(Context context, CommandQueue commandQueue) {
+ super(context);
+ mCommandQueue = commandQueue;
+ }
+
+ @Override
+ public void start() {
+ mCommandQueue.addCallback(this);
+ }
+
+ @Override
+ public void showInattentiveSleepWarning() {
+ if (mOverlayView == null) {
+ mOverlayView = new InattentiveSleepWarningView(mContext);
+ }
+
+ mOverlayView.show();
+ }
+
+ @Override
+ public void dismissInattentiveSleepWarning() {
+ if (mOverlayView != null) {
+ mOverlayView.dismiss();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
new file mode 100644
index 000000000000..8ccc679d32b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.power;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+/**
+ * View that shows a warning shortly before the device goes into sleep
+ * after prolonged user inactivity when bound to.
+ */
+public class InattentiveSleepWarningView extends FrameLayout {
+ private final IBinder mWindowToken = new Binder();
+ private final WindowManager mWindowManager;
+
+ InattentiveSleepWarningView(Context context) {
+ super(context);
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+
+ final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ layoutInflater.inflate(R.layout.inattentive_sleep_warning, this, true /* attachToRoot */);
+
+ setFocusable(true);
+ setOnKeyListener((v, keyCode, event) -> {
+ // overlay consumes key presses
+ return true;
+ });
+ }
+
+ /**
+ * Show the warning.
+ */
+ public void show() {
+ if (getParent() == null) {
+ mWindowManager.addView(this, getLayoutParams(mWindowToken));
+ }
+ }
+
+ /**
+ * Dismiss the warning.
+ */
+ public void dismiss() {
+ if (getParent() != null) {
+ mWindowManager.removeView(this);
+ }
+ }
+
+ /**
+ * @param windowToken token for the window
+ */
+ private WindowManager.LayoutParams getLayoutParams(IBinder windowToken) {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("InattentiveSleepWarning");
+ lp.token = windowToken;
+ return lp;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 5de6d1c42b4f..0134aa3a15df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -169,7 +169,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
v.setText(mContext.getString(
com.android.internal.R.string.bugreport_status,
- Build.VERSION.RELEASE_OR_CODENAME,
+ Build.VERSION.RELEASE,
Build.ID));
v.setVisibility(View.VISIBLE);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 94a1cf0c4e35..d377f1c793a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -319,7 +319,14 @@ public class QuickQSPanel extends QSPanel {
} else{
mColumns = mCellWidth == 0 ? 1 :
Math.min(maxTiles, availableWidth / mCellWidth );
- mCellMarginHorizontal = (availableWidth - mColumns * mCellWidth) / (mColumns - 1);
+ // If we can only fit one column, use mCellMarginHorizontal to center it.
+ if (mColumns == 1) {
+ mCellMarginHorizontal = (availableWidth - mCellWidth) / 2;
+ } else {
+ mCellMarginHorizontal =
+ (availableWidth - mColumns * mCellWidth) / (mColumns - 1);
+ }
+
}
return mColumns != prevNumColumns;
}
@@ -357,6 +364,10 @@ public class QuickQSPanel extends QSPanel {
@Override
protected int getColumnStart(int column) {
+ if (mColumns == 1) {
+ // Only one column/tile. Use the margin to center the tile.
+ return getPaddingStart() + mCellMarginHorizontal;
+ }
return getPaddingStart() + column * (mCellWidth + mCellMarginHorizontal);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 60bc6b6855e6..fcdd23463c5b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -35,7 +35,6 @@ import android.widget.Toast;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.Divider;
@@ -73,7 +72,7 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
}
@Override
- public void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {
+ public void onStart(Context context) {
mContext = context;
mHandler = new Handler();
mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 882930bc1049..5f37cc4520a3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -45,7 +45,7 @@ public class Recents extends SystemUI implements CommandQueue.Callbacks {
@Override
public void start() {
mCommandQueue.addCallback(this);
- mImpl.onStart(mContext, this);
+ mImpl.onStart(mContext);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
index 8cd17e9a1728..1d29ac629cd8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -19,15 +19,13 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
-import com.android.systemui.SysUiServiceProvider;
-
import java.io.PrintWriter;
/**
* API for creating a Recents view.
*/
public interface RecentsImplementation {
- default void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {}
+ default void onStart(Context context) {}
default void onBootCompleted() {}
default void onAppTransitionFinished() {}
default void onConfigurationChanged(Configuration newConfig) {}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
new file mode 100644
index 000000000000..8c4865510ed1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
+
+/**
+ * An AsyncTask that deletes an image from the media store in the background.
+ */
+class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
+ private Context mContext;
+
+ DeleteImageInBackgroundTask(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected Void doInBackground(Uri... params) {
+ if (params.length != 1) return null;
+
+ Uri screenshotUri = params[0];
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.delete(screenshotUri, null, null);
+ return null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 7dc3236915e8..79632039c832 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -32,52 +32,31 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
import android.app.Notification;
-import android.app.Notification.BigPictureStyle;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipDescription;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Picture;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.media.ExifInterface;
import android.media.MediaActionSound;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.DeviceConfig;
-import android.provider.MediaStore;
-import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
@@ -93,32 +72,16 @@ import android.widget.ImageView;
import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.dagger.qualifiers.MainResources;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.NotificationChannels;
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
import java.util.Collections;
-import java.util.Date;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -140,7 +103,7 @@ public class GlobalScreenshot {
/**
* POD used in the AsyncTask which saves an image in the background.
*/
- private static class SaveImageInBackgroundData {
+ static class SaveImageInBackgroundData {
public Context context;
public Bitmap image;
public Uri imageUri;
@@ -156,426 +119,12 @@ public class GlobalScreenshot {
imageUri = null;
iconSize = 0;
}
+
void clearContext() {
context = null;
}
}
- /**
- * An AsyncTask that saves an image to the media store in the background.
- */
- private static class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
- private static final String TAG = "SaveImageInBackgroundTask";
-
- private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
- private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
-
- private final SaveImageInBackgroundData mParams;
- private final NotificationManager mNotificationManager;
- private final Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
- private final String mImageFileName;
- private final long mImageTime;
- private final BigPictureStyle mNotificationStyle;
- private final int mImageWidth;
- private final int mImageHeight;
- private final Handler mHandler;
- private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
-
- SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
- NotificationManager nManager) {
- Resources r = context.getResources();
-
- // Prepare all the output metadata
- mParams = data;
- mImageTime = System.currentTimeMillis();
- String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
- mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
-
- // Initialize screenshot notification smart actions provider.
- mHandler = new Handler();
- mSmartActionsProvider =
- SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider();
-
- // Create the large notification icon
- mImageWidth = data.image.getWidth();
- mImageHeight = data.image.getHeight();
- int iconSize = data.iconSize;
- int previewWidth = data.previewWidth;
- int previewHeight = data.previewheight;
-
- Paint paint = new Paint();
- ColorMatrix desat = new ColorMatrix();
- desat.setSaturation(0.25f);
- paint.setColorFilter(new ColorMatrixColorFilter(desat));
- Matrix matrix = new Matrix();
- int overlayColor = 0x40FFFFFF;
-
- matrix.setTranslate((previewWidth - mImageWidth) / 2,
- (previewHeight - mImageHeight) / 2);
- Bitmap picture = generateAdjustedHwBitmap(data.image, previewWidth, previewHeight,
- matrix, paint, overlayColor);
-
- // Note, we can't use the preview for the small icon, since it is non-square
- float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
- matrix.setScale(scale, scale);
- matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
- (iconSize - (scale * mImageHeight)) / 2);
- Bitmap icon = generateAdjustedHwBitmap(data.image, iconSize, iconSize, matrix, paint,
- overlayColor);
-
- mNotificationManager = nManager;
- final long now = System.currentTimeMillis();
-
- // Setup the notification
- mNotificationStyle = new Notification.BigPictureStyle()
- .bigPicture(picture.createAshmemBitmap());
-
- // The public notification will show similar info but with the actual screenshot omitted
- mPublicNotificationBuilder =
- new Notification.Builder(context, NotificationChannels.SCREENSHOTS_HEADSUP)
- .setContentTitle(r.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setCategory(Notification.CATEGORY_PROGRESS)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(r.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder, true);
-
- mNotificationBuilder = new Notification.Builder(context,
- NotificationChannels.SCREENSHOTS_HEADSUP)
- .setContentTitle(r.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(r.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setStyle(mNotificationStyle)
- .setPublicVersion(mPublicNotificationBuilder.build());
- mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
- SystemUI.overrideNotificationAppName(context, mNotificationBuilder, true);
-
- mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
-
- /**
- * NOTE: The following code prepares the notification builder for updating the
- * notification after the screenshot has been written to disk.
- */
-
- // On the tablet, the large icon makes the notification appear as if it is clickable
- // (and on small devices, the large icon is not shown) so defer showing the large icon
- // until we compose the final post-save notification below.
- mNotificationBuilder.setLargeIcon(icon.createAshmemBitmap());
- // But we still don't set it for the expanded view, allowing the smallIcon to show here.
- mNotificationStyle.bigLargeIcon((Bitmap) null);
- }
-
- private int getUserHandleOfForegroundApplication(Context context) {
- // This logic matches
- // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile
- try {
- return ActivityTaskManager.getService().getLastResumedActivityUserId();
- } catch (RemoteException e) {
- Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e);
- return context.getUserId();
- }
- }
-
- private boolean isManagedProfile(Context context) {
- UserManager manager = UserManager.get(context);
- UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context));
- return info.isManagedProfile();
- }
-
- /**
- * Generates a new hardware bitmap with specified values, copying the content from the
- * passed in bitmap.
- */
- private Bitmap generateAdjustedHwBitmap(Bitmap bitmap, int width, int height, Matrix matrix,
- Paint paint, int color) {
- Picture picture = new Picture();
- Canvas canvas = picture.beginRecording(width, height);
- canvas.drawColor(color);
- canvas.drawBitmap(bitmap, matrix, paint);
- picture.endRecording();
- return Bitmap.createBitmap(picture);
- }
-
- @Override
- protected Void doInBackground(Void... paramsUnused) {
- if (isCancelled()) {
- return null;
- }
-
- // By default, AsyncTask sets the worker thread to have background thread priority,
- // so bump it back up so that we save a little quicker.
- Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
-
- Context context = mParams.context;
- Bitmap image = mParams.image;
- boolean smartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
- CompletableFuture<List<Notification.Action>> smartActionsFuture = getSmartActionsFuture(
- context, image, mSmartActionsProvider, mHandler, smartActionsEnabled,
- isManagedProfile(context));
-
- Resources r = context.getResources();
-
- try {
- // Save the screenshot to the MediaStore
- final MediaStore.PendingParams params = new MediaStore.PendingParams(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
- params.setRelativePath(Environment.DIRECTORY_PICTURES + File.separator
- + Environment.DIRECTORY_SCREENSHOTS);
-
- final Uri uri = MediaStore.createPending(context, params);
- final MediaStore.PendingSession session = MediaStore.openPending(context, uri);
- try {
- // First, write the actual data for our screenshot
- try (OutputStream out = session.openOutputStream()) {
- if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
- throw new IOException("Failed to compress");
- }
- }
-
- // Next, write metadata to help index the screenshot
- try (ParcelFileDescriptor pfd = session.open()) {
- final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
-
- exif.setAttribute(ExifInterface.TAG_SOFTWARE,
- "Android " + Build.DISPLAY);
-
- exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH,
- Integer.toString(image.getWidth()));
- exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH,
- Integer.toString(image.getHeight()));
-
- final ZonedDateTime time = ZonedDateTime.ofInstant(
- Instant.ofEpochMilli(mImageTime), ZoneId.systemDefault());
- exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL,
- DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(time));
- exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL,
- DateTimeFormatter.ofPattern("SSS").format(time));
-
- if (Objects.equals(time.getOffset(), ZoneOffset.UTC)) {
- exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00");
- } else {
- exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
- DateTimeFormatter.ofPattern("XXX").format(time));
- }
-
- exif.saveAttributes();
- }
- session.publish();
- } catch (Exception e) {
- session.abandon();
- throw e;
- } finally {
- IoUtils.closeQuietly(session);
- }
-
- // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
- // order to do some common work like dismissing the keyguard and sending
- // closeSystemWindows
-
- // Create a share intent, this will always go through the chooser activity first
- // which should not trigger auto-enter PiP
- String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
- String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
- Intent sharingIntent = new Intent(Intent.ACTION_SEND);
- sharingIntent.setType("image/png");
- sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
- // Include URI in ClipData also, so that grantPermission picks it up.
- // We don't use setData here because some apps interpret this as "to:".
- ClipData clipdata = new ClipData(new ClipDescription("content",
- new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
- new ClipData.Item(uri));
- sharingIntent.setClipData(clipdata);
- sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
- sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- // Make sure pending intents for the system user are still unique across users
- // by setting the (otherwise unused) request code to the current user id.
- int requestCode = context.getUserId();
-
- PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
- chooserAction.getIntentSender())
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- // Create a share action for the notification
- PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
- .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent)
- .putExtra(EXTRA_DISALLOW_ENTER_PIP, true)
- .setAction(Intent.ACTION_SEND),
- PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
- Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_share,
- r.getString(com.android.internal.R.string.share), shareAction);
- mNotificationBuilder.addAction(shareActionBuilder.build());
-
- // Create an edit intent, if a specific package is provided as the editor, then
- // launch that directly
- String editorPackage = context.getString(R.string.config_screenshotEditor);
- Intent editIntent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- editIntent.setType("image/png");
- editIntent.setData(uri);
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // Create a edit action
- PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
- .putExtra(EXTRA_ACTION_INTENT, editIntent)
- .putExtra(EXTRA_CANCEL_NOTIFICATION,
- editIntent.getComponent() != null)
- .setAction(Intent.ACTION_EDIT),
- PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
- Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_edit,
- r.getString(com.android.internal.R.string.screenshot_edit), editAction);
- mNotificationBuilder.addAction(editActionBuilder.build());
- if (editAction != null && mParams.onEditReady != null) {
- mParams.onEditReady.apply(editAction);
- }
-
- // Create a delete action for the notification
- PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
- .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_delete,
- r.getString(com.android.internal.R.string.delete), deleteAction);
- mNotificationBuilder.addAction(deleteActionBuilder.build());
-
- mParams.imageUri = uri;
- mParams.image = null;
- mParams.errorMsgResId = 0;
-
- if (smartActionsEnabled) {
- int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags
- .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
- 1000);
- List<Notification.Action> smartActions = getSmartActions(smartActionsFuture,
- timeoutMs);
- for (Notification.Action action : smartActions) {
- mNotificationBuilder.addAction(action);
- }
- }
- } catch (Exception e) {
- // IOException/UnsupportedOperationException may be thrown if external storage is
- // not mounted
- Slog.e(TAG, "unable to save screenshot", e);
- mParams.clearImage();
- mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
- }
-
- // Recycle the bitmap data
- if (image != null) {
- image.recycle();
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void params) {
- if (mParams.errorMsgResId != 0) {
- // Show a message that we've failed to save the image to disk
- GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
- mParams.errorMsgResId);
- } else {
- if (mParams.onEditReady != null) {
- // Cancel the "saving screenshot" notification
- mNotificationManager.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
- } else {
- // Show the final notification to indicate screenshot saved
- Context context = mParams.context;
- Resources r = context.getResources();
-
- // Create the intent to show the screenshot in gallery
- Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(mParams.imageUri, "image/png");
- launchIntent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- final long now = System.currentTimeMillis();
-
- // Update the text and the icon for the existing notification
- mPublicNotificationBuilder
- .setContentTitle(r.getString(R.string.screenshot_saved_title))
- .setContentText(r.getString(R.string.screenshot_saved_text))
- .setContentIntent(
- PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(context.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- mNotificationBuilder
- .setContentTitle(r.getString(R.string.screenshot_saved_title))
- .setContentText(r.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(mParams.context, 0,
- launchIntent, 0))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(context.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setPublicVersion(mPublicNotificationBuilder.build())
- .setFlag(Notification.FLAG_NO_CLEAR, false);
-
- mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
- }
- }
- mParams.finisher.run();
- mParams.clearContext();
- }
-
- @Override
- protected void onCancelled(Void params) {
- // If we are cancelled while the task is running in the background, we may get null
- // params. The finisher is expected to always be called back, so just use the baked-in
- // params from the ctor in any case.
- mParams.finisher.run();
- mParams.clearImage();
- mParams.clearContext();
-
- // Cancel the posted notification
- mNotificationManager.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
- }
- }
-
- /**
- * An AsyncTask that deletes an image from the media store in the background.
- */
- private static class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
- private Context mContext;
-
- DeleteImageInBackgroundTask(Context context) {
- mContext = context;
- }
-
- @Override
- protected Void doInBackground(Uri... params) {
- if (params.length != 1) return null;
-
- Uri screenshotUri = params[0];
- ContentResolver resolver = mContext.getContentResolver();
- resolver.delete(screenshotUri, null, null);
- return null;
- }
- }
-
static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
@@ -639,7 +188,6 @@ public class GlobalScreenshot {
}
};
-
/**
* @param context everything needs a context :(
*/
@@ -670,25 +218,25 @@ public class GlobalScreenshot {
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
WindowManager.LayoutParams.TYPE_SCREENSHOT,
WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mNotificationManager =
- (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
mDisplay.getRealMetrics(mDisplayMetrics);
// Get the various target sizes
mNotificationIconSize =
- resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
+ resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
// Scale has to account for both sides of the bg
mBgPadding = (float) resources.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);
- mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
+ mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
// determine the optimal preview size
int panelWidth = 0;
@@ -916,6 +464,7 @@ public class GlobalScreenshot {
mScreenshotAnimation.start();
});
}
+
private ValueAnimator createScreenshotDropInAnimation() {
final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
/ SCREENSHOT_DROP_IN_DURATION);
@@ -965,6 +514,7 @@ public class GlobalScreenshot {
mScreenshotFlash.setAlpha(0f);
mScreenshotFlash.setVisibility(View.VISIBLE);
}
+
@Override
public void onAnimationEnd(android.animation.Animator animation) {
mScreenshotFlash.setVisibility(View.GONE);
@@ -975,7 +525,7 @@ public class GlobalScreenshot {
public void onAnimationUpdate(ValueAnimator animation) {
float t = (Float) animation.getAnimatedValue();
float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
+ - scaleInterpolator.getInterpolation(t)
* (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
mScreenshotView.setAlpha(t);
@@ -986,6 +536,7 @@ public class GlobalScreenshot {
});
return anim;
}
+
private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
boolean navBarVisible) {
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
@@ -1007,7 +558,8 @@ public class GlobalScreenshot {
public void onAnimationUpdate(ValueAnimator animation) {
float t = (Float) animation.getAnimatedValue();
float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
+ - t * (SCREENSHOT_DROP_IN_MIN_SCALE
+ - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
mScreenshotView.setAlpha(1f - t);
mScreenshotView.setScaleX(scaleT);
@@ -1034,8 +586,10 @@ public class GlobalScreenshot {
float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
final PointF finalPos = new PointF(
- -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
- -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
+ -halfScreenWidth
+ + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
+ -halfScreenHeight
+ + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
// Animate the screenshot to the status bar
anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
@@ -1044,7 +598,7 @@ public class GlobalScreenshot {
public void onAnimationUpdate(ValueAnimator animation) {
float t = (Float) animation.getAnimatedValue();
float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
+ - scaleInterpolator.getInterpolation(t)
* (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
@@ -1105,15 +659,15 @@ public class GlobalScreenshot {
// Repurpose the existing notification to notify the user of the error
Notification.Builder b = new Notification.Builder(context, NotificationChannels.ALERTS)
- .setTicker(r.getString(R.string.screenshot_failed_title))
- .setContentTitle(r.getString(R.string.screenshot_failed_title))
- .setContentText(errorMsg)
- .setSmallIcon(R.drawable.stat_notify_image_error)
- .setWhen(System.currentTimeMillis())
- .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
- .setCategory(Notification.CATEGORY_ERROR)
- .setAutoCancel(true)
- .setColor(context.getColor(
+ .setTicker(r.getString(R.string.screenshot_failed_title))
+ .setContentTitle(r.getString(R.string.screenshot_failed_title))
+ .setContentText(errorMsg)
+ .setSmallIcon(R.drawable.stat_notify_image_error)
+ .setWhen(System.currentTimeMillis())
+ .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
+ .setCategory(Notification.CATEGORY_ERROR)
+ .setAutoCancel(true)
+ .setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
new file mode 100644
index 000000000000..083f9712d705
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import android.app.ActivityTaskManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Picture;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.DeviceConfig;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.util.NotificationChannels;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * An AsyncTask that saves an image to the media store in the background.
+ */
+class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
+ private static final String TAG = "SaveImageInBackgroundTask";
+
+ private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
+ private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
+
+ private final GlobalScreenshot.SaveImageInBackgroundData mParams;
+ private final NotificationManager mNotificationManager;
+ private final Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
+ private final String mImageFileName;
+ private final long mImageTime;
+ private final Notification.BigPictureStyle mNotificationStyle;
+ private final int mImageWidth;
+ private final int mImageHeight;
+ private final Handler mHandler;
+ private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+
+ SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data,
+ NotificationManager nManager) {
+ Resources r = context.getResources();
+
+ // Prepare all the output metadata
+ mParams = data;
+ mImageTime = System.currentTimeMillis();
+ String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
+ mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
+
+ // Initialize screenshot notification smart actions provider.
+ mHandler = new Handler();
+ mSmartActionsProvider =
+ SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider();
+
+ // Create the large notification icon
+ mImageWidth = data.image.getWidth();
+ mImageHeight = data.image.getHeight();
+ int iconSize = data.iconSize;
+ int previewWidth = data.previewWidth;
+ int previewHeight = data.previewheight;
+
+ Paint paint = new Paint();
+ ColorMatrix desat = new ColorMatrix();
+ desat.setSaturation(0.25f);
+ paint.setColorFilter(new ColorMatrixColorFilter(desat));
+ Matrix matrix = new Matrix();
+ int overlayColor = 0x40FFFFFF;
+
+ matrix.setTranslate((previewWidth - mImageWidth) / 2,
+ (previewHeight - mImageHeight) / 2);
+ Bitmap picture = generateAdjustedHwBitmap(data.image, previewWidth, previewHeight,
+ matrix, paint, overlayColor);
+
+ // Note, we can't use the preview for the small icon, since it is non-square
+ float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
+ matrix.setScale(scale, scale);
+ matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
+ (iconSize - (scale * mImageHeight)) / 2);
+ Bitmap icon = generateAdjustedHwBitmap(data.image, iconSize, iconSize, matrix, paint,
+ overlayColor);
+
+ mNotificationManager = nManager;
+ final long now = System.currentTimeMillis();
+
+ // Setup the notification
+ mNotificationStyle = new Notification.BigPictureStyle()
+ .bigPicture(picture.createAshmemBitmap());
+
+ // The public notification will show similar info but with the actual screenshot omitted
+ mPublicNotificationBuilder =
+ new Notification.Builder(context, NotificationChannels.SCREENSHOTS_HEADSUP)
+ .setContentTitle(r.getString(R.string.screenshot_saving_title))
+ .setSmallIcon(R.drawable.stat_notify_image)
+ .setCategory(Notification.CATEGORY_PROGRESS)
+ .setWhen(now)
+ .setShowWhen(true)
+ .setColor(r.getColor(
+ com.android.internal.R.color.system_notification_accent_color));
+ SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder, true);
+
+ mNotificationBuilder = new Notification.Builder(context,
+ NotificationChannels.SCREENSHOTS_HEADSUP)
+ .setContentTitle(r.getString(R.string.screenshot_saving_title))
+ .setSmallIcon(R.drawable.stat_notify_image)
+ .setWhen(now)
+ .setShowWhen(true)
+ .setColor(r.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setStyle(mNotificationStyle)
+ .setPublicVersion(mPublicNotificationBuilder.build());
+ mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
+ SystemUI.overrideNotificationAppName(context, mNotificationBuilder, true);
+
+ mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
+ mNotificationBuilder.build());
+
+ /**
+ * NOTE: The following code prepares the notification builder for updating the
+ * notification after the screenshot has been written to disk.
+ */
+
+ // On the tablet, the large icon makes the notification appear as if it is clickable
+ // (and on small devices, the large icon is not shown) so defer showing the large icon
+ // until we compose the final post-save notification below.
+ mNotificationBuilder.setLargeIcon(icon.createAshmemBitmap());
+ // But we still don't set it for the expanded view, allowing the smallIcon to show here.
+ mNotificationStyle.bigLargeIcon((Bitmap) null);
+ }
+
+ private int getUserHandleOfForegroundApplication(Context context) {
+ // This logic matches
+ // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile
+ try {
+ return ActivityTaskManager.getService().getLastResumedActivityUserId();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e);
+ return context.getUserId();
+ }
+ }
+
+ private boolean isManagedProfile(Context context) {
+ UserManager manager = UserManager.get(context);
+ UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context));
+ return info.isManagedProfile();
+ }
+
+ /**
+ * Generates a new hardware bitmap with specified values, copying the content from the
+ * passed in bitmap.
+ */
+ private Bitmap generateAdjustedHwBitmap(Bitmap bitmap, int width, int height, Matrix matrix,
+ Paint paint, int color) {
+ Picture picture = new Picture();
+ Canvas canvas = picture.beginRecording(width, height);
+ canvas.drawColor(color);
+ canvas.drawBitmap(bitmap, matrix, paint);
+ picture.endRecording();
+ return Bitmap.createBitmap(picture);
+ }
+
+ @Override
+ protected Void doInBackground(Void... paramsUnused) {
+ if (isCancelled()) {
+ return null;
+ }
+
+ // By default, AsyncTask sets the worker thread to have background thread priority,
+ // so bump it back up so that we save a little quicker.
+ Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+
+ Context context = mParams.context;
+ Bitmap image = mParams.image;
+ boolean smartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
+ CompletableFuture<List<Notification.Action>>
+ smartActionsFuture = GlobalScreenshot.getSmartActionsFuture(
+ context, image, mSmartActionsProvider, mHandler, smartActionsEnabled,
+ isManagedProfile(context));
+
+ Resources r = context.getResources();
+
+ try {
+ // Save the screenshot to the MediaStore
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
+ params.setRelativePath(Environment.DIRECTORY_PICTURES + File.separator
+ + Environment.DIRECTORY_SCREENSHOTS);
+
+ final Uri uri = MediaStore.createPending(context, params);
+ final MediaStore.PendingSession session = MediaStore.openPending(context, uri);
+ try {
+ // First, write the actual data for our screenshot
+ try (OutputStream out = session.openOutputStream()) {
+ if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
+ throw new IOException("Failed to compress");
+ }
+ }
+
+ // Next, write metadata to help index the screenshot
+ try (ParcelFileDescriptor pfd = session.open()) {
+ final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
+
+ exif.setAttribute(ExifInterface.TAG_SOFTWARE,
+ "Android " + Build.DISPLAY);
+
+ exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH,
+ Integer.toString(image.getWidth()));
+ exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH,
+ Integer.toString(image.getHeight()));
+
+ final ZonedDateTime time = ZonedDateTime.ofInstant(
+ Instant.ofEpochMilli(mImageTime), ZoneId.systemDefault());
+ exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(time));
+ exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("SSS").format(time));
+
+ if (Objects.equals(time.getOffset(), ZoneOffset.UTC)) {
+ exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00");
+ } else {
+ exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("XXX").format(time));
+ }
+
+ exif.saveAttributes();
+ }
+ session.publish();
+ } catch (Exception e) {
+ session.abandon();
+ throw e;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+
+ // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
+ // order to do some common work like dismissing the keyguard and sending
+ // closeSystemWindows
+
+ // Create a share intent, this will always go through the chooser activity first
+ // which should not trigger auto-enter PiP
+ String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
+ String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
+ Intent sharingIntent = new Intent(Intent.ACTION_SEND);
+ sharingIntent.setType("image/png");
+ sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ // Include URI in ClipData also, so that grantPermission picks it up.
+ // We don't use setData here because some apps interpret this as "to:".
+ ClipData clipdata = new ClipData(new ClipDescription("content",
+ new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
+ new ClipData.Item(uri));
+ sharingIntent.setClipData(clipdata);
+ sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+ sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ // Make sure pending intents for the system user are still unique across users
+ // by setting the (otherwise unused) request code to the current user id.
+ int requestCode = context.getUserId();
+
+ PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
+ new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
+ chooserAction.getIntentSender())
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ // Create a share action for the notification
+ PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
+ new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, sharingChooserIntent)
+ .putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
+ .setAction(Intent.ACTION_SEND),
+ PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+ Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_share,
+ r.getString(com.android.internal.R.string.share), shareAction);
+ mNotificationBuilder.addAction(shareActionBuilder.build());
+
+ // Create an edit intent, if a specific package is provided as the editor, then
+ // launch that directly
+ String editorPackage = context.getString(R.string.config_screenshotEditor);
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ editIntent.setType("image/png");
+ editIntent.setData(uri);
+ editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ // Create a edit action
+ PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
+ new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, editIntent)
+ .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
+ editIntent.getComponent() != null)
+ .setAction(Intent.ACTION_EDIT),
+ PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+ Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_edit,
+ r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+ mNotificationBuilder.addAction(editActionBuilder.build());
+ if (editAction != null && mParams.onEditReady != null) {
+ mParams.onEditReady.apply(editAction);
+ }
+
+ // Create a delete action for the notification
+ PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
+ new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+ .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_delete,
+ r.getString(com.android.internal.R.string.delete), deleteAction);
+ mNotificationBuilder.addAction(deleteActionBuilder.build());
+
+ mParams.imageUri = uri;
+ mParams.image = null;
+ mParams.errorMsgResId = 0;
+
+ if (smartActionsEnabled) {
+ int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags
+ .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
+ 1000);
+ List<Notification.Action> smartActions = GlobalScreenshot.getSmartActions(
+ smartActionsFuture,
+ timeoutMs);
+ for (Notification.Action action : smartActions) {
+ mNotificationBuilder.addAction(action);
+ }
+ }
+ } catch (Exception e) {
+ // IOException/UnsupportedOperationException may be thrown if external storage is
+ // not mounted
+ Slog.e(TAG, "unable to save screenshot", e);
+ mParams.clearImage();
+ mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
+ }
+
+ // Recycle the bitmap data
+ if (image != null) {
+ image.recycle();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void params) {
+ if (mParams.errorMsgResId != 0) {
+ // Show a message that we've failed to save the image to disk
+ GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
+ mParams.errorMsgResId);
+ } else {
+ if (mParams.onEditReady != null) {
+ // Cancel the "saving screenshot" notification
+ mNotificationManager.cancel(
+ SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+ } else {
+ // Show the final notification to indicate screenshot saved
+ Context context = mParams.context;
+ Resources r = context.getResources();
+
+ // Create the intent to show the screenshot in gallery
+ Intent launchIntent = new Intent(Intent.ACTION_VIEW);
+ launchIntent.setDataAndType(mParams.imageUri, "image/png");
+ launchIntent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ final long now = System.currentTimeMillis();
+
+ // Update the text and the icon for the existing notification
+ mPublicNotificationBuilder
+ .setContentTitle(r.getString(R.string.screenshot_saved_title))
+ .setContentText(r.getString(R.string.screenshot_saved_text))
+ .setContentIntent(
+ PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
+ .setWhen(now)
+ .setAutoCancel(true)
+ .setColor(context.getColor(
+ com.android.internal.R.color.system_notification_accent_color));
+ mNotificationBuilder
+ .setContentTitle(r.getString(R.string.screenshot_saved_title))
+ .setContentText(r.getString(R.string.screenshot_saved_text))
+ .setContentIntent(PendingIntent.getActivity(mParams.context, 0,
+ launchIntent, 0))
+ .setWhen(now)
+ .setAutoCancel(true)
+ .setColor(context.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setPublicVersion(mPublicNotificationBuilder.build())
+ .setFlag(Notification.FLAG_NO_CLEAR, false);
+
+ mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
+ mNotificationBuilder.build());
+ }
+ }
+ mParams.finisher.run();
+ mParams.clearContext();
+ }
+
+ @Override
+ protected void onCancelled(Void params) {
+ // If we are cancelled while the task is running in the background, we may get null
+ // params. The finisher is expected to always be called back, so just use the baked-in
+ // params from the ctor in any case.
+ mParams.finisher.run();
+ mParams.clearImage();
+ mParams.clearContext();
+
+ // Cancel the posted notification
+ mNotificationManager.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 621f101cd8af..88b6fdda3dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -70,53 +70,55 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
private static final int OP_SET_ICON = 1;
private static final int OP_REMOVE_ICON = 2;
- private static final int MSG_ICON = 1 << MSG_SHIFT;
- private static final int MSG_DISABLE = 2 << MSG_SHIFT;
- private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT;
- private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT;
- private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT;
- private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT;
- private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT;
- private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT;
- private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT;
- private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT;
- private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 11 << MSG_SHIFT;
- private static final int MSG_SET_WINDOW_STATE = 12 << MSG_SHIFT;
- private static final int MSG_SHOW_RECENT_APPS = 13 << MSG_SHIFT;
- private static final int MSG_HIDE_RECENT_APPS = 14 << MSG_SHIFT;
- private static final int MSG_SHOW_SCREEN_PIN_REQUEST = 18 << MSG_SHIFT;
- private static final int MSG_APP_TRANSITION_PENDING = 19 << MSG_SHIFT;
- private static final int MSG_APP_TRANSITION_CANCELLED = 20 << MSG_SHIFT;
- private static final int MSG_APP_TRANSITION_STARTING = 21 << MSG_SHIFT;
- private static final int MSG_ASSIST_DISCLOSURE = 22 << MSG_SHIFT;
- private static final int MSG_START_ASSIST = 23 << MSG_SHIFT;
- private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT;
- private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT;
- private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 26 << MSG_SHIFT;
- private static final int MSG_ADD_QS_TILE = 27 << MSG_SHIFT;
- private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT;
- private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT;
- private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT;
- private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT;
- private static final int MSG_DISMISS_KEYBOARD_SHORTCUTS = 32 << MSG_SHIFT;
- private static final int MSG_HANDLE_SYSTEM_KEY = 33 << MSG_SHIFT;
- private static final int MSG_SHOW_GLOBAL_ACTIONS = 34 << MSG_SHIFT;
- private static final int MSG_TOGGLE_PANEL = 35 << MSG_SHIFT;
- private static final int MSG_SHOW_SHUTDOWN_UI = 36 << MSG_SHIFT;
- private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR = 37 << MSG_SHIFT;
- private static final int MSG_ROTATION_PROPOSAL = 38 << MSG_SHIFT;
- private static final int MSG_BIOMETRIC_SHOW = 39 << MSG_SHIFT;
- private static final int MSG_BIOMETRIC_AUTHENTICATED = 40 << MSG_SHIFT;
- private static final int MSG_BIOMETRIC_HELP = 41 << MSG_SHIFT;
- private static final int MSG_BIOMETRIC_ERROR = 42 << MSG_SHIFT;
- private static final int MSG_BIOMETRIC_HIDE = 43 << MSG_SHIFT;
- private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
- private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
- private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
- private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT;
- private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT;
- private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT;
- private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT;
+ private static final int MSG_ICON = 1 << MSG_SHIFT;
+ private static final int MSG_DISABLE = 2 << MSG_SHIFT;
+ private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT;
+ private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT;
+ private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT;
+ private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT;
+ private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT;
+ private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT;
+ private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT;
+ private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT;
+ private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 11 << MSG_SHIFT;
+ private static final int MSG_SET_WINDOW_STATE = 12 << MSG_SHIFT;
+ private static final int MSG_SHOW_RECENT_APPS = 13 << MSG_SHIFT;
+ private static final int MSG_HIDE_RECENT_APPS = 14 << MSG_SHIFT;
+ private static final int MSG_SHOW_SCREEN_PIN_REQUEST = 18 << MSG_SHIFT;
+ private static final int MSG_APP_TRANSITION_PENDING = 19 << MSG_SHIFT;
+ private static final int MSG_APP_TRANSITION_CANCELLED = 20 << MSG_SHIFT;
+ private static final int MSG_APP_TRANSITION_STARTING = 21 << MSG_SHIFT;
+ private static final int MSG_ASSIST_DISCLOSURE = 22 << MSG_SHIFT;
+ private static final int MSG_START_ASSIST = 23 << MSG_SHIFT;
+ private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT;
+ private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT;
+ private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 26 << MSG_SHIFT;
+ private static final int MSG_ADD_QS_TILE = 27 << MSG_SHIFT;
+ private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT;
+ private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT;
+ private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT;
+ private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT;
+ private static final int MSG_DISMISS_KEYBOARD_SHORTCUTS = 32 << MSG_SHIFT;
+ private static final int MSG_HANDLE_SYSTEM_KEY = 33 << MSG_SHIFT;
+ private static final int MSG_SHOW_GLOBAL_ACTIONS = 34 << MSG_SHIFT;
+ private static final int MSG_TOGGLE_PANEL = 35 << MSG_SHIFT;
+ private static final int MSG_SHOW_SHUTDOWN_UI = 36 << MSG_SHIFT;
+ private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR = 37 << MSG_SHIFT;
+ private static final int MSG_ROTATION_PROPOSAL = 38 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_SHOW = 39 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_AUTHENTICATED = 40 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_HELP = 41 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_ERROR = 42 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_HIDE = 43 << MSG_SHIFT;
+ private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
+ private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
+ private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
+ private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT;
+ private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT;
+ private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT;
+ private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT;
+ private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT;
+ private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 52 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -294,6 +296,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
*/
default void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
}
+
+ /**
+ * Called to notify System UI that a warning about the device going to sleep
+ * due to prolonged user inactivity should be shown.
+ */
+ default void showInattentiveSleepWarning() { }
+
+ /**
+ * Called to notify System UI that the warning about the device going to sleep
+ * due to prolonged user inactivity should be dismissed.
+ */
+ default void dismissInattentiveSleepWarning() { }
}
public CommandQueue(Context context) {
@@ -793,6 +807,22 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
}
+ @Override
+ public void showInattentiveSleepWarning() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SHOW_INATTENTIVE_SLEEP_WARNING)
+ .sendToTarget();
+ }
+ }
+
+ @Override
+ public void dismissInattentiveSleepWarning() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_DISMISS_INATTENTIVE_SLEEP_WARNING)
+ .sendToTarget();
+ }
+ }
+
private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher, boolean isMultiClientImeEnabled) {
if (displayId == INVALID_DISPLAY) return;
@@ -1138,6 +1168,16 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
args.recycle();
break;
}
+ case MSG_SHOW_INATTENTIVE_SLEEP_WARNING:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showInattentiveSleepWarning();
+ }
+ break;
+ case MSG_DISMISS_INATTENTIVE_SLEEP_WARNING:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).dismissInattentiveSleepWarning();
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
index b21c65ea85ea..18574f0f891e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
@@ -71,7 +71,7 @@ public class MediaTransferManager {
ViewParent parent = view.getParent();
StatusBarNotification statusBarNotification =
- getRowForParent(parent).getStatusBarNotification();
+ getRowForParent(parent).getEntry().getSbn();
final Intent intent = new Intent()
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index ef40d9834749..0bfcdbdb1118 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -44,7 +44,7 @@ public class NotificationHeaderUtil {
private static final DataExtractor sIconExtractor = new DataExtractor() {
@Override
public Object extractData(ExpandableNotificationRow row) {
- return row.getStatusBarNotification().getNotification();
+ return row.getEntry().getSbn().getNotification();
}
};
private static final IconComparator sIconVisibilityComparator = new IconComparator() {
@@ -207,7 +207,7 @@ public class NotificationHeaderUtil {
}
// in case no view is visible we make sure the time is visible
int timeVisibility = !hasVisibleText
- || mRow.getStatusBarNotification().getNotification().showsTime()
+ || mRow.getEntry().getSbn().getNotification().showsTime()
? View.VISIBLE : View.GONE;
time.setVisibility(timeVisibility);
View left = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index e10d27b241cc..c556bc0b39d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -216,7 +216,7 @@ public class NotificationRemoteInputManager implements Dumpable {
private StatusBarNotification getNotificationForParent(ViewParent parent) {
while (parent != null) {
if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent).getStatusBarNotification();
+ return ((ExpandableNotificationRow) parent).getEntry().getSbn();
}
parent = parent.getParent();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ef733a967840..4204f684a632 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -204,7 +204,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
}
for (ExpandableNotificationRow viewToRemove : viewsToRemove) {
- if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getStatusBarNotification())) {
+ if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getEntry().getSbn())) {
// we are only transferring this notification to its parent, don't generate an
// animation
mListContainer.setChildTransferInProgress(true);
@@ -339,7 +339,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
for (ExpandableNotificationRow remove : toRemove) {
parent.removeChildNotification(remove);
if (mEntryManager.getActiveNotificationUnfiltered(
- remove.getStatusBarNotification().getKey()) == null) {
+ remove.getEntry().getSbn().getKey()) == null) {
// We only want to add an animation if the view is completely removed
// otherwise it's just a transfer
mListContainer.notifyGroupChildRemoved(remove,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index fd2f72062be7..b5c664116944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -55,7 +55,7 @@ public final class NotificationClicker implements View.OnClickListener {
mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK");
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- final StatusBarNotification sbn = row.getStatusBarNotification();
+ final StatusBarNotification sbn = row.getEntry().getSbn();
if (sbn == null) {
Log.e(TAG, "NotificationClicker called on an unclickable notification,");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java
index 17fef6850f97..cefb506b5233 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java
@@ -22,7 +22,7 @@ import java.util.Collection;
* Interface for the class responsible for converting a NotifCollection into the final sorted,
* filtered, and grouped list of currently visible notifications.
*/
-public interface NotifListBuilder {
+public interface CollectionReadyForBuildListener {
/**
* Called after the NotifCollection has received an update from NotificationManager but before
* it dispatches any change events to its listeners. This is to inform the list builder that
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
new file mode 100644
index 000000000000..f9f3266f1afa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Represents a set of grouped notifications. The final notification list is usually a mix of
+ * GroupEntries and NotificationEntries.
+ */
+public class GroupEntry extends ListEntry {
+ @Nullable private NotificationEntry mSummary;
+ private final List<NotificationEntry> mChildren = new ArrayList<>();
+
+ private final List<NotificationEntry> mUnmodifiableChildren =
+ Collections.unmodifiableList(mChildren);
+
+ GroupEntry(String key) {
+ super(key);
+ }
+
+ @Override
+ public NotificationEntry getRepresentativeEntry() {
+ return mSummary;
+ }
+
+ @Nullable
+ public NotificationEntry getSummary() {
+ return mSummary;
+ }
+
+ public List<NotificationEntry> getChildren() {
+ return mUnmodifiableChildren;
+ }
+
+ void setSummary(@Nullable NotificationEntry summary) {
+ mSummary = summary;
+ }
+
+ void clearChildren() {
+ mChildren.clear();
+ }
+
+ void addChild(NotificationEntry child) {
+ mChildren.add(child);
+ }
+
+ void sortChildren(Comparator<? super NotificationEntry> c) {
+ mChildren.sort(c);
+ }
+
+ List<NotificationEntry> getRawChildren() {
+ return mChildren;
+ }
+
+ public static final GroupEntry ROOT_ENTRY = new GroupEntry("<root>");
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
new file mode 100644
index 000000000000..e1268f6d60ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+
+import java.util.List;
+
+
+/**
+ * Utility class for dumping the results of a {@link NotifListBuilder} to a debug string.
+ */
+public class ListDumper {
+
+ /** See class description */
+ public static String dumpList(List<ListEntry> entries) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < entries.size(); i++) {
+ ListEntry entry = entries.get(i);
+ dumpEntry(entry, Integer.toString(i), "", sb);
+ if (entry instanceof GroupEntry) {
+ GroupEntry ge = (GroupEntry) entry;
+ for (int j = 0; j < ge.getChildren().size(); j++) {
+ dumpEntry(
+ ge.getChildren().get(j),
+ Integer.toString(j),
+ INDENT,
+ sb);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ private static void dumpEntry(
+ ListEntry entry, String index, String indent, StringBuilder sb) {
+ sb.append(indent)
+ .append("[").append(index).append("] ")
+ .append(entry.getKey())
+ .append(" (parent=")
+ .append(entry.getParent() != null ? entry.getParent().getKey() : null)
+ .append(")\n");
+ }
+
+ private static final String INDENT = " ";
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
new file mode 100644
index 000000000000..dc68c4bdba78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import android.annotation.Nullable;
+
+/**
+ * Abstract superclass for top-level entries, i.e. things that can appear in the final notification
+ * list shown to users. In practice, this means either GroupEntries or NotificationEntries.
+ */
+public abstract class ListEntry {
+ private final String mKey;
+
+ @Nullable private GroupEntry mParent;
+ @Nullable private GroupEntry mPreviousParent;
+ private int mSection;
+ int mFirstAddedIteration = -1;
+
+ ListEntry(String key) {
+ mKey = key;
+ }
+
+ public String getKey() {
+ return mKey;
+ }
+
+ /**
+ * Should return the "representative entry" for this ListEntry. For NotificationEntries, its
+ * the entry itself. For groups, it should be the summary. This method exists to interface with
+ * legacy code that expects groups to also be NotificationEntries.
+ */
+ public abstract NotificationEntry getRepresentativeEntry();
+
+ @Nullable public GroupEntry getParent() {
+ return mParent;
+ }
+
+ void setParent(@Nullable GroupEntry parent) {
+ mParent = parent;
+ }
+
+ @Nullable public GroupEntry getPreviousParent() {
+ return mPreviousParent;
+ }
+
+ void setPreviousParent(@Nullable GroupEntry previousParent) {
+ mPreviousParent = previousParent;
+ }
+
+ /** The section this notification was assigned to (0 to N-1, where N is number of sections). */
+ public int getSection() {
+ return mSection;
+ }
+
+ void setSection(int section) {
+ mSection = section;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index b5513529d7ba..6f085c0ce7c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -95,7 +95,7 @@ public class NotifCollection {
private final Collection<NotificationEntry> mReadOnlyNotificationSet =
Collections.unmodifiableCollection(mNotificationSet.values());
- @Nullable private NotifListBuilder mListBuilder;
+ @Nullable private CollectionReadyForBuildListener mBuildListener;
private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
@@ -123,9 +123,9 @@ public class NotifCollection {
* Sets the class responsible for converting the collection into the list of currently-visible
* notifications.
*/
- public void setListBuilder(NotifListBuilder listBuilder) {
+ public void setBuildListener(CollectionReadyForBuildListener buildListener) {
Assert.isMainThread();
- mListBuilder = listBuilder;
+ mBuildListener = buildListener;
}
/**
@@ -282,8 +282,8 @@ public class NotifCollection {
}
private void rebuildList() {
- if (mListBuilder != null) {
- mListBuilder.onBuildList(mReadOnlyNotificationSet);
+ if (mBuildListener != null) {
+ mBuildListener.onBuildList(mReadOnlyNotificationSet);
}
}
@@ -339,8 +339,8 @@ public class NotifCollection {
private void dispatchOnEntryAdded(NotificationEntry entry) {
mAmDispatchingToOtherCode = true;
- if (mListBuilder != null) {
- mListBuilder.onBeginDispatchToListeners();
+ if (mBuildListener != null) {
+ mBuildListener.onBeginDispatchToListeners();
}
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onEntryAdded(entry);
@@ -350,8 +350,8 @@ public class NotifCollection {
private void dispatchOnEntryUpdated(NotificationEntry entry) {
mAmDispatchingToOtherCode = true;
- if (mListBuilder != null) {
- mListBuilder.onBeginDispatchToListeners();
+ if (mBuildListener != null) {
+ mBuildListener.onBeginDispatchToListeners();
}
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onEntryUpdated(entry);
@@ -364,8 +364,8 @@ public class NotifCollection {
@CancellationReason int reason,
boolean removedByUser) {
mAmDispatchingToOtherCode = true;
- if (mListBuilder != null) {
- mListBuilder.onBeginDispatchToListeners();
+ if (mBuildListener != null) {
+ mBuildListener.onBeginDispatchToListeners();
}
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onEntryRemoved(entry, reason, removedByUser);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
new file mode 100644
index 000000000000..21a4b4f895e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
+import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpList;
+import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_BUILD_PENDING;
+import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_BUILD_STARTED;
+import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FILTERING;
+import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZING;
+import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_IDLE;
+import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
+import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
+
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.time.SystemClock;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * The implementation of {@link NotifListBuilder}.
+ */
+@MainThread
+@Singleton
+public class NotifListBuilderImpl implements NotifListBuilder {
+
+ private final SystemClock mSystemClock;
+
+ private final List<ListEntry> mNotifList = new ArrayList<>();
+
+ private final PipelineState mPipelineState = new PipelineState();
+ private final Map<String, GroupEntry> mGroups = new ArrayMap<>();
+ private Collection<NotificationEntry> mAllEntries = Collections.emptyList();
+ private final List<ListEntry> mNewEntries = new ArrayList<>();
+ private int mIterationCount = 0;
+
+ private final List<NotifFilter> mNotifFilters = new ArrayList<>();
+ private final List<NotifPromoter> mNotifPromoters = new ArrayList<>();
+ private final List<NotifComparator> mNotifComparators = new ArrayList<>();
+ private SectionsProvider mSectionsProvider = new DefaultSectionsProvider();
+
+ private final List<OnBeforeTransformGroupsListener> mOnBeforeTransformGroupsListeners =
+ new ArrayList<>();
+ private final List<OnBeforeSortListener> mOnBeforeSortListeners =
+ new ArrayList<>();
+ private final List<OnBeforeRenderListListener> mOnBeforeRenderListListeners =
+ new ArrayList<>();
+ @Nullable private OnRenderListListener mOnRenderListListener;
+
+ private final List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
+
+ @Inject
+ public NotifListBuilderImpl(SystemClock systemClock) {
+ Assert.isMainThread();
+ mSystemClock = systemClock;
+ }
+
+ /**
+ * Attach the list builder to the NotifCollection. After this is called, it will start building
+ * the notif list in response to changes to the colletion.
+ */
+ public void attach(NotifCollection collection) {
+ Assert.isMainThread();
+ collection.setBuildListener(mReadyForBuildListener);
+ }
+
+ /**
+ * Registers the listener that's responsible for rendering the notif list to the screen. Called
+ * At the very end of pipeline execution, after all other listeners and pluggables have fired.
+ */
+ public void setOnRenderListListener(OnRenderListListener onRenderListListener) {
+ Assert.isMainThread();
+
+ mPipelineState.requireState(STATE_IDLE);
+ mOnRenderListListener = onRenderListListener;
+ }
+
+ @Override
+ public void addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener listener) {
+ Assert.isMainThread();
+
+ mPipelineState.requireState(STATE_IDLE);
+ mOnBeforeTransformGroupsListeners.add(listener);
+ }
+
+ @Override
+ public void addOnBeforeSortListener(OnBeforeSortListener listener) {
+ Assert.isMainThread();
+
+ mPipelineState.requireState(STATE_IDLE);
+ mOnBeforeSortListeners.add(listener);
+ }
+
+ @Override
+ public void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) {
+ Assert.isMainThread();
+
+ mPipelineState.requireState(STATE_IDLE);
+ mOnBeforeRenderListListeners.add(listener);
+ }
+
+ @Override
+ public void addFilter(NotifFilter filter) {
+ Assert.isMainThread();
+ mPipelineState.requireState(STATE_IDLE);
+
+ mNotifFilters.add(filter);
+ filter.setInvalidationListener(this::onFilterInvalidated);
+ }
+
+ @Override
+ public void addPromoter(NotifPromoter promoter) {
+ Assert.isMainThread();
+ mPipelineState.requireState(STATE_IDLE);
+
+ mNotifPromoters.add(promoter);
+ promoter.setInvalidationListener(this::onPromoterInvalidated);
+ }
+
+ @Override
+ public void setSectionsProvider(SectionsProvider provider) {
+ Assert.isMainThread();
+ mPipelineState.requireState(STATE_IDLE);
+
+ mSectionsProvider = provider;
+ provider.setInvalidationListener(this::onSectionsProviderInvalidated);
+ }
+
+ @Override
+ public void setComparators(List<NotifComparator> comparators) {
+ Assert.isMainThread();
+ mPipelineState.requireState(STATE_IDLE);
+
+ mNotifComparators.clear();
+ for (NotifComparator comparator : comparators) {
+ mNotifComparators.add(comparator);
+ comparator.setInvalidationListener(this::onNotifComparatorInvalidated);
+ }
+ }
+
+ @Override
+ public List<ListEntry> getActiveNotifs() {
+ Assert.isMainThread();
+ return mReadOnlyNotifList;
+ }
+
+ private final CollectionReadyForBuildListener mReadyForBuildListener =
+ new CollectionReadyForBuildListener() {
+ @Override
+ public void onBeginDispatchToListeners() {
+ Assert.isMainThread();
+ mPipelineState.incrementTo(STATE_BUILD_PENDING);
+ }
+
+ @Override
+ public void onBuildList(Collection<NotificationEntry> entries) {
+ Assert.isMainThread();
+ mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
+
+ Log.i(TAG, "Build request received from NotifCollection");
+ mAllEntries = entries;
+ buildList();
+ }
+ };
+
+ private void onFilterInvalidated(NotifFilter filter) {
+ Assert.isMainThread();
+
+ // TODO: Convert these log statements (here and elsewhere) into timeline logging
+ Log.i(TAG, String.format(
+ "Filter \"%s\" invalidated; pipeline state is %d",
+ filter.getName(),
+ mPipelineState.getState()));
+
+ rebuildListIfBefore(STATE_FILTERING);
+ }
+
+ private void onPromoterInvalidated(NotifPromoter filter) {
+ Assert.isMainThread();
+
+ Log.i(TAG, String.format(
+ "NotifPromoter \"%s\" invalidated; pipeline state is %d",
+ filter.getName(),
+ mPipelineState.getState()));
+
+ rebuildListIfBefore(STATE_TRANSFORMING);
+ }
+
+ private void onSectionsProviderInvalidated(SectionsProvider provider) {
+ Assert.isMainThread();
+
+ Log.i(TAG, String.format(
+ "Sections provider \"%s\" invalidated; pipeline state is %d",
+ provider.getName(),
+ mPipelineState.getState()));
+
+ rebuildListIfBefore(STATE_SORTING);
+ }
+
+ private void onNotifComparatorInvalidated(NotifComparator comparator) {
+ Assert.isMainThread();
+
+ Log.i(TAG, String.format(
+ "Comparator \"%s\" invalidated; pipeline state is %d",
+ comparator.getName(),
+ mPipelineState.getState()));
+
+ rebuildListIfBefore(STATE_SORTING);
+ }
+
+ /**
+ * The core algorithm of the pipeline. See the top comment in {@link NotifListBuilder} for
+ * details on our contracts with other code.
+ *
+ * Once the build starts we are very careful to protect against reentrant code. Anything that
+ * tries to invalidate itself after the pipeline has passed it by will return in an exception.
+ * In general, we should be extremely sensitive to client code doing things in the wrong order;
+ * if we detect that behavior, we should crash instantly.
+ */
+ private void buildList() {
+ Log.i(TAG, "Starting notif list build #" + mIterationCount + "...");
+
+ mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
+ mPipelineState.setState(STATE_BUILD_STARTED);
+
+ // Step 1: Filtering and initial grouping
+ // Filter out any notifs that shouldn't be shown right now and cluster any that are part of
+ // a group
+ mPipelineState.incrementTo(STATE_FILTERING);
+ mNotifList.clear();
+ mNewEntries.clear();
+ filterAndGroup(mAllEntries, mNotifList, mNewEntries);
+ pruneIncompleteGroups(mNotifList, mNewEntries);
+
+ // Step 2: Group transforming
+ // Move some notifs out of their groups and up to top-level (mostly used for heads-upping)
+ dispatchOnBeforeTransformGroups(mReadOnlyNotifList, mNewEntries);
+ mPipelineState.incrementTo(STATE_TRANSFORMING);
+ promoteNotifs(mNotifList);
+ pruneIncompleteGroups(mNotifList, mNewEntries);
+
+ // Step 3: Sort
+ // Assign each top-level entry a section, then sort the list by section and then within
+ // section by our list of custom comparators
+ dispatchOnBeforeSort(mReadOnlyNotifList);
+ mPipelineState.incrementTo(STATE_SORTING);
+ sortList();
+
+ // Step 4: Lock in our group structure and log anything that's changed since the last run
+ mPipelineState.incrementTo(STATE_FINALIZING);
+ logParentingChanges();
+ freeEmptyGroups();
+
+ // Step 5: Dispatch the new list, first to any listeners and then to the view layer
+ Log.i(TAG, "List finalized, is:\n" + dumpList(mNotifList));
+ Log.i(TAG, "Dispatching final list to listeners...");
+ dispatchOnBeforeRenderList(mReadOnlyNotifList);
+ if (mOnRenderListListener != null) {
+ mOnRenderListListener.onRenderList(mReadOnlyNotifList);
+ }
+
+ // Step 6: We're done!
+ Log.i(TAG, "Notif list build #" + mIterationCount + " completed");
+ mPipelineState.setState(STATE_IDLE);
+ mIterationCount++;
+ }
+
+ private void filterAndGroup(
+ Collection<NotificationEntry> entries,
+ List<ListEntry> out,
+ List<ListEntry> newlyVisibleEntries) {
+
+ long now = mSystemClock.uptimeMillis();
+
+ for (GroupEntry group : mGroups.values()) {
+ group.setPreviousParent(group.getParent());
+ group.setParent(null);
+ group.clearChildren();
+ group.setSummary(null);
+ }
+
+ for (NotificationEntry entry : entries) {
+ entry.setPreviousParent(entry.getParent());
+ entry.setParent(null);
+
+ // See if we should filter out this notification
+ boolean shouldFilterOut = applyFilters(entry, now);
+ if (shouldFilterOut) {
+ continue;
+ }
+
+ if (entry.mFirstAddedIteration == -1) {
+ entry.mFirstAddedIteration = mIterationCount;
+ newlyVisibleEntries.add(entry);
+ }
+
+ // Otherwise, group it
+ if (entry.getSbn().isGroup()) {
+ final String topLevelKey = entry.getSbn().getGroupKey();
+
+ GroupEntry group = mGroups.get(topLevelKey);
+ if (group == null) {
+ group = new GroupEntry(topLevelKey);
+ group.mFirstAddedIteration = mIterationCount;
+ newlyVisibleEntries.add(group);
+ mGroups.put(topLevelKey, group);
+ }
+ if (group.getParent() == null) {
+ group.setParent(ROOT_ENTRY);
+ out.add(group);
+ }
+
+ entry.setParent(group);
+
+ if (entry.getSbn().getNotification().isGroupSummary()) {
+ final NotificationEntry existingSummary = group.getSummary();
+
+ if (existingSummary == null) {
+ group.setSummary(entry);
+ } else {
+ Log.w(TAG, String.format(
+ "Duplicate summary for group '%s': '%s' vs. '%s'",
+ group.getKey(),
+ existingSummary.getKey(),
+ entry.getKey()));
+
+ // Use whichever one was posted most recently
+ if (entry.getSbn().getPostTime()
+ > existingSummary.getSbn().getPostTime()) {
+ group.setSummary(entry);
+ annulAddition(existingSummary, out, newlyVisibleEntries);
+ } else {
+ annulAddition(entry, out, newlyVisibleEntries);
+ }
+ }
+ } else {
+ group.addChild(entry);
+ }
+
+ } else {
+
+ final String topLevelKey = entry.getKey();
+ if (mGroups.containsKey(topLevelKey)) {
+ Log.wtf(TAG, "Duplicate non-group top-level key: " + topLevelKey);
+ } else {
+ entry.setParent(ROOT_ENTRY);
+ out.add(entry);
+ }
+ }
+ }
+ }
+
+ private void promoteNotifs(List<ListEntry> list) {
+ for (int i = 0; i < list.size(); i++) {
+ final ListEntry tle = list.get(i);
+
+ if (tle instanceof GroupEntry) {
+ final GroupEntry group = (GroupEntry) tle;
+
+ group.getRawChildren().removeIf(child -> {
+ final boolean shouldPromote = applyTopLevelPromoters(child);
+
+ if (shouldPromote) {
+ child.setParent(ROOT_ENTRY);
+ list.add(child);
+ }
+
+ return shouldPromote;
+ });
+ }
+ }
+ }
+
+ private void pruneIncompleteGroups(
+ List<ListEntry> shadeList,
+ List<ListEntry> newlyVisibleEntries) {
+
+ for (int i = 0; i < shadeList.size(); i++) {
+ final ListEntry tle = shadeList.get(i);
+
+ if (tle instanceof GroupEntry) {
+ final GroupEntry group = (GroupEntry) tle;
+ final List<NotificationEntry> children = group.getRawChildren();
+
+ if (group.getSummary() != null && children.size() == 0) {
+ shadeList.remove(i);
+ i--;
+
+ NotificationEntry summary = group.getSummary();
+ summary.setParent(ROOT_ENTRY);
+ shadeList.add(summary);
+
+ group.setSummary(null);
+ annulAddition(group, shadeList, newlyVisibleEntries);
+
+ } else if (group.getSummary() == null
+ || children.size() < MIN_CHILDREN_FOR_GROUP) {
+ // If the group doesn't provide a summary or is too small, ignore it and add
+ // its children (if any) directly to top-level.
+
+ shadeList.remove(i);
+ i--;
+
+ if (group.getSummary() != null) {
+ final NotificationEntry summary = group.getSummary();
+ group.setSummary(null);
+ annulAddition(summary, shadeList, newlyVisibleEntries);
+ }
+
+ for (int j = 0; j < children.size(); j++) {
+ final NotificationEntry child = children.get(j);
+ child.setParent(ROOT_ENTRY);
+ shadeList.add(child);
+ }
+ children.clear();
+
+ annulAddition(group, shadeList, newlyVisibleEntries);
+ }
+ }
+ }
+ }
+
+ /**
+ * If a ListEntry was added to the shade list and then later removed (e.g. because it was a
+ * group that was broken up), this method will erase any bookkeeping traces of that addition
+ * and/or check that they were already erased.
+ *
+ * Before calling this method, the entry must already have been removed from its parent. If
+ * it's a group, its summary must be null and its children must be empty.
+ */
+ private void annulAddition(
+ ListEntry entry,
+ List<ListEntry> shadeList,
+ List<ListEntry> newlyVisibleEntries) {
+
+ // This function does very little, but if any of its assumptions are violated (and it has a
+ // lot of them), it will put the system into an inconsistent state. So we check all of them
+ // here.
+
+ if (entry.getParent() == null || entry.mFirstAddedIteration == -1) {
+ throw new IllegalStateException(
+ "Cannot nullify addition of " + entry.getKey() + ": no such addition. ("
+ + entry.getParent() + " " + entry.mFirstAddedIteration + ")");
+ }
+
+ if (entry.getParent() == ROOT_ENTRY) {
+ if (shadeList.contains(entry)) {
+ throw new IllegalStateException("Cannot nullify addition of " + entry.getKey()
+ + ": it's still in the shade list.");
+ }
+ }
+
+ if (entry instanceof GroupEntry) {
+ GroupEntry ge = (GroupEntry) entry;
+ if (ge.getSummary() != null) {
+ throw new IllegalStateException(
+ "Cannot nullify group " + ge.getKey() + ": summary is not null");
+ }
+ if (!ge.getChildren().isEmpty()) {
+ throw new IllegalStateException(
+ "Cannot nullify group " + ge.getKey() + ": still has children");
+ }
+ } else if (entry instanceof NotificationEntry) {
+ if (entry == entry.getParent().getSummary()
+ || entry.getParent().getChildren().contains(entry)) {
+ throw new IllegalStateException("Cannot nullify addition of child "
+ + entry.getKey() + ": it's still attached to its parent.");
+ }
+ }
+
+ entry.setParent(null);
+ if (entry.mFirstAddedIteration == mIterationCount) {
+ if (!newlyVisibleEntries.remove(entry)) {
+ throw new IllegalStateException("Cannot late-filter entry " + entry.getKey() + " "
+ + entry + " from " + newlyVisibleEntries + " "
+ + entry.mFirstAddedIteration);
+ }
+ entry.mFirstAddedIteration = -1;
+ }
+ }
+
+ private void sortList() {
+ // Assign sections to top-level elements and sort their children
+ for (ListEntry entry : mNotifList) {
+ entry.setSection(mSectionsProvider.getSection(entry));
+ if (entry instanceof GroupEntry) {
+ GroupEntry parent = (GroupEntry) entry;
+ for (NotificationEntry child : parent.getChildren()) {
+ child.setSection(0);
+ }
+ parent.sortChildren(sChildComparator);
+ }
+ }
+
+ // Finally, sort all top-level elements
+ mNotifList.sort(mTopLevelComparator);
+ }
+
+ private void freeEmptyGroups() {
+ mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty());
+ }
+
+ private void logParentingChanges() {
+ for (NotificationEntry entry : mAllEntries) {
+ if (entry.getParent() != entry.getPreviousParent()) {
+ Log.i(TAG, String.format(
+ "%s: parent changed from %s to %s",
+ entry.getKey(),
+ entry.getPreviousParent() == null
+ ? "null" : entry.getPreviousParent().getKey(),
+ entry.getParent() == null
+ ? "null" : entry.getParent().getKey()));
+ }
+ }
+ for (GroupEntry group : mGroups.values()) {
+ if (group.getParent() != group.getPreviousParent()) {
+ Log.i(TAG, String.format(
+ "%s: parent changed from %s to %s",
+ group.getKey(),
+ group.getPreviousParent() == null
+ ? "null" : group.getPreviousParent().getKey(),
+ group.getParent() == null
+ ? "null" : group.getParent().getKey()));
+ }
+ }
+ }
+
+ private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
+
+ int cmp = Integer.compare(o1.getSection(), o2.getSection());
+
+ if (cmp == 0) {
+ for (int i = 0; i < mNotifComparators.size(); i++) {
+ cmp = mNotifComparators.get(i).compare(o1, o2);
+ if (cmp != 0) {
+ break;
+ }
+ }
+ }
+
+ final NotificationEntry rep1 = o1.getRepresentativeEntry();
+ final NotificationEntry rep2 = o2.getRepresentativeEntry();
+
+ if (cmp == 0) {
+ cmp = rep1.getRanking().getRank() - rep2.getRanking().getRank();
+ }
+
+ if (cmp == 0) {
+ cmp = Long.compare(
+ rep2.getSbn().getNotification().when,
+ rep1.getSbn().getNotification().when);
+ }
+
+ return cmp;
+ };
+
+ private static final Comparator<NotificationEntry> sChildComparator = (o1, o2) -> {
+ int cmp = o1.getRanking().getRank() - o2.getRanking().getRank();
+
+ if (cmp == 0) {
+ cmp = Long.compare(
+ o2.getSbn().getNotification().when,
+ o1.getSbn().getNotification().when);
+ }
+
+ return cmp;
+ };
+
+ private boolean applyFilters(NotificationEntry entry, long now) {
+ NotifFilter filter = findRejectingFilter(entry, now);
+
+ if (filter != entry.mExcludingFilter) {
+ if (entry.mExcludingFilter == null) {
+ Log.i(TAG, String.format(
+ "%s: filtered out by '%s'",
+ entry.getKey(),
+ filter.getName()));
+ } else if (filter == null) {
+ Log.i(TAG, String.format(
+ "%s: no longer filtered out (previous filter was '%s')",
+ entry.getKey(),
+ entry.mExcludingFilter.getName()));
+ } else {
+ Log.i(TAG, String.format(
+ "%s: filter changed: '%s' -> '%s'",
+ entry.getKey(),
+ entry.mExcludingFilter,
+ filter));
+ }
+
+ // Note that groups and summaries can also be filtered out later if they're part of a
+ // malformed group. We currently don't have a great way to track that beyond parenting
+ // change logs. Consider adding something similar to mExcludingFilter for them.
+ entry.mExcludingFilter = filter;
+ }
+
+ return filter != null;
+ }
+
+ @Nullable private NotifFilter findRejectingFilter(NotificationEntry entry, long now) {
+ for (int i = 0; i < mNotifFilters.size(); i++) {
+ NotifFilter filter = mNotifFilters.get(i);
+ if (filter.shouldFilterOut(entry, now)) {
+ return filter;
+ }
+ }
+ return null;
+ }
+
+ private boolean applyTopLevelPromoters(NotificationEntry entry) {
+ NotifPromoter promoter = findPromoter(entry);
+
+ if (promoter != entry.mNotifPromoter) {
+ if (entry.mNotifPromoter == null) {
+ Log.i(TAG, String.format(
+ "%s: Entry promoted to top level by '%s'",
+ entry.getKey(),
+ promoter.getName()));
+ } else if (promoter == null) {
+ Log.i(TAG, String.format(
+ "%s: Entry is no longer promoted to top level (previous promoter was '%s')",
+ entry.getKey(),
+ entry.mNotifPromoter.getName()));
+ } else {
+ Log.i(TAG, String.format(
+ "%s: Top-level promoter changed: '%s' -> '%s'",
+ entry.getKey(),
+ entry.mNotifPromoter,
+ promoter));
+ }
+
+ entry.mNotifPromoter = promoter;
+ }
+
+ return promoter != null;
+ }
+
+ @Nullable private NotifPromoter findPromoter(NotificationEntry entry) {
+ for (int i = 0; i < mNotifPromoters.size(); i++) {
+ NotifPromoter promoter = mNotifPromoters.get(i);
+ if (promoter.shouldPromoteToTopLevel(entry)) {
+ return promoter;
+ }
+ }
+ return null;
+ }
+
+ private void rebuildListIfBefore(@PipelineState.StateName int state) {
+ mPipelineState.requireIsBefore(state);
+ if (mPipelineState.is(STATE_IDLE)) {
+ buildList();
+ }
+ }
+
+ private void dispatchOnBeforeTransformGroups(
+ List<ListEntry> entries,
+ List<ListEntry> newlyVisibleEntries) {
+ for (int i = 0; i < mOnBeforeTransformGroupsListeners.size(); i++) {
+ mOnBeforeTransformGroupsListeners.get(i)
+ .onBeforeTransformGroups(entries, newlyVisibleEntries);
+ }
+ }
+
+ private void dispatchOnBeforeSort(List<ListEntry> entries) {
+ for (int i = 0; i < mOnBeforeSortListeners.size(); i++) {
+ mOnBeforeSortListeners.get(i).onBeforeSort(entries);
+ }
+ }
+
+ private void dispatchOnBeforeRenderList(List<ListEntry> entries) {
+ for (int i = 0; i < mOnBeforeRenderListListeners.size(); i++) {
+ mOnBeforeRenderListListeners.get(i).onBeforeRenderList(entries);
+ }
+ }
+
+ /** See {@link #setOnRenderListListener(OnRenderListListener)} */
+ public interface OnRenderListListener {
+ /**
+ * Called with the final filtered, grouped, and sorted list.
+ *
+ * @param entries A read-only view into the current notif list. Note that this list is
+ * backed by the live list and will change in response to new pipeline runs.
+ */
+ void onRenderList(List<ListEntry> entries);
+ }
+
+ private static class DefaultSectionsProvider extends SectionsProvider {
+ DefaultSectionsProvider() {
+ super("DefaultSectionsProvider");
+ }
+
+ @Override
+ public int getSection(ListEntry entry) {
+ return 0;
+ }
+ }
+
+ private static final String TAG = "NotifListBuilderImpl";
+
+ private static final int MIN_CHILDREN_FOR_GROUP = 2;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 28ccaf54e15e..3eb55ef6bc93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -60,6 +60,8 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -84,7 +86,7 @@ import java.util.Objects;
* At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
* clean this up in the future.
*/
-public final class NotificationEntry {
+public final class NotificationEntry extends ListEntry {
private final String mKey;
private StatusBarNotification mSbn;
@@ -98,6 +100,12 @@ public final class NotificationEntry {
/** List of lifetime extenders that are extending the lifetime of this notification. */
final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
+ /** If this notification was filtered out, then the filter that did the filtering. */
+ @Nullable NotifFilter mExcludingFilter;
+
+ /** If this was a group child that was promoted to the top level, then who did the promoting. */
+ @Nullable NotifPromoter mNotifPromoter;
+
/*
* Old members
@@ -164,8 +172,8 @@ public final class NotificationEntry {
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@NonNull Ranking ranking) {
- checkNotNull(sbn);
- checkNotNull(sbn.getKey());
+ super(checkNotNull(checkNotNull(sbn).getKey()));
+
checkNotNull(ranking);
mKey = sbn.getKey();
@@ -173,6 +181,11 @@ public final class NotificationEntry {
setRanking(ranking);
}
+ @Override
+ public NotificationEntry getRepresentativeEntry() {
+ return this;
+ }
+
/** The key for this notification. Guaranteed to be immutable and unique */
public String getKey() {
return mKey;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
new file mode 100644
index 000000000000..986ee17cc906
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.init;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Temporary class that tracks the result of the list builder and dumps it to text when requested.
+ *
+ * Eventually, this will be something that hands off the result of the pipeline to the View layer.
+ */
+public class FakePipelineConsumer implements Dumpable {
+ private List<ListEntry> mEntries = Collections.emptyList();
+
+ /** Attach the consumer to the pipeline. */
+ public void attach(NotifListBuilderImpl listBuilder) {
+ listBuilder.setOnRenderListListener(this::onBuildComplete);
+ }
+
+ private void onBuildComplete(List<ListEntry> entries) {
+ mEntries = entries;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println();
+ pw.println("Active notif tree:");
+ for (int i = 0; i < mEntries.size(); i++) {
+ ListEntry entry = mEntries.get(i);
+ if (entry instanceof GroupEntry) {
+ GroupEntry ge = (GroupEntry) entry;
+ pw.println(dumpGroup(ge, "", i));
+
+ pw.println(dumpEntry(ge.getSummary(), INDENT, -1));
+ for (int j = 0; j < ge.getChildren().size(); j++) {
+ pw.println(dumpEntry(ge.getChildren().get(j), INDENT, j));
+ }
+ } else {
+ pw.println(dumpEntry(entry.getRepresentativeEntry(), "", i));
+ }
+ }
+ }
+
+ private String dumpGroup(GroupEntry entry, String indent, int index) {
+ return String.format(
+ "%s[%d] %s (group)",
+ indent,
+ index,
+ entry.getKey());
+ }
+
+ private String dumpEntry(NotificationEntry entry, String indent, int index) {
+ return String.format(
+ "%s[%s] %s (channel=%s)",
+ indent,
+ index == -1 ? "*" : Integer.toString(index),
+ entry.getKey(),
+ entry.getChannel() != null ? entry.getChannel().getId() : "");
+ }
+
+ private static final String INDENT = " ";
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
index 31921a436747..3b3e7e2a2933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
@@ -14,12 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.collection.init;
import android.util.Log;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -28,21 +34,38 @@ import javax.inject.Singleton;
* Initialization code for the new notification pipeline.
*/
@Singleton
-public class NewNotifPipeline {
+public class NewNotifPipeline implements Dumpable {
private final NotifCollection mNotifCollection;
+ private final NotifListBuilderImpl mNotifPipeline;
+ private final DumpController mDumpController;
+
+ private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
@Inject
public NewNotifPipeline(
- NotifCollection notifCollection) {
+ NotifCollection notifCollection,
+ NotifListBuilderImpl notifPipeline,
+ DumpController dumpController) {
mNotifCollection = notifCollection;
+ mNotifPipeline = notifPipeline;
+ mDumpController = dumpController;
}
/** Hooks the new pipeline up to NotificationManager */
public void initialize(
NotificationListener notificationService) {
+ mFakePipelineConsumer.attach(mNotifPipeline);
+ mNotifPipeline.attach(mNotifCollection);
mNotifCollection.attach(notificationService);
Log.d(TAG, "Notif pipeline initialized");
+
+ mDumpController.registerDumpable("NotifPipeline", this);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mFakePipelineConsumer.dump(fd, pw, args);
}
private static final String TAG = "NewNotifPipeline";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifListBuilder.java
new file mode 100644
index 000000000000..15d3b9222cd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifListBuilder.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+
+import java.util.List;
+
+/**
+ * The system that constructs the current "notification list", the list of notifications that are
+ * currently being displayed to the user.
+ *
+ * The pipeline proceeds through a series of stages in order to produce the final list (see below).
+ * Each stage exposes hooks and listeners for other code to participate.
+ *
+ * This list differs from the canonical one we receive from system server in a few ways:
+ * - Filtered: Some notifications are filtered out. For example, we filter out notifications whose
+ * views haven't been inflated yet. We also filter out some notifications if we're on the lock
+ * screen. To participate, see {@link #addFilter(NotifFilter)}.
+ * - Grouped: Notifications that are part of the same group are clustered together into a single
+ * GroupEntry. These groups are then transformed in order to remove children or completely split
+ * them apart. To participate, see {@link #addPromoter(NotifPromoter)}.
+ * - Sorted: All top-level notifications are sorted. To participate, see
+ * {@link #setSectionsProvider(SectionsProvider)} and {@link #setComparators(List)}
+ *
+ * The exact order of all hooks is as follows:
+ * 0. Collection listeners are fired (see {@link NotifCollection}).
+ * 1. NotifFilters are called on each notification currently in NotifCollection.
+ * 2. Initial grouping is performed (NotificationEntries will have their parents set
+ * appropriately).
+ * 3. OnBeforeTransformGroupListeners are fired
+ * 4. NotifPromoters are called on each notification with a parent
+ * 5. OnBeforeSortListeners are fired
+ * 6. SectionsProvider is called on each top-level entry in the list
+ * 7. The top-level entries are sorted using the provided NotifComparators (plus some additional
+ * built-in logic).
+ * 8. OnBeforeRenderListListeners are fired
+ * 9. The list is handed off to the view layer to be rendered.
+ */
+public interface NotifListBuilder {
+
+ /**
+ * Registers a filter with the pipeline. Filters are called on each notification in the order
+ * that they were registered. If any filter returns true, the notification is removed from the
+ * pipeline (and no other filters are called on that notif).
+ */
+ void addFilter(NotifFilter filter);
+
+ /**
+ * Registers a promoter with the pipeline. Promoters are able to promote child notifications to
+ * top-level, i.e. move a notification that would be a child of a group and make it appear
+ * ungrouped. Promoters are called on each child notification in the order that they are
+ * registered. If any promoter returns true, the notification is removed from the group (and no
+ * other promoters are called on it).
+ */
+ void addPromoter(NotifPromoter promoter);
+
+ /**
+ * Assigns sections to each top-level entry, where a section is simply an integer. Sections are
+ * the primary metric by which top-level entries are sorted; NotifComparators are only consulted
+ * when two entries are in the same section. The pipeline doesn't assign any particular meaning
+ * to section IDs -- from it's perspective they're just numbers and it sorts them by a simple
+ * numerical comparison.
+ */
+ void setSectionsProvider(SectionsProvider provider);
+
+ /**
+ * Comparators that are used to sort top-level entries that share the same section. The
+ * comparators are executed in order until one of them returns a non-zero result. If all return
+ * zero, the pipeline falls back to sorting by rank (and, failing that, Notification.when).
+ */
+ void setComparators(List<NotifComparator> comparators);
+
+ /**
+ * Called after notifications have been filtered and after the initial grouping has been
+ * performed but before NotifPromoters have had a chance to promote children out of groups.
+ */
+ void addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener listener);
+
+ /**
+ * Called after notifs have been filtered and groups have been determined but before sections
+ * have been determined or the notifs have been sorted.
+ */
+ void addOnBeforeSortListener(OnBeforeSortListener listener);
+
+ /**
+ * Called at the end of the pipeline after the notif list has been finalized but before it has
+ * been handed off to the view layer.
+ */
+ void addOnBeforeRenderListListener(OnBeforeRenderListListener listener);
+
+ /**
+ * Returns a read-only view in to the current notification list. If this method is called
+ * during pipeline execution it will return the current state of the list, which will likely
+ * be only partially-generated.
+ */
+ List<ListEntry> getActiveNotifs();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
new file mode 100644
index 000000000000..f6ca12d83fdd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+
+import java.util.List;
+
+/** See {@link NotifListBuilder#addOnBeforeRenderListListener(OnBeforeRenderListListener)} */
+public interface OnBeforeRenderListListener {
+ /**
+ * Called at the end of the pipeline after the notif list has been finalized but before it has
+ * been handed off to the view layer.
+ *
+ * @param entries The current list of top-level entries. Note that this is a live view into the
+ * current list and will change whenever the pipeline is rerun.
+ */
+ void onBeforeRenderList(List<ListEntry> entries);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
new file mode 100644
index 000000000000..7be7ac03e1f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+
+import java.util.List;
+
+/** See {@link NotifListBuilder#addOnBeforeSortListener(OnBeforeSortListener)} */
+public interface OnBeforeSortListener {
+ /**
+ * Called after the notif list has been filtered and grouped but before sections have been
+ * determined or sorting has taken place.
+ *
+ * @param entries The current list of top-level entries. Note that this is a live view into the
+ * current list and will change whenever the pipeline is rerun.
+ */
+ void onBeforeSort(List<ListEntry> entries);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
new file mode 100644
index 000000000000..170ff48ad7f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+
+import java.util.List;
+
+/**
+ * See
+ * {@link NotifListBuilder#addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener)}
+ */
+public interface OnBeforeTransformGroupsListener {
+ /**
+ * Called after notifs have been filtered and grouped but before {@link NotifPromoter}s have
+ * been called.
+ *
+ * @param list The current filtered and grouped list of (top-level) entries. Note that this is
+ * a live view into the current notif list and will change as the list moves through
+ * the pipeline.
+ * @param newlyVisibleEntries The list of all entries (both top-level and children) who have
+ * been added to the list for the first time.
+ */
+ void onBeforeTransformGroups(List<ListEntry> list, List<ListEntry> newlyVisibleEntries);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
new file mode 100644
index 000000000000..ad4bbd915787
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import android.annotation.IntDef;
+
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Used by {@link NotifListBuilderImpl} to track its internal state machine.
+ */
+public class PipelineState {
+
+ private @StateName int mState = STATE_IDLE;
+
+ /** Returns true if the current state matches <code>state</code> */
+ public boolean is(@StateName int state) {
+ return state == mState;
+ }
+
+ public @StateName int getState() {
+ return mState;
+ }
+
+ public void setState(@StateName int state) {
+ mState = state;
+ }
+
+ /**
+ * Increments the state from <code>(to - 1)</code> to <code>to</code>. If the current state
+ * isn't <code>(to - 1)</code>, throws an exception.
+ */
+ public void incrementTo(@StateName int to) {
+ if (mState != to - 1) {
+ throw new IllegalStateException(
+ "Cannot increment from state " + mState + " to state " + to);
+ }
+ mState = to;
+ }
+
+ /**
+ * Throws an exception if the current state is not <code>state</code>.
+ */
+ public void requireState(@StateName int state) {
+ if (state != mState) {
+ throw new IllegalStateException(
+ "Required state is <" + state + " but actual state is " + mState);
+ }
+ }
+
+ /**
+ * Throws an exception if the current state is >= <code>state</code>.
+ */
+ public void requireIsBefore(@StateName int state) {
+ if (mState >= state) {
+ throw new IllegalStateException(
+ "Required state is <" + state + " but actual state is " + mState);
+ }
+ }
+
+ public static final int STATE_IDLE = 0;
+ public static final int STATE_BUILD_PENDING = 1;
+ public static final int STATE_BUILD_STARTED = 2;
+ public static final int STATE_FILTERING = 3;
+ public static final int STATE_TRANSFORMING = 4;
+ public static final int STATE_SORTING = 5;
+ public static final int STATE_FINALIZING = 6;
+
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_IDLE,
+ STATE_BUILD_PENDING,
+ STATE_BUILD_STARTED,
+ STATE_FILTERING,
+ STATE_TRANSFORMING,
+ STATE_SORTING,
+ STATE_FINALIZING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StateName {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
new file mode 100644
index 000000000000..a191c830537d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Pluggable for participating in notif sorting. See {@link NotifListBuilder#setComparators(List)}.
+ */
+public abstract class NotifComparator
+ extends Pluggable<NotifComparator>
+ implements Comparator<ListEntry> {
+
+ protected NotifComparator(String name) {
+ super(name);
+ }
+
+ /**
+ * Compare two ListEntries. Note that these might be either NotificationEntries or GroupEntries.
+ *
+ * @return a negative integer, zero, or a positive integer as the first argument is less than
+ * equal to, or greater than the second (same as standard Comparator<> interface).
+ */
+ public abstract int compare(ListEntry o1, ListEntry o2);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
new file mode 100644
index 000000000000..685eac88de34
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+
+/**
+ * Pluggable for participating in notif filtering. See
+ * {@link NotifListBuilder#addFilter(NotifFilter)}.
+ */
+public abstract class NotifFilter extends Pluggable<NotifFilter> {
+ protected NotifFilter(String name) {
+ super(name);
+ }
+
+ /**
+ * If returns true, this notification will not be included in the final list displayed to the
+ * user. Filtering is performed on each active notification every time the pipeline is run.
+ * This doesn't necessarily mean that your filter will get called on every notification,
+ * however. If another filter returns true before yours, we'll skip straight to the next notif.
+ *
+ * @param entry The entry in question
+ * @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
+ * pipeline execution. This value will be the same for all pluggable calls made
+ * during this pipeline run, giving pluggables a stable concept of "now" to compare
+ * various entries against.
+ * @return True if the notif should be removed from the list
+ */
+ public abstract boolean shouldFilterOut(NotificationEntry entry, long now);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java
new file mode 100644
index 000000000000..84e16f432740
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+
+/**
+ * Pluggable for participating in notif promotion. Notif promoters can upgrade notifications
+ * from being children of a group to top-level notifications. See
+ * {@link NotifListBuilder#addPromoter(NotifPromoter)}.
+ */
+public abstract class NotifPromoter extends Pluggable<NotifPromoter> {
+ protected NotifPromoter(String name) {
+ super(name);
+ }
+
+ /**
+ * If true, the child will be removed from its parent and placed at the top level of the notif
+ * list. By the time this method is called, child.getParent() has been set, so you can
+ * examine it (or any other entries in the notif list) for extra information.
+ *
+ * This method is only called on notifs that are currently children of groups. This doesn't
+ * necessarily mean that your promoter will get called on every child notification, however. If
+ * another promoter returns true before yours, we'll skip straight to the next notif.
+ */
+ public abstract boolean shouldPromoteToTopLevel(NotificationEntry child);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
new file mode 100644
index 000000000000..f9ce197c6547
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+
+import android.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+
+/**
+ * Generic superclass for chunks of code that can plug into the {@link NotifListBuilder}.
+ *
+ * A pluggable is fundamentally three things:
+ * 1. A name (for debugging purposes)
+ * 2. The functionality that the pluggable provides to the pipeline (this is determined by the
+ * subclass).
+ * 3. A way for the pluggable to inform the pipeline that its state has changed and the pipeline
+ * should be rerun (in this case, the invalidate() method).
+ *
+ * @param <This> The type of the subclass. Subclasses should bind their own type here.
+ */
+public abstract class Pluggable<This> {
+ private final String mName;
+ @Nullable private PluggableListener<This> mListener;
+
+ Pluggable(String name) {
+ mName = name;
+ }
+
+ public final String getName() {
+ return mName;
+ }
+
+ /**
+ * Call this method when something has caused this pluggable's behavior to change. The pipeline
+ * will be re-run.
+ */
+ public final void invalidateList() {
+ if (mListener != null) {
+ mListener.onPluggableInvalidated((This) this);
+ }
+ }
+
+ /** Set a listener to be notified when a pluggable is invalidated. */
+ public void setInvalidationListener(PluggableListener<This> listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Listener interface for when pluggables are invalidated.
+ *
+ * @param <T> The type of pluggable that is being listened to.
+ */
+ public interface PluggableListener<T> {
+ /** Called whenever {@link #invalidateList()} is called on this pluggable. */
+ void onPluggableInvalidated(T pluggable);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/SectionsProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/SectionsProvider.java
new file mode 100644
index 000000000000..11ea85062781
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/SectionsProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+
+/**
+ * Interface for sorting notifications into "sections", such as a heads-upping section, people
+ * section, alerting section, silent section, etc.
+ */
+public abstract class SectionsProvider extends Pluggable<SectionsProvider> {
+
+ protected SectionsProvider(String name) {
+ super(name);
+ }
+
+ /**
+ * Returns the section that this entry belongs to. A section can be any non-negative integer.
+ * When entries are sorted, they are first sorted by section and then by any remainining
+ * comparators.
+ */
+ public abstract int getSection(ListEntry entry);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index f0d07a7ed0bd..9ac5b44f8639 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -198,7 +198,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private String mLoggingKey;
private NotificationGuts mGuts;
private NotificationEntry mEntry;
- private StatusBarNotification mStatusBarNotification;
private String mAppName;
/**
@@ -261,10 +260,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public void onClick(View v) {
if (!shouldShowPublic() && (!mIsLowPriority || isExpanded())
- && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
+ && mGroupManager.isSummaryOfGroup(mEntry.getSbn())) {
mGroupExpansionChanging = true;
- final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
- boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
+ final boolean wasExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn());
+ boolean nowExpanded = mGroupManager.toggleGroupExpansion(mEntry.getSbn());
mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
nowExpanded);
@@ -441,7 +440,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public void setEntry(@NonNull NotificationEntry entry) {
mEntry = entry;
- mStatusBarNotification = entry.getSbn();
cacheIsSystemNotification();
}
@@ -516,7 +514,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public boolean getIsNonblockable() {
boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
- .isNonblockable(mStatusBarNotification.getPackageName(),
+ .isNonblockable(mEntry.getSbn().getPackageName(),
mEntry.getChannel().getId());
// If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
@@ -526,7 +524,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
Log.d(TAG, "Retrieving isSystemNotification on main thread");
}
mSystemNotificationAsyncTask.cancel(true /* mayInterruptIfRunning */);
- mEntry.mIsSystemNotification = isSystemNotification(mContext, mStatusBarNotification);
+ mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn());
}
isNonblockable |= mEntry.getChannel().isImportanceLockedByOEM();
@@ -547,11 +545,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
for (NotificationContentView l : mLayouts) {
l.onNotificationUpdated(mEntry);
}
- mIsColorized = mStatusBarNotification.getNotification().isColorized();
+ mIsColorized = mEntry.getSbn().getNotification().isColorized();
mShowingPublicInitialized = false;
updateNotificationColor();
if (mMenuRow != null) {
- mMenuRow.onNotificationUpdated(mStatusBarNotification);
+ mMenuRow.onNotificationUpdated(mEntry.getSbn());
mMenuRow.setAppName(mAppName);
}
if (mIsSummaryWithChildren) {
@@ -583,7 +581,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
/** Called when the notification's ranking was changed (but nothing else changed). */
public void onNotificationRankingUpdated() {
if (mMenuRow != null) {
- mMenuRow.onNotificationUpdated(mStatusBarNotification);
+ mMenuRow.onNotificationUpdated(mEntry.getSbn());
}
}
@@ -676,10 +674,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight);
}
- public StatusBarNotification getStatusBarNotification() {
- return mStatusBarNotification;
- }
-
public NotificationEntry getEntry() {
return mEntry;
}
@@ -807,7 +801,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* @return whether this notification is the only child in the group summary
*/
public boolean isOnlyChildInGroup() {
- return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
+ return mGroupManager.isOnlyChildInGroup(mEntry.getSbn());
}
public ExpandableNotificationRow getNotificationParent() {
@@ -1181,7 +1175,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
if (mMenuRow.getMenuView() == null) {
- mMenuRow.createMenu(this, mStatusBarNotification);
+ mMenuRow.createMenu(this, mEntry.getSbn());
mMenuRow.setAppName(mAppName);
FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
@@ -1222,7 +1216,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (oldMenu != null) {
int menuIndex = indexOfChild(oldMenu);
removeView(oldMenu);
- mMenuRow.createMenu(ExpandableNotificationRow.this, mStatusBarNotification);
+ mMenuRow.createMenu(ExpandableNotificationRow.this, mEntry.getSbn());
mMenuRow.setAppName(mAppName);
addView(mMenuRow.getMenuView(), menuIndex);
}
@@ -1230,7 +1224,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.initView();
l.reInflateViews();
}
- mStatusBarNotification.clearPackageContext();
+ mEntry.getSbn().clearPackageContext();
mNotificationInflater.clearCachesAndReInflate();
}
@@ -1290,7 +1284,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
== Configuration.UI_MODE_NIGHT_YES;
mNotificationColor = ContrastColorUtil.resolveContrastColor(mContext,
- getStatusBarNotification().getNotification().color,
+ mEntry.getSbn().getNotification().color,
getBackgroundColorWithoutTint(), nightMode);
}
@@ -1438,7 +1432,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void performDismiss(boolean fromAccessibility) {
if (isOnlyChildInGroup()) {
NotificationEntry groupSummary =
- mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
+ mGroupManager.getLogicalGroupSummary(mEntry.getSbn());
if (groupSummary.isClearable()) {
// If this is the only child in the group, dismiss the group, but don't try to show
// the blocking helper affordance!
@@ -2209,8 +2203,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
getFalsingManager().setNotificationExpanded();
if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
&& !mChildrenContainer.showingAsLowPriority()) {
- final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
- mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
+ final boolean wasExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn());
+ mGroupManager.setGroupExpanded(mEntry.getSbn(), userExpanded);
onExpansionChanged(true /* userAction */, wasExpanded);
return;
}
@@ -2356,7 +2350,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public boolean isGroupExpanded() {
- return mGroupManager.isGroupExpanded(mStatusBarNotification);
+ return mGroupManager.isGroupExpanded(mEntry.getSbn());
}
private void onChildrenCountChanged() {
@@ -2397,9 +2391,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
for (int i = 0; i < numChildren; i++) {
final ExpandableNotificationRow childRow = childrenRows.get(i);
final NotificationChannel childChannel = childRow.getEntry().getChannel();
- final StatusBarNotification childSbn = childRow.getStatusBarNotification();
- if (childSbn.getUser().equals(mStatusBarNotification.getUser()) &&
- childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) {
+ final StatusBarNotification childSbn = childRow.getEntry().getSbn();
+ if (childSbn.getUser().equals(mEntry.getSbn().getUser())
+ && childSbn.getPackageName().equals(mEntry.getSbn().getPackageName())) {
channels.add(childChannel);
}
}
@@ -2593,7 +2587,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void makeActionsVisibile() {
setUserExpanded(true, true);
if (isChildInGroup()) {
- mGroupManager.setGroupExpanded(mStatusBarNotification, true);
+ mGroupManager.setGroupExpanded(mEntry.getSbn(), true);
}
notifyHeightChanged(false /* needsAnimation */);
}
@@ -2890,7 +2884,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void onExpandedByGesture(boolean userExpanded) {
int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
- if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
+ if (mGroupManager.isSummaryOfGroup(mEntry.getSbn())) {
event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
}
MetricsLogger.action(mContext, event, userExpanded);
@@ -2935,7 +2929,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
boolean nowExpanded = isExpanded();
if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
- nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
+ nowExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn());
}
if (nowExpanded != wasExpanded) {
updateShelfIconColor();
@@ -3224,7 +3218,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dump(fd, pw, args);
- pw.println(" Notification: " + getStatusBarNotification().getKey());
+ pw.println(" Notification: " + mEntry.getKey());
pw.print(" visibility: " + getVisibility());
pw.print(", alpha: " + getAlpha());
pw.print(", translation: " + getTranslation());
@@ -3267,7 +3261,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected Boolean doInBackground(Void... voids) {
- return isSystemNotification(mContext, mStatusBarNotification);
+ return isSystemNotification(mContext, mEntry.getSbn());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 37f63c9779f8..7b758aa09e5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -166,7 +166,7 @@ public class NotificationBlockingHelperManager {
}
private LogMaker getLogMaker() {
- return mBlockingHelperRow.getStatusBarNotification()
+ return mBlockingHelperRow.getEntry().getSbn()
.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index a91a119d4ea7..54dee8c32940 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -512,7 +512,7 @@ public class NotificationContentInflater {
existingWrapper.onReinflated();
}
} catch (Exception e) {
- handleInflationError(runningInflations, e, row.getStatusBarNotification(), callback);
+ handleInflationError(runningInflations, e, row.getEntry().getSbn(), callback);
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
runningInflations.put(inflationId, new CancellationSignal());
@@ -563,7 +563,7 @@ public class NotificationContentInflater {
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, row.getStatusBarNotification(),
+ handleInflationError(runningInflations, e, row.getEntry().getSbn(),
callback);
}
}
@@ -838,7 +838,7 @@ public class NotificationContentInflater {
private void handleError(Exception e) {
mRow.getEntry().onInflationTaskFinished();
- StatusBarNotification sbn = mRow.getStatusBarNotification();
+ StatusBarNotification sbn = mRow.getEntry().getSbn();
final String ident = sbn.getPackageName() + "/0x"
+ Integer.toHexString(sbn.getId());
Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index f67cd1bef281..77c0a460f3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -189,7 +189,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@VisibleForTesting
protected boolean bindGuts(final ExpandableNotificationRow row,
NotificationMenuRowPlugin.MenuItem item) {
- StatusBarNotification sbn = row.getStatusBarNotification();
+ StatusBarNotification sbn = row.getEntry().getSbn();
row.setGutsView(item);
row.setTag(sbn.getPackageName());
@@ -238,7 +238,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
final ExpandableNotificationRow row,
NotificationSnooze notificationSnoozeView) {
NotificationGuts guts = row.getGuts();
- StatusBarNotification sbn = row.getStatusBarNotification();
+ StatusBarNotification sbn = row.getEntry().getSbn();
notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
notificationSnoozeView.setStatusBarNotification(sbn);
@@ -258,7 +258,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
final ExpandableNotificationRow row,
AppOpsInfo appOpsInfoView) {
NotificationGuts guts = row.getGuts();
- StatusBarNotification sbn = row.getStatusBarNotification();
+ StatusBarNotification sbn = row.getEntry().getSbn();
UserHandle userHandle = sbn.getUser();
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
userHandle.getIdentifier());
@@ -284,7 +284,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
final ExpandableNotificationRow row,
NotificationInfo notificationInfoView) throws Exception {
NotificationGuts guts = row.getGuts();
- StatusBarNotification sbn = row.getStatusBarNotification();
+ StatusBarNotification sbn = row.getEntry().getSbn();
String packageName = sbn.getPackageName();
// Settings link is only valid for notifications that specify a non-system user
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 2da4d2cf57d2..7248bcef621c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -39,7 +39,7 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
- updateImageTag(row.getStatusBarNotification());
+ updateImageTag(row.getEntry().getSbn());
}
private void updateImageTag(StatusBarNotification notification) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index 4261df3dab27..41f93cceacc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -44,7 +44,7 @@ public class NotificationBigTextTemplateViewWrapper extends NotificationTemplate
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
// the transformation types and we need to have our values set by then.
- resolveViews(row.getStatusBarNotification());
+ resolveViews(row.getEntry().getSbn());
super.onContentUpdated(row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 0b3871de4e5e..5e52c0a5a66f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -125,7 +125,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
updateTransformedTypes();
addRemainingTransformTypes();
updateCropToPaddingForImageViews();
- Notification notification = row.getStatusBarNotification().getNotification();
+ Notification notification = row.getEntry().getSbn().getNotification();
mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
// The work profile image is always the same lets just set the icon tag for it not to
// animate
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 516d649a3bea..2a4b315b0aa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -183,7 +183,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
com.android.systemui.R.id.quick_qs_panel);
panel.getMediaPlayer().setMediaSession(token,
- mRow.getStatusBarNotification().getNotification().getSmallIcon(),
+ mRow.getEntry().getSbn().getNotification().getSmallIcon(),
getNotificationHeader().getOriginalIconColor(),
mRow.getCurrentBackgroundTint(),
mActions,
@@ -191,11 +191,11 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
QSPanel bigPanel = ctrl.getStatusBarView().findViewById(
com.android.systemui.R.id.quick_settings_panel);
bigPanel.addMediaSession(token,
- mRow.getStatusBarNotification().getNotification().getSmallIcon(),
+ mRow.getEntry().getSbn().getNotification().getSmallIcon(),
getNotificationHeader().getOriginalIconColor(),
mRow.getCurrentBackgroundTint(),
mActions,
- mRow.getStatusBarNotification());
+ mRow.getEntry().getSbn());
}
boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 97d84433dafe..90ea6e344226 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -286,7 +286,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
// the transformation types and we need to have our values set by then.
- resolveTemplateViews(row.getStatusBarNotification());
+ resolveTemplateViews(row.getEntry().getSbn());
super.onContentUpdated(row);
if (row.getHeaderVisibleAmount() != DEFAULT_HEADER_VISIBLE_AMOUNT) {
setHeaderVisibleAmount(row.getHeaderVisibleAmount());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 45f7b3a5bb2a..75ceb0fa1989 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -299,7 +299,7 @@ public class NotificationChildrenContainer extends ViewGroup {
public void recreateNotificationHeader(OnClickListener listener) {
mHeaderClickListener = listener;
- StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
+ StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
notification.getNotification());
RemoteViews header = builder.makeNotificationHeader();
@@ -329,7 +329,7 @@ public class NotificationChildrenContainer extends ViewGroup {
*/
private void recreateLowPriorityHeader(Notification.Builder builder) {
RemoteViews header;
- StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
+ StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
if (mIsLowPriority) {
if (builder == null) {
builder = Notification.Builder.recoverBuilder(getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 462fa59c785c..43af3aaa5b84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1454,8 +1454,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
ExpandableNotificationRow row = topEntry.getRow();
if (row.isChildInGroup()) {
- final NotificationEntry groupSummary
- = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+ final NotificationEntry groupSummary =
+ mGroupManager.getGroupSummary(row.getEntry().getSbn());
if (groupSummary != null) {
row = groupSummary.getRow();
}
@@ -2996,7 +2996,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private boolean isChildInGroup(View child) {
return child instanceof ExpandableNotificationRow
&& mGroupManager.isChildInGroupWithSummary(
- ((ExpandableNotificationRow) child).getStatusBarNotification());
+ ((ExpandableNotificationRow) child).getEntry().getSbn());
}
/**
@@ -3071,7 +3071,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
NotificationEntry groupSummary =
- mGroupManager.getGroupSummary(row.getStatusBarNotification());
+ mGroupManager.getGroupSummary(row.getEntry().getSbn());
if (groupSummary != null && groupSummary.getRow() != row) {
return row.getVisibility() == View.INVISIBLE;
}
@@ -6134,7 +6134,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- mMetricsLogger.write(row.getStatusBarNotification().getLogMaker()
+ mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
.setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
.setType(MetricsEvent.TYPE_ACTION)
);
@@ -6160,7 +6160,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
public void onMenuShown(View row) {
if (row instanceof ExpandableNotificationRow) {
ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
- mMetricsLogger.write(notificationRow.getStatusBarNotification().getLogMaker()
+ mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
.setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
.setType(MetricsEvent.TYPE_ACTION));
mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
@@ -6256,7 +6256,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isHeadsUp()) {
mHeadsUpManager.addSwipedOutNotification(
- row.getStatusBarNotification().getKey());
+ row.getEntry().getSbn().getKey());
}
isBlockingHelperShown =
row.performDismissWithBlockingHelper(false /* fromAccessibility */);
@@ -6327,9 +6327,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
if (animView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
if (row.isPinned() && !canChildBeDismissed(row)
- && row.getStatusBarNotification().getNotification().fullScreenIntent
+ && row.getEntry().getSbn().getNotification().fullScreenIntent
== null) {
- mHeadsUpManager.removeNotification(row.getStatusBarNotification().getKey(),
+ mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
true /* removeImmediately */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 1c8e832d03d2..8ee964f3f771 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -22,7 +22,6 @@ import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -32,9 +31,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
-import android.util.MathUtils;
import android.util.StatsLog;
-import android.view.Gravity;
import android.view.ISystemGestureExclusionListener;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -44,7 +41,6 @@ import android.view.InputMonitor;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -53,7 +49,10 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.NavigationEdgeBackPlugin;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
@@ -62,7 +61,8 @@ import java.util.concurrent.Executor;
/**
* Utility class to handle edge swipes for back gesture
*/
-public class EdgeBackGestureHandler implements DisplayListener {
+public class EdgeBackGestureHandler implements DisplayListener,
+ PluginListener<NavigationEdgeBackPlugin> {
private static final String TAG = "EdgeBackGestureHandler";
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
@@ -85,6 +85,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
+ private PluginManager mPluginManager;
private final Point mDisplaySize = new Point();
private final int mDisplayId;
@@ -100,14 +101,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
private final float mTouchSlop;
// Duration after which we consider the event as longpress.
private final int mLongPressTimeout;
- // The threshold where the touch needs to be at most, such that the arrow is displayed above the
- // finger, otherwise it will be below
- private final int mMinArrowPosition;
- // The amount by which the arrow is shifted to avoid the finger
- private final int mFingerOffset;
-
-
- private final int mNavBarHeight;
private final PointF mDownPoint = new PointF();
private boolean mThresholdCrossed = false;
@@ -123,24 +116,49 @@ public class EdgeBackGestureHandler implements DisplayListener {
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
- private final WindowManager mWm;
-
- private NavigationBarEdgePanel mEdgePanel;
- private WindowManager.LayoutParams mEdgePanelLp;
- private final Rect mSamplingRect = new Rect();
- private RegionSamplingHelper mRegionSamplingHelper;
+ private NavigationEdgeBackPlugin mEdgeBackPlugin;
private int mLeftInset;
private int mRightInset;
private int mSysUiFlags;
+ private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
+ new NavigationEdgeBackPlugin.BackCallback() {
+ @Override
+ public void triggerBack() {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+
+ mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
+ (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
+ int backtype = (mInRejectedExclusion
+ ? StatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED :
+ StatsLog.BACK_GESTURE__TYPE__COMPLETED);
+ StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
+ (int) mDownPoint.y, mIsOnLeftEdge
+ ? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
+ StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
+ }
+
+ @Override
+ public void cancelBack() {
+ mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x,
+ (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
+ int backtype = StatsLog.BACK_GESTURE__TYPE__INCOMPLETE;
+ StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
+ (int) mDownPoint.y, mIsOnLeftEdge
+ ? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
+ StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
+ }
+ };
+
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiFlagContainer) {
+ SysUiState sysUiFlagContainer, PluginManager pluginManager) {
final Resources res = context.getResources();
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
- mWm = context.getSystemService(WindowManager.class);
mOverviewProxyService = overviewProxyService;
+ mPluginManager = pluginManager;
// Reduce the default touch slop to ensure that we can intercept the gesture
// before the app starts to react to it.
@@ -149,9 +167,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
ViewConfiguration.getLongPressTimeout());
- mNavBarHeight = res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height);
- mMinArrowPosition = res.getDimensionPixelSize(R.dimen.navigation_edge_arrow_min_y);
- mFingerOffset = res.getDimensionPixelSize(R.dimen.navigation_edge_finger_offset);
updateCurrentUserResources(res);
sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
@@ -206,15 +221,14 @@ public class EdgeBackGestureHandler implements DisplayListener {
mIsEnabled = isEnabled;
disposeInputChannel();
- if (mEdgePanel != null) {
- mWm.removeView(mEdgePanel);
- mEdgePanel = null;
- mRegionSamplingHelper.stop();
- mRegionSamplingHelper = null;
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.onDestroy();
+ mEdgeBackPlugin = null;
}
if (!mIsEnabled) {
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+ mPluginManager.removePluginListener(this);
try {
WindowManagerGlobal.getWindowManagerService()
@@ -244,39 +258,49 @@ public class EdgeBackGestureHandler implements DisplayListener {
mInputMonitor.getInputChannel(), Looper.getMainLooper());
// Add a nav bar panel window
- mEdgePanel = new NavigationBarEdgePanel(mContext);
- mEdgePanelLp = new WindowManager.LayoutParams(
- mContext.getResources()
- .getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
- mContext.getResources()
- .getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
- PixelFormat.TRANSLUCENT);
- mEdgePanelLp.privateFlags |=
- WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- mEdgePanelLp.setTitle(TAG + mDisplayId);
- mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
- mEdgePanelLp.windowAnimations = 0;
- mEdgePanel.setLayoutParams(mEdgePanelLp);
- mWm.addView(mEdgePanel, mEdgePanelLp);
- mRegionSamplingHelper = new RegionSamplingHelper(mEdgePanel,
- new RegionSamplingHelper.SamplingCallback() {
- @Override
- public void onRegionDarknessChanged(boolean isRegionDark) {
- mEdgePanel.setIsDark(!isRegionDark, true /* animate */);
- }
-
- @Override
- public Rect getSampledRegion(View sampledView) {
- return mSamplingRect;
- }
- });
- mRegionSamplingHelper.setWindowVisible(true);
+ setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+ mPluginManager.addPluginListener(
+ this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
+ }
+ }
+
+ @Override
+ public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) {
+ setEdgeBackPlugin(plugin);
+ }
+
+ @Override
+ public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
+ setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+ }
+
+ private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.onDestroy();
}
+ mEdgeBackPlugin = edgeBackPlugin;
+ mEdgeBackPlugin.setBackCallback(mBackCallback);
+ mEdgeBackPlugin.setLayoutParams(createLayoutParams());
+ updateDisplaySize();
+ }
+
+ private WindowManager.LayoutParams createLayoutParams() {
+ Resources resources = mContext.getResources();
+ WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
+ resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.privateFlags |=
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ layoutParams.setTitle(TAG + mContext.getDisplayId());
+ layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
+ layoutParams.windowAnimations = 0;
+ return layoutParams;
}
private void onInputEvent(InputEvent ev) {
@@ -316,7 +340,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
mInRejectedExclusion = false;
MotionEvent cancelEv = MotionEvent.obtain(ev);
cancelEv.setAction(MotionEvent.ACTION_CANCEL);
- mEdgePanel.handleTouch(cancelEv);
+ mEdgeBackPlugin.onMotionEvent(cancelEv);
cancelEv.recycle();
}
@@ -330,14 +354,8 @@ public class EdgeBackGestureHandler implements DisplayListener {
mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
if (mAllowGesture) {
- mEdgePanelLp.gravity = mIsOnLeftEdge
- ? (Gravity.LEFT | Gravity.TOP)
- : (Gravity.RIGHT | Gravity.TOP);
- mEdgePanel.setIsLeftPanel(mIsOnLeftEdge);
- mEdgePanel.handleTouch(ev);
- updateEdgePanelPosition(ev.getY());
- mWm.updateViewLayout(mEdgePanel, mEdgePanelLp);
- mRegionSamplingHelper.start(mSamplingRect);
+ mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
+ mEdgeBackPlugin.onMotionEvent(ev);
mDownPoint.set(ev.getX(), ev.getY());
mThresholdCrossed = false;
@@ -369,53 +387,10 @@ public class EdgeBackGestureHandler implements DisplayListener {
}
// forward touch
- mEdgePanel.handleTouch(ev);
-
- boolean isUp = action == MotionEvent.ACTION_UP;
- if (isUp) {
- boolean performAction = mEdgePanel.shouldTriggerBack();
- if (performAction) {
- // Perform back
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- }
- mOverviewProxyService.notifyBackAction(performAction, (int) mDownPoint.x,
- (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
- int backtype = performAction ? (mInRejectedExclusion
- ? StatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED :
- StatsLog.BACK_GESTURE__TYPE__COMPLETED) :
- StatsLog.BACK_GESTURE__TYPE__INCOMPLETE;
- StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
- (int) mDownPoint.y, mIsOnLeftEdge
- ? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
- StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
- }
- if (isUp || action == MotionEvent.ACTION_CANCEL) {
- mRegionSamplingHelper.stop();
- } else {
- updateSamplingRect();
- mRegionSamplingHelper.updateSamplingRect();
- }
+ mEdgeBackPlugin.onMotionEvent(ev);
}
}
- private void updateEdgePanelPosition(float touchY) {
- float position = touchY - mFingerOffset;
- position = Math.max(position, mMinArrowPosition);
- position = (position - mEdgePanelLp.height / 2.0f);
- mEdgePanelLp.y = MathUtils.constrain((int) position, 0, mDisplaySize.y);
- updateSamplingRect();
- }
-
- private void updateSamplingRect() {
- int top = mEdgePanelLp.y;
- int left = mIsOnLeftEdge ? mLeftInset : mDisplaySize.x - mRightInset - mEdgePanelLp.width;
- int right = left + mEdgePanelLp.width;
- int bottom = top + mEdgePanelLp.height;
- mSamplingRect.set(left, top, right, bottom);
- mEdgePanel.adjustRectToBoundingBox(mSamplingRect);
- }
-
@Override
public void onDisplayAdded(int displayId) { }
@@ -433,6 +408,9 @@ public class EdgeBackGestureHandler implements DisplayListener {
mContext.getSystemService(DisplayManager.class)
.getDisplay(mDisplayId)
.getRealSize(mDisplaySize);
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.setDisplaySize(mDisplaySize);
+ }
}
private void sendEvent(int action, int code) {
@@ -454,6 +432,9 @@ public class EdgeBackGestureHandler implements DisplayListener {
public void setInsets(int leftInset, int rightInset) {
mLeftInset = leftInset;
mRightInset = rightInset;
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.setInsets(leftInset, rightInset);
+ }
}
public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4e06c84c30be..6ac6d354cfff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -52,13 +52,9 @@ import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Stack;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* A implementation of HeadsUpManager for phone and car.
*/
-@Singleton
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
VisualStabilityManager.Callback, OnHeadsUpChangedListener,
ConfigurationController.ConfigurationListener, StateListener {
@@ -113,7 +109,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
///////////////////////////////////////////////////////////////////////////////////////////////
// Constructor:
- @Inject
public HeadsUpManagerPhone(@NonNull final Context context,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 7f31f3db4507..ac06d9de92a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -132,7 +132,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
if (mPickedChild != null && mTouchingHeadsUpView) {
// We may swallow this click if the heads up just came in.
if (mHeadsUpManager.shouldSwallowClick(
- mPickedChild.getStatusBarNotification().getKey())) {
+ mPickedChild.getEntry().getSbn().getKey())) {
endMotion();
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index 4f223c385eb4..23573095e037 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -19,34 +19,40 @@ package com.android.systemui.statusbar.phone;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Canvas;;
+import android.content.res.Resources;
+import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.os.VibrationEffect;
-import android.util.DisplayMetrics;
import android.util.MathUtils;
import android.view.ContextThemeWrapper;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import androidx.core.graphics.ColorUtils;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
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.NavigationEdgeBackPlugin;
import com.android.systemui.statusbar.VibratorHelper;
-import androidx.core.graphics.ColorUtils;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
+public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPlugin {
-public class NavigationBarEdgePanel extends View {
+ private static final String TAG = "NavigationBarEdgePanel";
private static final long COLOR_ANIMATION_DURATION_MS = 120;
private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80;
@@ -114,6 +120,7 @@ public class NavigationBarEdgePanel extends View {
private static final Interpolator RUBBER_BAND_INTERPOLATOR_APPEAR
= new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f);
+ private final WindowManager mWindowManager;
private final VibratorHelper mVibratorHelper;
/**
@@ -134,9 +141,14 @@ public class NavigationBarEdgePanel extends View {
* The minimum delta needed in movement for the arrow to change direction / stop triggering back
*/
private final float mMinDeltaForSwitch;
+ // The closest to y = 0 that the arrow will be displayed.
+ private int mMinArrowPosition;
+ // The amount the arrow is shifted to avoid the finger.
+ private int mFingerOffset;
private final float mSwipeThreshold;
private final Path mArrowPath = new Path();
+ private final Point mDisplaySize = new Point();
private final SpringAnimation mAngleAnimation;
private final SpringAnimation mTranslationAnimation;
@@ -158,6 +170,11 @@ public class NavigationBarEdgePanel extends View {
private int mArrowColorDark;
private int mProtectionColor;
private int mArrowColor;
+ private RegionSamplingHelper mRegionSamplingHelper;
+ private final Rect mSamplingRect = new Rect();
+ private WindowManager.LayoutParams mLayoutParams;
+ private int mLeftInset;
+ private int mRightInset;
/**
* True if the panel is currently on the left of the screen
@@ -242,10 +259,12 @@ public class NavigationBarEdgePanel extends View {
return object.getVerticalTranslation();
}
};
+ private BackCallback mBackCallback;
public NavigationBarEdgePanel(Context context) {
super(context);
+ mWindowManager = context.getSystemService(WindowManager.class);
mVibratorHelper = Dependency.get(VibratorHelper.class);
mDensity = context.getResources().getDisplayMetrics().density;
@@ -263,13 +282,10 @@ public class NavigationBarEdgePanel extends View {
mArrowColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mArrowColorAnimator.setDuration(COLOR_ANIMATION_DURATION_MS);
- mArrowColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int newColor = ColorUtils.blendARGB(mArrowStartColor, mArrowColor,
- animation.getAnimatedFraction());
- setCurrentArrowColor(newColor);
- }
+ mArrowColorAnimator.addUpdateListener(animation -> {
+ int newColor = ColorUtils.blendARGB(
+ mArrowStartColor, mArrowColor, animation.getAnimatedFraction());
+ setCurrentArrowColor(newColor);
});
mArrowDisappearAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
@@ -317,37 +333,79 @@ public class NavigationBarEdgePanel extends View {
mSwipeThreshold = context.getResources()
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
setVisibility(GONE);
+
+ mRegionSamplingHelper = new RegionSamplingHelper(this,
+ new RegionSamplingHelper.SamplingCallback() {
+ @Override
+ public void onRegionDarknessChanged(boolean isRegionDark) {
+ setIsDark(!isRegionDark, true /* animate */);
+ }
+
+ @Override
+ public Rect getSampledRegion(View sampledView) {
+ return mSamplingRect;
+ }
+ });
+ mRegionSamplingHelper.setWindowVisible(true);
}
@Override
- public boolean hasOverlappingRendering() {
- return false;
+ public void onDestroy() {
+ mWindowManager.removeView(this);
+ mRegionSamplingHelper.stop();
+ mRegionSamplingHelper = null;
}
- public boolean shouldTriggerBack() {
- return mTriggerBack;
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
}
- public void setIsDark(boolean isDark, boolean animate) {
+ private void setIsDark(boolean isDark, boolean animate) {
mIsDark = isDark;
updateIsDark(animate);
}
- public void setShowProtection(boolean showProtection) {
+ private void setShowProtection(boolean showProtection) {
mShowProtection = showProtection;
invalidate();
}
+ @Override
public void setIsLeftPanel(boolean isLeftPanel) {
mIsLeftPanel = isLeftPanel;
+ mLayoutParams.gravity = mIsLeftPanel
+ ? (Gravity.LEFT | Gravity.TOP)
+ : (Gravity.RIGHT | Gravity.TOP);
+ }
+
+ @Override
+ public void setInsets(int leftInset, int rightInset) {
+ mLeftInset = leftInset;
+ mRightInset = rightInset;
+ }
+
+ @Override
+ public void setDisplaySize(Point displaySize) {
+ mDisplaySize.set(displaySize.x, displaySize.y);
+ mScreenSize = Math.min(mDisplaySize.x, mDisplaySize.y);
+ }
+
+ @Override
+ public void setBackCallback(BackCallback callback) {
+ mBackCallback = callback;
+ }
+
+ @Override
+ public void setLayoutParams(WindowManager.LayoutParams layoutParams) {
+ mLayoutParams = layoutParams;
+ mWindowManager.addView(this, mLayoutParams);
}
/**
- * Adjust the rect to conform the the actual visible bounding box of the arrow.
- *
- * @param samplingRect the existing bounding box in screen coordinates, to be modified
+ * Adjusts the sampling rect to conform to the actual visible bounding box of the arrow.
*/
- public void adjustRectToBoundingBox(Rect samplingRect) {
+ private void adjustSamplingRectToBoundingBox() {
float translation = mDesiredTranslation;
if (!mTriggerBack) {
// Let's take the resting position and bounds as the sampling rect, since we are not
@@ -361,7 +419,7 @@ public class NavigationBarEdgePanel extends View {
}
}
float left = translation - mArrowThickness / 2.0f;
- left = mIsLeftPanel ? left : samplingRect.width() - left;
+ left = mIsLeftPanel ? left : mSamplingRect.width() - left;
// Let's calculate the position of the end based on the angle
float width = getStaticArrowWidth();
@@ -371,49 +429,49 @@ public class NavigationBarEdgePanel extends View {
}
float top = (getHeight() * 0.5f) + mDesiredVerticalTranslation - height / 2.0f;
- samplingRect.offset((int) left, (int) top);
- samplingRect.set(samplingRect.left, samplingRect.top,
- (int) (samplingRect.left + width),
- (int) (samplingRect.top + height));
+ mSamplingRect.offset((int) left, (int) top);
+ mSamplingRect.set(mSamplingRect.left, mSamplingRect.top,
+ (int) (mSamplingRect.left + width),
+ (int) (mSamplingRect.top + height));
+ mRegionSamplingHelper.updateSamplingRect();
}
- /**
- * Updates the UI based on the motion events passed in device co-ordinates
- */
- public void handleTouch(MotionEvent event) {
+ @Override
+ public void onMotionEvent(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN : {
+ case MotionEvent.ACTION_DOWN:
mDragSlopPassed = false;
resetOnDown();
mStartX = event.getX();
mStartY = event.getY();
setVisibility(VISIBLE);
+ updatePosition(event.getY());
+ mRegionSamplingHelper.start(mSamplingRect);
+ mWindowManager.updateViewLayout(this, mLayoutParams);
break;
- }
- case MotionEvent.ACTION_MOVE: {
+ case MotionEvent.ACTION_MOVE:
handleMoveEvent(event);
break;
- }
- // Fall through
case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL: {
if (mTriggerBack) {
triggerBack();
} else {
- if (mTranslationAnimation.isRunning()) {
- mTranslationAnimation.addEndListener(mSetGoneEndListener);
- } else {
- setVisibility(GONE);
- }
+ cancelBack();
}
+ mRegionSamplingHelper.stop();
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ cancelBack();
+ mRegionSamplingHelper.stop();
mVelocityTracker.recycle();
mVelocityTracker = null;
break;
- }
}
}
@@ -452,10 +510,10 @@ public class NavigationBarEdgePanel extends View {
}
private void loadDimens() {
- mArrowPaddingEnd = getContext().getResources().getDimensionPixelSize(
- R.dimen.navigation_edge_panel_padding);
- DisplayMetrics metrics = getResources().getDisplayMetrics();
- mScreenSize = Math.min(metrics.widthPixels, metrics.heightPixels);
+ Resources res = getResources();
+ mArrowPaddingEnd = res.getDimensionPixelSize(R.dimen.navigation_edge_panel_padding);
+ mMinArrowPosition = res.getDimensionPixelSize(R.dimen.navigation_edge_arrow_min_y);
+ mFingerOffset = res.getDimensionPixelSize(R.dimen.navigation_edge_finger_offset);
}
private void updateArrowDirection() {
@@ -531,6 +589,11 @@ public class NavigationBarEdgePanel extends View {
}
private void triggerBack() {
+ mBackCallback.triggerBack();
+
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
mVelocityTracker.computeCurrentVelocity(1000);
// Only do the extra translation if we're not already flinging
boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500;
@@ -573,7 +636,16 @@ public class NavigationBarEdgePanel extends View {
} else {
translationEnd.run();
}
+ }
+
+ private void cancelBack() {
+ mBackCallback.cancelBack();
+ if (mTranslationAnimation.isRunning()) {
+ mTranslationAnimation.addEndListener(mSetGoneEndListener);
+ } else {
+ setVisibility(GONE);
+ }
}
private void resetOnDown() {
@@ -680,6 +752,24 @@ public class NavigationBarEdgePanel extends View {
float verticalTranslation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
* maxYOffset * Math.signum(yOffset);
setDesiredVerticalTransition(verticalTranslation, true /* animated */);
+ updateSamplingRect();
+ }
+
+ private void updatePosition(float touchY) {
+ float position = touchY - mFingerOffset;
+ position = Math.max(position, mMinArrowPosition);
+ position -= mLayoutParams.height / 2.0f;
+ mLayoutParams.y = MathUtils.constrain((int) position, 0, mDisplaySize.y);
+ updateSamplingRect();
+ }
+
+ private void updateSamplingRect() {
+ int top = mLayoutParams.y;
+ int left = mIsLeftPanel ? mLeftInset : mDisplaySize.x - mRightInset - mLayoutParams.width;
+ int right = left + mLayoutParams.width;
+ int bottom = top + mLayoutParams.height;
+ mSamplingRect.set(left, top, right, bottom);
+ adjustSamplingRectToBoundingBox();
}
private void setDesiredVerticalTransition(float verticalTranslation, boolean animated) {
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 d3c79404863e..5a1b20dd1e71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -72,6 +72,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsOnboarding;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -96,6 +97,7 @@ public class NavigationBarView extends FrameLayout implements
private final RegionSamplingHelper mRegionSamplingHelper;
private final int mNavColorSampleMargin;
private final SysUiState mSysUiFlagContainer;
+ private final PluginManager mPluginManager;
View mCurrentView = null;
private View mVertical;
@@ -272,6 +274,7 @@ public class NavigationBarView extends FrameLayout implements
boolean isGesturalMode = isGesturalMode(mNavBarMode);
mSysUiFlagContainer = Dependency.get(SysUiState.class);
+ mPluginManager = Dependency.get(PluginManager.class);
// Set up the context group of buttons
mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
@@ -315,8 +318,8 @@ public class NavigationBarView extends FrameLayout implements
mNavColorSampleMargin = getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
- mEdgeBackGestureHandler =
- new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiFlagContainer);
+ mEdgeBackGestureHandler = new EdgeBackGestureHandler(
+ context, mOverviewProxyService, mSysUiFlagContainer, mPluginManager);
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@Override
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 6176cff82f6f..35407c6df690 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -986,7 +986,7 @@ public class NotificationPanelView extends PanelView implements
}
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
boolean suppressedSummary = mGroupManager != null
- && mGroupManager.isSummaryOfSuppressedGroup(row.getStatusBarNotification());
+ && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn());
if (suppressedSummary) {
continue;
}
@@ -3562,7 +3562,7 @@ public class NotificationPanelView extends PanelView implements
private void updateShowEmptyShadeView() {
boolean showEmptyShadeView =
- mBarState != StatusBarState.KEYGUARD && mEntryManager.hasActiveNotifications();
+ mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
showEmptyShadeView(showEmptyShadeView);
}
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 adea8c6aa014..fafdf6aa1128 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -148,7 +148,6 @@ import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.doze.DozeHost;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -198,7 +197,6 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NewNotifPipeline;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationClicker;
@@ -210,6 +208,7 @@ import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -222,7 +221,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -795,7 +793,6 @@ public class StatusBar extends SystemUI implements DemoMode,
R.bool.config_vibrateOnIconAnimation);
DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
- putComponent(StatusBar.class, this);
// start old BaseStatusBar.start().
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
@@ -897,7 +894,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mDozeServiceHost.initialize(this, mNotificationIconAreaController,
mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager,
mNotificationPanel, mAmbientIndicationContainer);
- putComponent(DozeHost.class, mDozeServiceHost);
Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
@@ -1053,7 +1049,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mGroupManager.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
- putComponent(HeadsUpManager.class, mHeadsUpManager);
createNavigationBar(result);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 60e93857e9da..88f1c63f627a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -56,12 +56,12 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NewNotifPipeline;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.policy.BatteryController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 1cf43cc310ba..b340813b42f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -40,6 +40,7 @@ import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.view.RemoteAnimationAdapter;
+import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
@@ -75,7 +76,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
*/
public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
- private static final String TAG = "NotificationClickHandler";
+ private static final String TAG = "NotifActivityStarter";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final AssistManager mAssistManager;
@@ -197,8 +198,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
return;
}
- final String notificationKey = sbn.getKey();
-
boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble;
final boolean afterKeyguardGone = isActivityIntent
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
@@ -209,7 +208,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mLockscreenUserManager.getCurrentUserId());
ActivityStarter.OnDismissAction postKeyguardAction =
() -> handleNotificationClickAfterKeyguardDismissed(
- sbn, row, controller, intent, notificationKey,
+ sbn, row, controller, intent,
isActivityIntent, wasOccluded, showOverLockscreen);
if (showOverLockscreen) {
mIsCollapsingToShowActivityOverLockscreen = true;
@@ -225,12 +224,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
ExpandableNotificationRow row,
RemoteInputController controller,
PendingIntent intent,
- String notificationKey,
boolean isActivityIntent,
boolean wasOccluded,
boolean showOverLockscreen) {
// TODO: Some of this code may be able to move to NotificationEntryManager.
- if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
+ if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(sbn.getKey())) {
// Release the HUN notification to the shade.
if (mPresenter.isPresenterFullyCollapsed()) {
@@ -252,7 +250,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}
final StatusBarNotification parentToCancelFinal = parentToCancel;
final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
- sbn, row, controller, intent, notificationKey,
+ sbn, row, controller, intent,
isActivityIntent, wasOccluded, parentToCancelFinal);
if (showOverLockscreen) {
@@ -273,10 +271,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
ExpandableNotificationRow row,
RemoteInputController controller,
PendingIntent intent,
- String notificationKey,
boolean isActivityIntent,
boolean wasOccluded,
StatusBarNotification parentToCancelFinal) {
+ String notificationKey = sbn.getKey();
try {
// The intent we are sending is for the application, which
// won't have permission to immediately start an activity after
@@ -310,7 +308,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (!TextUtils.isEmpty(entry.remoteInputText)) {
remoteInputText = entry.remoteInputText;
}
- if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.getKey())) {
+ if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(notificationKey)) {
fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
remoteInputText.toString());
}
@@ -326,14 +324,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
collapseOnMainThread();
}
- //TODO(b/144306683): prove that this `activeEntry` is the same as `entry` above and simplify
- // this call stack
- NotificationEntry activeEntry =
- mEntryManager.getActiveNotificationUnfiltered(notificationKey);
final int count = mEntryManager.getActiveNotificationsCount();
- final int rank = activeEntry != null ? activeEntry.getRanking().getRank() : 0;
+ final int rank = entry.getRanking().getRank();
NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(activeEntry);
+ NotificationLogger.getNotificationLocation(entry);
final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
rank, count, true, location);
try {
@@ -365,7 +359,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}
private void startNotificationIntent(PendingIntent intent, Intent fillInIntent,
- ExpandableNotificationRow row, boolean wasOccluded, boolean isActivityIntent) {
+ View row, boolean wasOccluded, boolean isActivityIntent) {
RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
wasOccluded);
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index b5a7847a2747..322b23fa50ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -175,7 +175,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
} else {
if (row.isChildInGroup() && !row.areChildrenExpanded()) {
// The group isn't expanded, let's make sure it's visible!
- mGroupManager.toggleGroupExpansion(row.getStatusBarNotification());
+ mGroupManager.toggleGroupExpansion(row.getEntry().getSbn());
}
row.setUserExpanded(true);
row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
index c1328ec2a060..6d6a1fe4d0b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -188,6 +188,7 @@ public class StatusBarWindowViewController {
boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL;
+ boolean expandingBelowNotch = mExpandingBelowNotch;
if (isUp || isCancel) {
mExpandingBelowNotch = false;
}
@@ -236,8 +237,9 @@ public class StatusBarWindowViewController {
// regular view bounds.
if (isDown && ev.getY() >= mView.getBottom()) {
mExpandingBelowNotch = true;
+ expandingBelowNotch = true;
}
- if (mExpandingBelowNotch) {
+ if (expandingBelowNotch) {
return mStatusBarView.dispatchTouchEvent(ev);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 7587c8ca64fe..6331a2d6db1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -111,8 +111,8 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
if (mWifiManager != null) {
if (mListening) {
if (mCallbacks.size() == 1) {
- mWifiManager.registerSoftApCallback(this,
- new HandlerExecutor(mMainHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mMainHandler),
+ this);
} else {
// mWifiManager#registerSoftApCallback triggers a call to
// onConnectedClientsChanged on the Main Handler. In order to always update
@@ -146,7 +146,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
if (mListening || !listening) return;
mListening = true;
if (mCallbacks.size() >= 1) {
- mWifiManager.registerSoftApCallback(this, new HandlerExecutor(mMainHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mMainHandler), this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 379cf1f273e5..39de0f3f5a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -50,8 +50,6 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
@Override
public void start() {
- putComponent(TvStatusBar.class, this);
-
final IStatusBarService barService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mCommandQueue.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index 49ada1a5e41e..a60ca6201419 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -158,7 +158,7 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference
String demoTime = "1010"; // 10:10, a classic choice of horologists
try {
- String[] versionParts = android.os.Build.VERSION.RELEASE_OR_CODENAME.split("\\.");
+ String[] versionParts = android.os.Build.VERSION.RELEASE.split("\\.");
int majorVersion = Integer.valueOf(versionParts[0]);
demoTime = String.format("%02d00", majorVersion % 24);
} catch (IllegalArgumentException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java b/packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java
new file mode 100644
index 000000000000..4316df1ced04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.time;
+
+/**
+ * Testable wrapper around {@link android.os.SystemClock}.
+ *
+ * Dagger can inject this wrapper into your classes. The implementation just proxies calls to the
+ * real SystemClock.
+ *
+ * In tests, pass an instance of FakeSystemClock, which allows you to control the values returned by
+ * the various getters below.
+ */
+public interface SystemClock {
+ /** @see android.os.SystemClock#uptimeMillis() */
+ long uptimeMillis();
+
+ /** @see android.os.SystemClock#elapsedRealtime() */
+ long elapsedRealtime();
+
+ /** @see android.os.SystemClock#elapsedRealtimeNanos() */
+ long elapsedRealtimeNanos();
+
+ /** @see android.os.SystemClock#currentThreadTimeMillis() */
+ long currentThreadTimeMillis();
+
+ /** @see android.os.SystemClock#currentThreadTimeMicro() */
+ long currentThreadTimeMicro();
+
+ /** @see android.os.SystemClock#currentTimeMicro() */
+ long currentTimeMicro();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java b/packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java
new file mode 100644
index 000000000000..532ea050bfdd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.time;
+
+import javax.inject.Inject;
+
+/** Default implementation of {@link SystemClock}. */
+public class SystemClockImpl implements SystemClock {
+ @Inject
+ public SystemClockImpl() {}
+
+ @Override
+ public long uptimeMillis() {
+ return android.os.SystemClock.uptimeMillis();
+ }
+
+ @Override
+ public long elapsedRealtime() {
+ return android.os.SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public long elapsedRealtimeNanos() {
+ return android.os.SystemClock.elapsedRealtimeNanos();
+ }
+
+ @Override
+ public long currentThreadTimeMillis() {
+ return android.os.SystemClock.currentThreadTimeMillis();
+ }
+
+ @Override
+ public long currentThreadTimeMicro() {
+ return android.os.SystemClock.currentThreadTimeMicro();
+ }
+
+ @Override
+ public long currentTimeMicro() {
+ return android.os.SystemClock.currentTimeMicro();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 1e9000b76c3f..ba264c0ae6db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -136,7 +136,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
}
};
- mScreenDecorations.mComponents = mContext.getComponents();
reset(mTunerService);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index f792d7d11e15..d847208bc4f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -17,13 +17,9 @@ package com.android.systemui;
import android.content.Context;
import android.testing.LeakCheck;
import android.testing.TestableContext;
-import android.util.ArrayMap;
import android.view.Display;
-public class SysuiTestableContext extends TestableContext implements SysUiServiceProvider {
-
- private ArrayMap<Class<?>, Object> mComponents;
-
+public class SysuiTestableContext extends TestableContext {
public SysuiTestableContext(Context base) {
super(base);
setTheme(R.style.Theme_SystemUI);
@@ -34,21 +30,6 @@ public class SysuiTestableContext extends TestableContext implements SysUiServic
setTheme(R.style.Theme_SystemUI);
}
- public ArrayMap<Class<?>, Object> getComponents() {
- if (mComponents == null) mComponents = new ArrayMap<>();
- return mComponents;
- }
-
- @SuppressWarnings("unchecked")
- public <T> T getComponent(Class<T> interfaceType) {
- return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
- }
-
- public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
- if (mComponents == null) mComponents = new ArrayMap<>();
- mComponents.put(interfaceType, component);
- }
-
@Override
public Context createDisplayContext(Display display) {
if (display == null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 486fa12e0577..f6375fce8b4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -53,7 +53,6 @@ import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBar;
import org.junit.Before;
import org.junit.Test;
@@ -88,8 +87,6 @@ public class AuthControllerTest extends SysuiTestCase {
TestableContext context = spy(mContext);
- mContext.putComponent(StatusBar.class, mock(StatusBar.class));
-
when(context.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(true);
@@ -104,7 +101,6 @@ public class AuthControllerTest extends SysuiTestCase {
mAuthController = new TestableAuthController(
context, mock(CommandQueue.class), new MockInjector());
- mAuthController.mComponents = mContext.getComponents();
mAuthController.start();
}
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 47b35fdc6dc1..e8f19238fdb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -687,7 +687,6 @@ public class PowerUITest extends SysuiTestCase {
private void createPowerUi() {
mPowerUI = new PowerUI(mContext, mBroadcastDispatcher, mStatusBarLazy);
- mPowerUI.mComponents = mContext.getComponents();
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java
index fcfdd11a1906..d18b16be33d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager.Importance;
+import android.content.Context;
import android.os.UserHandle;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
@@ -92,6 +93,10 @@ public class NotificationEntryBuilder {
return this;
}
+ public Notification.Builder modifyNotification(Context context) {
+ return mSbnBuilder.modifyNotification(context);
+ }
+
public NotificationEntryBuilder setUser(UserHandle user) {
mSbnBuilder.setUser(user);
return this;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 99c94ac7dec2..46a8dad8c382 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -117,7 +117,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
private NotificationEntry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
NotificationEntry entry = new NotificationEntryBuilder()
- .setSbn(row.getStatusBarNotification())
+ .setSbn(row.getEntry().getSbn())
.build();
entry.setRow(row);
return entry;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
index fe117fe443a6..94b3ac49ab09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar;
+import android.annotation.Nullable;
import android.app.Notification;
+import android.content.Context;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -32,7 +34,8 @@ public class SbnBuilder {
private String mTag;
private int mUid;
private int mInitialPid;
- private Notification mNotification = new Notification();
+ @Nullable private Notification mNotification;
+ @Nullable private Notification.Builder mNotificationBuilder;
private Notification.BubbleMetadata mBubbleMetadata;
private UserHandle mUser = UserHandle.of(0);
private String mOverrideGroupKey;
@@ -55,9 +58,19 @@ public class SbnBuilder {
}
public StatusBarNotification build() {
+ Notification notification;
+ if (mNotificationBuilder != null) {
+ notification = mNotificationBuilder.build();
+ } else if (mNotification != null) {
+ notification = mNotification;
+ } else {
+ notification = new Notification();
+ }
+
if (mBubbleMetadata != null) {
- mNotification.setBubbleMetadata(mBubbleMetadata);
+ notification.setBubbleMetadata(mBubbleMetadata);
}
+
return new StatusBarNotification(
mPkg,
mOpPkg,
@@ -65,7 +78,7 @@ public class SbnBuilder {
mTag,
mUid,
mInitialPid,
- mNotification,
+ notification,
mUser,
mOverrideGroupKey,
mPostTime);
@@ -106,6 +119,17 @@ public class SbnBuilder {
return this;
}
+ public Notification.Builder modifyNotification(Context context) {
+ if (mNotification != null) {
+ mNotificationBuilder = new Notification.Builder(context, mNotification);
+ mNotification = null;
+ } else if (mNotificationBuilder == null) {
+ mNotificationBuilder = new Notification.Builder(context);
+ }
+
+ return mNotificationBuilder;
+ }
+
public SbnBuilder setUser(UserHandle user) {
mUser = user;
return this;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
new file mode 100644
index 000000000000..a25af84ca28e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpList;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArrayMap;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl.OnRenderListListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotifListBuilderImplTest extends SysuiTestCase {
+
+ private NotifListBuilderImpl mListBuilder;
+ private FakeSystemClock mSystemClock = new FakeSystemClock();
+
+ @Mock private NotifCollection mNotifCollection;
+ @Spy private OnBeforeTransformGroupsListener mOnBeforeTransformGroupsListener;
+ @Spy private OnBeforeSortListener mOnBeforeSortListener;
+ @Spy private OnBeforeRenderListListener mOnBeforeRenderListListener;
+ @Spy private OnRenderListListener mOnRenderListListener = list -> mBuiltList = list;
+
+ @Captor private ArgumentCaptor<CollectionReadyForBuildListener> mBuildListenerCaptor;
+
+ private CollectionReadyForBuildListener mReadyForBuildListener;
+ private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>();
+ private List<NotificationEntry> mEntrySet = new ArrayList<>();
+ private List<ListEntry> mBuiltList;
+
+ private Map<String, Integer> mNextIdMap = new ArrayMap<>();
+ private int mNextRank = 0;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
+ mListBuilder = new NotifListBuilderImpl(mSystemClock);
+ mListBuilder.setOnRenderListListener(mOnRenderListListener);
+
+ mListBuilder.attach(mNotifCollection);
+
+ Mockito.verify(mNotifCollection).setBuildListener(mBuildListenerCaptor.capture());
+ mReadyForBuildListener = checkNotNull(mBuildListenerCaptor.getValue());
+ }
+
+ @Test
+ public void testNotifsAreSortedByRankAndWhen() {
+ // GIVEN a simple pipeline
+
+ // WHEN a series of notifs with jumbled ranks are added
+ addNotif(0, PACKAGE_1).setRank(2);
+ addNotif(1, PACKAGE_2).setRank(4).modifyNotification(mContext).setWhen(22);
+ addNotif(2, PACKAGE_3).setRank(4).modifyNotification(mContext).setWhen(33);
+ addNotif(3, PACKAGE_3).setRank(3);
+ addNotif(4, PACKAGE_5).setRank(4).modifyNotification(mContext).setWhen(11);
+ addNotif(5, PACKAGE_3).setRank(1);
+ addNotif(6, PACKAGE_1).setRank(0);
+ dispatchBuild();
+
+ // The final output is sorted based first by rank and then by when
+ verifyBuiltList(
+ notif(6),
+ notif(5),
+ notif(0),
+ notif(3),
+ notif(2),
+ notif(1),
+ notif(4)
+ );
+ }
+
+ @Test
+ public void testNotifsAreGrouped() {
+ // GIVEN a simple pipeline
+
+ // WHEN a group is added
+ addGroupChild(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+ addGroupSummary(3, PACKAGE_1, GROUP_1);
+ dispatchBuild();
+
+ // THEN the notifs are grouped together
+ verifyBuiltList(
+ group(
+ summary(3),
+ child(0),
+ child(1),
+ child(2)
+ )
+ );
+ }
+
+ @Test
+ public void testNotifsWithDifferentGroupKeysAreGrouped() {
+ // GIVEN a simple pipeline
+
+ // WHEN a package posts two different groups
+ addGroupChild(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_2);
+ addGroupSummary(2, PACKAGE_1, GROUP_2);
+ addGroupChild(3, PACKAGE_1, GROUP_2);
+ addGroupChild(4, PACKAGE_1, GROUP_1);
+ addGroupChild(5, PACKAGE_1, GROUP_2);
+ addGroupChild(6, PACKAGE_1, GROUP_1);
+ addGroupSummary(7, PACKAGE_1, GROUP_1);
+ dispatchBuild();
+
+ // THEN the groups are separated separately
+ verifyBuiltList(
+ group(
+ summary(2),
+ child(1),
+ child(3),
+ child(5)
+ ),
+ group(
+ summary(7),
+ child(0),
+ child(4),
+ child(6)
+ )
+ );
+ }
+
+ @Test
+ public void testNotifsNotifChildrenAreSorted() {
+ // GIVEN a simple pipeline
+
+ // WHEN a group is added
+ addGroupChild(0, PACKAGE_1, GROUP_1).setRank(4);
+ addGroupChild(1, PACKAGE_1, GROUP_1).setRank(2)
+ .modifyNotification(mContext).setWhen(11);
+ addGroupChild(2, PACKAGE_1, GROUP_1).setRank(1);
+ addGroupChild(3, PACKAGE_1, GROUP_1).setRank(2)
+ .modifyNotification(mContext).setWhen(33);
+ addGroupChild(4, PACKAGE_1, GROUP_1).setRank(2)
+ .modifyNotification(mContext).setWhen(22);
+ addGroupChild(5, PACKAGE_1, GROUP_1).setRank(0);
+ addGroupSummary(6, PACKAGE_1, GROUP_1).setRank(3);
+ dispatchBuild();
+
+ // THEN the children are sorted by rank and when
+ verifyBuiltList(
+ group(
+ summary(6),
+ child(5),
+ child(2),
+ child(3),
+ child(4),
+ child(1),
+ child(0)
+ )
+ );
+ }
+
+ @Test
+ public void testDuplicateGroupSummariesAreDiscarded() {
+ // GIVEN a simple pipeline
+
+ // WHEN a group with multiple summaries is added
+ addNotif(0, PACKAGE_3);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+ addGroupSummary(3, PACKAGE_1, GROUP_1).setPostTime(22);
+ addGroupSummary(4, PACKAGE_1, GROUP_1).setPostTime(33);
+ addNotif(5, PACKAGE_2);
+ addGroupSummary(6, PACKAGE_1, GROUP_1).setPostTime(11);
+ addGroupChild(7, PACKAGE_1, GROUP_1);
+ dispatchBuild();
+
+ // THEN only most recent summary is used
+ verifyBuiltList(
+ notif(0),
+ group(
+ summary(4),
+ child(1),
+ child(2),
+ child(7)
+ ),
+ notif(5)
+ );
+
+ // THEN the extra summaries have their parents set to null
+ assertNull(mEntrySet.get(3).getParent());
+ assertNull(mEntrySet.get(6).getParent());
+ }
+
+ @Test
+ public void testGroupsWithNoSummaryAreUngrouped() {
+ // GIVEN a group with no summary
+ addNotif(0, PACKAGE_2);
+ addGroupChild(1, PACKAGE_4, GROUP_2);
+ addGroupChild(2, PACKAGE_4, GROUP_2);
+ addGroupChild(3, PACKAGE_4, GROUP_2);
+ addGroupChild(4, PACKAGE_4, GROUP_2);
+
+ // WHEN we build the list
+ dispatchBuild();
+
+ // THEN the children aren't grouped
+ verifyBuiltList(
+ notif(0),
+ notif(1),
+ notif(2),
+ notif(3),
+ notif(4)
+ );
+ }
+
+ @Test
+ public void testGroupsWithNoChildrenAreUngrouped() {
+ // GIVEN a group with a summary but no children
+ addGroupSummary(0, PACKAGE_5, GROUP_1);
+ addNotif(1, PACKAGE_5);
+ addNotif(2, PACKAGE_1);
+
+ // WHEN we build the list
+ dispatchBuild();
+
+ // THEN the summary isn't grouped but is still added to the final list
+ verifyBuiltList(
+ notif(0),
+ notif(1),
+ notif(2)
+ );
+ }
+
+ @Test
+ public void testGroupsWithTooFewChildrenAreSplitUp() {
+ // GIVEN a group with one child
+ addGroupChild(0, PACKAGE_2, GROUP_1);
+ addGroupSummary(1, PACKAGE_2, GROUP_1);
+
+ // WHEN we build the list
+ dispatchBuild();
+
+ // THEN the child is added at top level and the summary is discarded
+ verifyBuiltList(
+ notif(0)
+ );
+
+ assertNull(mEntrySet.get(1).getParent());
+ }
+
+ @Test
+ public void testGroupsWhoLoseChildrenMidPipelineAreSplitUp() {
+ // GIVEN a group with two children
+ addGroupChild(0, PACKAGE_2, GROUP_1);
+ addGroupSummary(1, PACKAGE_2, GROUP_1);
+ addGroupChild(2, PACKAGE_2, GROUP_1);
+
+ // GIVEN a promoter that will promote one of children to top level
+ mListBuilder.addPromoter(new IdPromoter(0));
+
+ // WHEN we build the list
+ dispatchBuild();
+
+ // THEN both children end up at top level (because group is now too small)
+ verifyBuiltList(
+ notif(0),
+ notif(2)
+ );
+
+ // THEN the summary is discarded
+ assertNull(mEntrySet.get(1).getParent());
+ }
+
+ @Test
+ public void testPreviousParentsAreSetProperly() {
+ // GIVEN a notification that is initially added to the list
+ PackageFilter filter = new PackageFilter(PACKAGE_2);
+ filter.setEnabled(false);
+ mListBuilder.addFilter(filter);
+
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ dispatchBuild();
+
+ // WHEN it is suddenly filtered out
+ filter.setEnabled(true);
+ dispatchBuild();
+
+ // THEN its previous parent indicates that it used to be added
+ assertNull(mEntrySet.get(1).getParent());
+ assertEquals(GroupEntry.ROOT_ENTRY, mEntrySet.get(1).getPreviousParent());
+ }
+
+ @Test
+ public void testThatAnnulledGroupsAndSummariesAreProperlyRolledBack() {
+ // GIVEN a registered transform groups listener
+ RecordingOnBeforeTransformGroupsListener listener =
+ new RecordingOnBeforeTransformGroupsListener();
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // GIVEN a malformed group that will be dismantled
+ addGroupChild(0, PACKAGE_2, GROUP_1);
+ addGroupSummary(1, PACKAGE_2, GROUP_1);
+ addNotif(2, PACKAGE_1);
+
+ // WHEN we build the list
+ dispatchBuild();
+
+ // THEN only the child appears in the final list
+ verifyBuiltList(
+ notif(0),
+ notif(2)
+ );
+
+ // THEN the list of newly visible entries doesn't contain the summary or the group
+ assertEquals(
+ Arrays.asList(
+ mEntrySet.get(0),
+ mEntrySet.get(2)),
+ listener.newlyVisibleEntries
+ );
+
+ // THEN the summary has a null parent and an unset firstAddedIteration
+ assertNull(mEntrySet.get(1).getParent());
+ assertEquals(-1, mEntrySet.get(1).mFirstAddedIteration);
+ }
+
+ @Test
+ public void testNotifsAreFiltered() {
+ // GIVEN a NotifFilter that filters out a specific package
+ NotifFilter filter1 = spy(new PackageFilter(PACKAGE_2));
+ mListBuilder.addFilter(filter1);
+
+ // WHEN the pipeline is kicked off on a list of notifs
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ addNotif(3, PACKAGE_2);
+ dispatchBuild();
+
+ // THEN the filter is called on each notif in the original set
+ verify(filter1).shouldFilterOut(eq(mEntrySet.get(0)), anyLong());
+ verify(filter1).shouldFilterOut(eq(mEntrySet.get(1)), anyLong());
+ verify(filter1).shouldFilterOut(eq(mEntrySet.get(2)), anyLong());
+ verify(filter1).shouldFilterOut(eq(mEntrySet.get(3)), anyLong());
+
+ // THEN the final list doesn't contain any filtered-out notifs
+ verifyBuiltList(
+ notif(0),
+ notif(2)
+ );
+
+ // THEN each filtered notif records the filter that did it
+ assertEquals(filter1, mEntrySet.get(1).mExcludingFilter);
+ assertEquals(filter1, mEntrySet.get(3).mExcludingFilter);
+ }
+
+ @Test
+ public void testNotifFiltersCanBePreempted() {
+ // GIVEN two notif filters
+ NotifFilter filter1 = spy(new PackageFilter(PACKAGE_2));
+ NotifFilter filter2 = spy(new PackageFilter(PACKAGE_5));
+ mListBuilder.addFilter(filter1);
+ mListBuilder.addFilter(filter2);
+
+ // WHEN the pipeline is kicked off on a list of notifs
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_5);
+ dispatchBuild();
+
+ // THEN both filters are called on the first notif but the second filter is never called
+ // on the already-filtered second notif
+ verify(filter1).shouldFilterOut(eq(mEntrySet.get(0)), anyLong());
+ verify(filter1).shouldFilterOut(eq(mEntrySet.get(1)), anyLong());
+ verify(filter1).shouldFilterOut(eq(mEntrySet.get(2)), anyLong());
+ verify(filter2).shouldFilterOut(eq(mEntrySet.get(0)), anyLong());
+ verify(filter2).shouldFilterOut(eq(mEntrySet.get(2)), anyLong());
+
+ // THEN the final list doesn't contain any filtered-out notifs
+ verifyBuiltList(
+ notif(0)
+ );
+
+ // THEN each filtered notif records the filter that did it
+ assertEquals(filter1, mEntrySet.get(1).mExcludingFilter);
+ assertEquals(filter2, mEntrySet.get(2).mExcludingFilter);
+ }
+
+ @Test
+ public void testNotifsArePromoted() {
+ // GIVEN a NotifPromoter that promotes certain notif IDs
+ NotifPromoter promoter = spy(new IdPromoter(1, 2));
+ mListBuilder.addPromoter(promoter);
+
+ // WHEN the pipeline is kicked off
+ addNotif(0, PACKAGE_1);
+ addGroupChild(1, PACKAGE_2, GROUP_1);
+ addGroupChild(2, PACKAGE_2, GROUP_1);
+ addGroupChild(3, PACKAGE_2, GROUP_1);
+ addGroupChild(4, PACKAGE_2, GROUP_1);
+ addGroupSummary(5, PACKAGE_2, GROUP_1);
+ addNotif(6, PACKAGE_3);
+ dispatchBuild();
+
+ // THEN the filter is called on each group child
+ verify(promoter).shouldPromoteToTopLevel(mEntrySet.get(1));
+ verify(promoter).shouldPromoteToTopLevel(mEntrySet.get(2));
+ verify(promoter).shouldPromoteToTopLevel(mEntrySet.get(3));
+ verify(promoter).shouldPromoteToTopLevel(mEntrySet.get(4));
+
+ // THEN the final list contains the promoted entries at top level
+ verifyBuiltList(
+ notif(0),
+ notif(2),
+ notif(3),
+ group(
+ summary(5),
+ child(1),
+ child(4)),
+ notif(6)
+ );
+
+ // THEN each promoted notif records the promoter that did it
+ assertEquals(promoter, mEntrySet.get(2).mNotifPromoter);
+ assertEquals(promoter, mEntrySet.get(3).mNotifPromoter);
+ }
+
+ @Test
+ public void testNotifPromotersCanBePreempted() {
+ // GIVEN two notif promoters
+ NotifPromoter promoter1 = spy(new IdPromoter(1));
+ NotifPromoter promoter2 = spy(new IdPromoter(2));
+ mListBuilder.addPromoter(promoter1);
+ mListBuilder.addPromoter(promoter2);
+
+ // WHEN the pipeline is kicked off on some notifs and a group
+ addNotif(0, PACKAGE_1);
+ addGroupChild(1, PACKAGE_2, GROUP_1);
+ addGroupChild(2, PACKAGE_2, GROUP_1);
+ addGroupChild(3, PACKAGE_2, GROUP_1);
+ addGroupSummary(4, PACKAGE_2, GROUP_1);
+ addNotif(5, PACKAGE_3);
+ dispatchBuild();
+
+ // THEN both promoters are called on each child, except for children that a previous
+ // promoter has already promoted
+ verify(promoter1).shouldPromoteToTopLevel(mEntrySet.get(1));
+ verify(promoter1).shouldPromoteToTopLevel(mEntrySet.get(2));
+ verify(promoter1).shouldPromoteToTopLevel(mEntrySet.get(3));
+
+ verify(promoter2).shouldPromoteToTopLevel(mEntrySet.get(1));
+ verify(promoter2).shouldPromoteToTopLevel(mEntrySet.get(3));
+
+ // THEN each promoter is recorded on each notif it promoted
+ assertEquals(promoter1, mEntrySet.get(2).mNotifPromoter);
+ assertEquals(promoter2, mEntrySet.get(3).mNotifPromoter);
+ }
+
+ @Test
+ public void testNotifsAreSectioned() {
+ // GIVEN a filter that removes all PACKAGE_4 notifs and a SectionsProvider that divides
+ // notifs based on package name
+ mListBuilder.addFilter(new PackageFilter(PACKAGE_4));
+ final SectionsProvider sectionsProvider = spy(new PackageSectioner());
+ mListBuilder.setSectionsProvider(sectionsProvider);
+
+ // WHEN we build a list with different packages
+ addNotif(0, PACKAGE_4);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_1);
+ addNotif(3, PACKAGE_3);
+ addGroupSummary(4, PACKAGE_2, GROUP_1);
+ addGroupChild(5, PACKAGE_2, GROUP_1);
+ addGroupChild(6, PACKAGE_2, GROUP_1);
+ addNotif(7, PACKAGE_1);
+ addNotif(8, PACKAGE_2);
+ addNotif(9, PACKAGE_5);
+ addNotif(10, PACKAGE_4);
+ dispatchBuild();
+
+ // THEN the list is sorted according to section
+ verifyBuiltList(
+ notif(2),
+ notif(7),
+ notif(1),
+ group(
+ summary(4),
+ child(5),
+ child(6)
+ ),
+ notif(8),
+ notif(3),
+ notif(9)
+ );
+
+ // THEN the sections provider is called on all top level elements (but no children and no
+ // entries that were filtered out)
+ verify(sectionsProvider).getSection(mEntrySet.get(1));
+ verify(sectionsProvider).getSection(mEntrySet.get(2));
+ verify(sectionsProvider).getSection(mEntrySet.get(3));
+ verify(sectionsProvider).getSection(mEntrySet.get(7));
+ verify(sectionsProvider).getSection(mEntrySet.get(8));
+ verify(sectionsProvider).getSection(mEntrySet.get(9));
+ verify(sectionsProvider).getSection(mBuiltList.get(3));
+ }
+
+ @Test
+ public void testThatNotifComparatorsAreCalled() {
+ // GIVEN a set of comparators that care about specific packages
+ mListBuilder.setComparators(Arrays.asList(
+ new HypeComparator(PACKAGE_4),
+ new HypeComparator(PACKAGE_1, PACKAGE_3),
+ new HypeComparator(PACKAGE_2)
+ ));
+
+ // WHEN the pipeline is kicked off on a bunch of notifications
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_5);
+ addNotif(2, PACKAGE_3);
+ addNotif(3, PACKAGE_4);
+ addNotif(4, PACKAGE_2);
+ dispatchBuild();
+
+ // THEN the notifs are sorted according to the hierarchy of comparators
+ verifyBuiltList(
+ notif(3),
+ notif(0),
+ notif(2),
+ notif(4),
+ notif(1)
+ );
+ }
+
+ @Test
+ public void testListenersAndPluggablesAreFiredInOrder() {
+ // GIVEN a bunch of registered listeners and pluggables
+ NotifFilter filter = spy(new PackageFilter(PACKAGE_1));
+ NotifPromoter promoter = spy(new IdPromoter(3));
+ PackageSectioner sectioner = spy(new PackageSectioner());
+ NotifComparator comparator = spy(new HypeComparator(PACKAGE_4));
+ mListBuilder.addFilter(filter);
+ mListBuilder.addOnBeforeTransformGroupsListener(mOnBeforeTransformGroupsListener);
+ mListBuilder.addPromoter(promoter);
+ mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener);
+ mListBuilder.setComparators(Collections.singletonList(comparator));
+ mListBuilder.setSectionsProvider(sectioner);
+ mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener);
+
+ // WHEN a few new notifs are added
+ addNotif(0, PACKAGE_1);
+ addGroupSummary(1, PACKAGE_2, GROUP_1);
+ addGroupChild(2, PACKAGE_2, GROUP_1);
+ addGroupChild(3, PACKAGE_2, GROUP_1);
+ addNotif(4, PACKAGE_5);
+ addNotif(5, PACKAGE_5);
+ addNotif(6, PACKAGE_4);
+ dispatchBuild();
+
+ // THEN the pluggables and listeners are called in order
+ InOrder inOrder = inOrder(
+ filter,
+ mOnBeforeTransformGroupsListener,
+ promoter,
+ mOnBeforeSortListener,
+ sectioner,
+ comparator,
+ mOnBeforeRenderListListener,
+ mOnRenderListListener);
+
+ inOrder.verify(filter, atLeastOnce())
+ .shouldFilterOut(any(NotificationEntry.class), anyLong());
+ inOrder.verify(mOnBeforeTransformGroupsListener)
+ .onBeforeTransformGroups(anyList(), anyList());
+ inOrder.verify(promoter, atLeastOnce())
+ .shouldPromoteToTopLevel(any(NotificationEntry.class));
+ inOrder.verify(mOnBeforeSortListener).onBeforeSort(anyList());
+ inOrder.verify(sectioner, atLeastOnce()).getSection(any(ListEntry.class));
+ inOrder.verify(comparator, atLeastOnce())
+ .compare(any(ListEntry.class), any(ListEntry.class));
+ inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
+ inOrder.verify(mOnRenderListListener).onRenderList(anyList());
+ }
+
+ @Test
+ public void testThatPluggableInvalidationsTriggersRerun() {
+ // GIVEN a variety of pluggables
+ NotifFilter packageFilter = new PackageFilter(PACKAGE_1);
+ NotifPromoter idPromoter = new IdPromoter(4);
+ SectionsProvider sectionsProvider = new PackageSectioner();
+ NotifComparator hypeComparator = new HypeComparator(PACKAGE_2);
+
+ mListBuilder.addFilter(packageFilter);
+ mListBuilder.addPromoter(idPromoter);
+ mListBuilder.setSectionsProvider(sectionsProvider);
+ mListBuilder.setComparators(Collections.singletonList(hypeComparator));
+
+ // GIVEN a set of random notifs
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ dispatchBuild();
+
+ // WHEN each pluggable is invalidated THEN the list is re-rendered
+
+ clearInvocations(mOnRenderListListener);
+ packageFilter.invalidateList();
+ verify(mOnRenderListListener).onRenderList(anyList());
+
+ clearInvocations(mOnRenderListListener);
+ idPromoter.invalidateList();
+ verify(mOnRenderListListener).onRenderList(anyList());
+
+ clearInvocations(mOnRenderListListener);
+ sectionsProvider.invalidateList();
+ verify(mOnRenderListListener).onRenderList(anyList());
+
+ clearInvocations(mOnRenderListListener);
+ hypeComparator.invalidateList();
+ verify(mOnRenderListListener).onRenderList(anyList());
+ }
+
+ @Test
+ public void testNotifFiltersAreAllSentTheSameNow() {
+ // GIVEN three notif filters
+ NotifFilter filter1 = spy(new PackageFilter(PACKAGE_5));
+ NotifFilter filter2 = spy(new PackageFilter(PACKAGE_5));
+ NotifFilter filter3 = spy(new PackageFilter(PACKAGE_5));
+ mListBuilder.addFilter(filter1);
+ mListBuilder.addFilter(filter2);
+ mListBuilder.addFilter(filter3);
+
+ // GIVEN the SystemClock is set to a particular time:
+ mSystemClock.setAutoIncrement(true);
+ mSystemClock.setUptimeMillis(47);
+
+ // WHEN the pipeline is kicked off on a list of notifs
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ dispatchBuild();
+
+ // THEN the value of `now` is the same for all calls to shouldFilterOut
+ verify(filter1).shouldFilterOut(mEntrySet.get(0), 47);
+ verify(filter2).shouldFilterOut(mEntrySet.get(0), 47);
+ verify(filter3).shouldFilterOut(mEntrySet.get(0), 47);
+ verify(filter1).shouldFilterOut(mEntrySet.get(1), 47);
+ verify(filter2).shouldFilterOut(mEntrySet.get(1), 47);
+ verify(filter3).shouldFilterOut(mEntrySet.get(1), 47);
+ }
+
+ @Test
+ public void testNewlyAddedEntries() {
+ // GIVEN a registered OnBeforeTransformGroupsListener
+ RecordingOnBeforeTransformGroupsListener listener =
+ spy(new RecordingOnBeforeTransformGroupsListener());
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // Given some new notifs
+ addNotif(0, PACKAGE_1);
+ addGroupChild(1, PACKAGE_2, GROUP_1);
+ addGroupSummary(2, PACKAGE_2, GROUP_1);
+ addGroupChild(3, PACKAGE_2, GROUP_1);
+ addNotif(4, PACKAGE_3);
+ addGroupChild(5, PACKAGE_2, GROUP_1);
+
+ // WHEN we run the pipeline
+ dispatchBuild();
+
+ verifyBuiltList(
+ notif(0),
+ group(
+ summary(2),
+ child(1),
+ child(3),
+ child(5)
+ ),
+ notif(4)
+ );
+
+ // THEN all the new notifs, including the new GroupEntry, are passed to the listener
+ verify(listener).onBeforeTransformGroups(
+ Arrays.asList(
+ mEntrySet.get(0),
+ mBuiltList.get(1),
+ mEntrySet.get(4)
+ ),
+ Arrays.asList(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mBuiltList.get(1),
+ mEntrySet.get(2),
+ mEntrySet.get(3),
+ mEntrySet.get(4),
+ mEntrySet.get(5)
+ )
+ );
+ }
+
+ @Test
+ public void testNewlyAddedEntriesOnSecondRun() {
+ // GIVEN a registered OnBeforeTransformGroupsListener
+ RecordingOnBeforeTransformGroupsListener listener =
+ spy(new RecordingOnBeforeTransformGroupsListener());
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // Given some notifs that have already been added (two of which are in malformed groups)
+ addNotif(0, PACKAGE_1);
+ addGroupChild(1, PACKAGE_2, GROUP_1);
+ addGroupChild(2, PACKAGE_3, GROUP_2);
+
+ dispatchBuild();
+ clearInvocations(listener);
+
+ // WHEN we run the pipeline
+ addGroupSummary(3, PACKAGE_2, GROUP_1);
+ addGroupChild(4, PACKAGE_3, GROUP_2);
+ addGroupSummary(5, PACKAGE_3, GROUP_2);
+ addGroupChild(6, PACKAGE_3, GROUP_2);
+ addNotif(7, PACKAGE_2);
+
+ dispatchBuild();
+
+ verifyBuiltList(
+ notif(0),
+ notif(1),
+ group(
+ summary(5),
+ child(2),
+ child(4),
+ child(6)
+ ),
+ notif(7)
+ );
+
+ // THEN all the new notifs, including the new GroupEntry, are passed to the listener
+ verify(listener).onBeforeTransformGroups(
+ Arrays.asList(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mBuiltList.get(2),
+ mEntrySet.get(7)
+ ),
+ Arrays.asList(
+ mBuiltList.get(2),
+ mEntrySet.get(4),
+ mEntrySet.get(5),
+ mEntrySet.get(6),
+ mEntrySet.get(7)
+ )
+ );
+ }
+
+ @Test
+ public void testAnnulledGroupsHaveParentSetProperly() {
+ // GIVEN a list containing a small group that's already been built once
+ addGroupChild(0, PACKAGE_2, GROUP_2);
+ addGroupSummary(1, PACKAGE_2, GROUP_2);
+ addGroupChild(2, PACKAGE_2, GROUP_2);
+ dispatchBuild();
+
+ verifyBuiltList(
+ group(
+ summary(1),
+ child(0),
+ child(2)
+ )
+ );
+ GroupEntry group = (GroupEntry) mBuiltList.get(0);
+
+ // WHEN a child is removed such that the group is no longer big enough
+ mEntrySet.remove(2);
+ dispatchBuild();
+
+ // THEN the group is annulled and its parent is set back to null
+ verifyBuiltList(
+ notif(0)
+ );
+ assertNull(group.getParent());
+
+ // but its previous parent indicates that it was added in the previous iteration
+ assertEquals(GroupEntry.ROOT_ENTRY, group.getPreviousParent());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testOutOfOrderFilterInvalidationThrows() {
+ // GIVEN a NotifFilter that gets invalidated during the grouping stage
+ NotifFilter filter = new PackageFilter(PACKAGE_5);
+ OnBeforeTransformGroupsListener listener =
+ (list, newlyVisibleEntries) -> filter.invalidateList();
+ mListBuilder.addFilter(filter);
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // WHEN we try to run the pipeline and the filter is invalidated
+ addNotif(0, PACKAGE_1);
+ dispatchBuild();
+
+ // Then an exception is thrown
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testOutOfOrderPrompterInvalidationThrows() {
+ // GIVEN a NotifPromoter that gets invalidated during the sorting stage
+ NotifPromoter promoter = new IdPromoter(47);
+ OnBeforeSortListener listener =
+ (list) -> promoter.invalidateList();
+ mListBuilder.addPromoter(promoter);
+ mListBuilder.addOnBeforeSortListener(listener);
+
+ // WHEN we try to run the pipeline and the promoter is invalidated
+ addNotif(0, PACKAGE_1);
+ dispatchBuild();
+
+ // Then an exception is thrown
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testOutOfOrderComparatorInvalidationThrows() {
+ // GIVEN a NotifComparator that gets invalidated during the finalizing stage
+ NotifComparator comparator = new HypeComparator(PACKAGE_5);
+ OnBeforeRenderListListener listener =
+ (list) -> comparator.invalidateList();
+ mListBuilder.setComparators(Collections.singletonList(comparator));
+ mListBuilder.addOnBeforeRenderListListener(listener);
+
+ // WHEN we try to run the pipeline and the comparator is invalidated
+ addNotif(0, PACKAGE_1);
+ dispatchBuild();
+
+ // Then an exception is thrown
+ }
+
+ /**
+ * Adds a notif to the collection that will be passed to the list builder when
+ * {@link #dispatchBuild()}s is called.
+ *
+ * @param index Index of this notification in the set. This must be the current size of the set.
+ * it exists to improve readability of the resulting code, since later tests will
+ * have to refer to notifs by index.
+ * @param packageId Package that the notif should be posted under
+ * @return A NotificationEntryBuilder that can be used to further modify the notif. Do not call
+ * build() on the builder; that will be done on the next dispatchBuild().
+ */
+ private NotificationEntryBuilder addNotif(int index, String packageId) {
+ final NotificationEntryBuilder builder = new NotificationEntryBuilder()
+ .setPkg(packageId)
+ .setId(nextId(packageId))
+ .setRank(nextRank());
+
+ builder.modifyNotification(mContext)
+ .setContentTitle("Top level singleton")
+ .setChannelId("test_channel");
+
+ assertEquals(mEntrySet.size() + mPendingSet.size(), index);
+ mPendingSet.add(builder);
+ return builder;
+ }
+
+ /** Same behavior as {@link #addNotif(int, String)}. */
+ private NotificationEntryBuilder addGroupSummary(int index, String packageId, String groupId) {
+ final NotificationEntryBuilder builder = new NotificationEntryBuilder()
+ .setPkg(packageId)
+ .setId(nextId(packageId))
+ .setRank(nextRank());
+
+ builder.modifyNotification(mContext)
+ .setChannelId("test_channel")
+ .setContentTitle("Group summary")
+ .setGroup(groupId)
+ .setGroupSummary(true);
+
+ assertEquals(mEntrySet.size() + mPendingSet.size(), index);
+ mPendingSet.add(builder);
+ return builder;
+ }
+
+ /** Same behavior as {@link #addNotif(int, String)}. */
+ private NotificationEntryBuilder addGroupChild(int index, String packageId, String groupId) {
+ final NotificationEntryBuilder builder = new NotificationEntryBuilder()
+ .setPkg(packageId)
+ .setId(nextId(packageId))
+ .setRank(nextRank());
+
+ builder.modifyNotification(mContext)
+ .setChannelId("test_channel")
+ .setContentTitle("Group child")
+ .setGroup(groupId);
+
+ assertEquals(mEntrySet.size() + mPendingSet.size(), index);
+ mPendingSet.add(builder);
+ return builder;
+ }
+
+ private int nextId(String packageName) {
+ Integer nextId = mNextIdMap.get(packageName);
+ if (nextId == null) {
+ nextId = 0;
+ }
+ mNextIdMap.put(packageName, nextId + 1);
+ return nextId;
+ }
+
+ private int nextRank() {
+ int nextRank = mNextRank;
+ mNextRank++;
+ return nextRank;
+ }
+
+ private void dispatchBuild() {
+ if (mPendingSet.size() > 0) {
+ for (NotificationEntryBuilder builder : mPendingSet) {
+ mEntrySet.add(builder.build());
+ }
+ mPendingSet.clear();
+ }
+
+ mReadyForBuildListener.onBeginDispatchToListeners();
+ mReadyForBuildListener.onBuildList(mEntrySet);
+ }
+
+ private void verifyBuiltList(ExpectedEntry ...expectedEntries) {
+ try {
+ assertEquals(
+ "List is the wrong length",
+ expectedEntries.length,
+ mBuiltList.size());
+
+ for (int i = 0; i < expectedEntries.length; i++) {
+ ListEntry outEntry = mBuiltList.get(i);
+ ExpectedEntry expectedEntry = expectedEntries[i];
+
+ if (expectedEntry instanceof ExpectedNotif) {
+ assertEquals(
+ "Entry " + i + " isn't a NotifEntry",
+ NotificationEntry.class,
+ outEntry.getClass());
+ assertEquals(
+ "Entry " + i + " doesn't match expected value.",
+ ((ExpectedNotif) expectedEntry).entry, outEntry);
+ } else {
+ ExpectedGroup cmpGroup = (ExpectedGroup) expectedEntry;
+
+ assertEquals(
+ "Entry " + i + " isn't a GroupEntry",
+ GroupEntry.class,
+ outEntry.getClass());
+
+ GroupEntry outGroup = (GroupEntry) outEntry;
+
+ assertEquals(
+ "Summary notif for entry " + i
+ + " doesn't match expected value",
+ cmpGroup.summary,
+ outGroup.getSummary());
+ assertEquals(
+ "Summary notif for entry " + i
+ + " doesn't have proper parent",
+ outGroup,
+ outGroup.getSummary().getParent());
+
+ assertEquals("Children for entry " + i,
+ cmpGroup.children,
+ outGroup.getChildren());
+
+ for (int j = 0; j < outGroup.getChildren().size(); j++) {
+ NotificationEntry child = outGroup.getChildren().get(j);
+ assertEquals(
+ "Child " + j + " for entry " + i
+ + " doesn't have proper parent",
+ outGroup,
+ child.getParent());
+ }
+ }
+ }
+ } catch (AssertionError err) {
+ throw new AssertionError(
+ "List under test failed verification:\n" + dumpList(mBuiltList), err);
+ }
+ }
+
+ private ExpectedNotif notif(int index) {
+ return new ExpectedNotif(mEntrySet.get(index));
+ }
+
+ private ExpectedGroup group(ExpectedSummary summary, ExpectedChild...children) {
+ return new ExpectedGroup(
+ summary.entry,
+ Arrays.stream(children)
+ .map(child -> child.entry)
+ .collect(Collectors.toList()));
+ }
+
+ private ExpectedSummary summary(int index) {
+ return new ExpectedSummary(mEntrySet.get(index));
+ }
+
+ private ExpectedChild child(int index) {
+ return new ExpectedChild(mEntrySet.get(index));
+ }
+
+ private abstract static class ExpectedEntry {
+ }
+
+ private static class ExpectedNotif extends ExpectedEntry {
+ public final NotificationEntry entry;
+
+ private ExpectedNotif(NotificationEntry entry) {
+ this.entry = entry;
+ }
+ }
+
+ private static class ExpectedGroup extends ExpectedEntry {
+ public final NotificationEntry summary;
+ public final List<NotificationEntry> children;
+
+ private ExpectedGroup(
+ NotificationEntry summary,
+ List<NotificationEntry> children) {
+ this.summary = summary;
+ this.children = children;
+ }
+ }
+
+ private static class ExpectedSummary {
+ public final NotificationEntry entry;
+
+ private ExpectedSummary(NotificationEntry entry) {
+ this.entry = entry;
+ }
+ }
+
+ private static class ExpectedChild {
+ public final NotificationEntry entry;
+
+ private ExpectedChild(NotificationEntry entry) {
+ this.entry = entry;
+ }
+ }
+
+ /** Filters out notifs from a particular package */
+ private static class PackageFilter extends NotifFilter {
+ private final String mPackageName;
+
+ private boolean mEnabled = true;
+
+ PackageFilter(String packageName) {
+ super("PackageFilter");
+
+ mPackageName = packageName;
+ }
+
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ return mEnabled && entry.getSbn().getPackageName().equals(mPackageName);
+ }
+
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+ }
+
+ /** Promotes notifs with particular IDs */
+ private static class IdPromoter extends NotifPromoter {
+ private final List<Integer> mIds;
+
+ IdPromoter(Integer... ids) {
+ super("IdPromoter");
+ mIds = Arrays.asList(ids);
+ }
+
+ @Override
+ public boolean shouldPromoteToTopLevel(NotificationEntry child) {
+ return mIds.contains(child.getSbn().getId());
+ }
+ }
+
+ /** Sorts specific notifs above all others. */
+ private static class HypeComparator extends NotifComparator {
+
+ private final List<String> mPreferredPackages;
+
+ HypeComparator(String ...preferredPackages) {
+ super("HypeComparator");
+ mPreferredPackages = Arrays.asList(preferredPackages);
+ }
+
+ @Override
+ public int compare(ListEntry o1, ListEntry o2) {
+ boolean contains1 = mPreferredPackages.contains(
+ o1.getRepresentativeEntry().getSbn().getPackageName());
+ boolean contains2 = mPreferredPackages.contains(
+ o2.getRepresentativeEntry().getSbn().getPackageName());
+
+ return Boolean.compare(contains2, contains1);
+ }
+ }
+
+ /** Sorts notifs into sections based on their package name */
+ private static class PackageSectioner extends SectionsProvider {
+
+ PackageSectioner() {
+ super("PackageSectioner");
+ }
+
+ @Override
+ public int getSection(ListEntry entry) {
+ switch (entry.getRepresentativeEntry().getSbn().getPackageName()) {
+ case PACKAGE_1:
+ return 1;
+ case PACKAGE_2:
+ return 2;
+ case PACKAGE_3:
+ return 3;
+ default:
+ return 4;
+ }
+ }
+ }
+
+ private static class RecordingOnBeforeTransformGroupsListener
+ implements OnBeforeTransformGroupsListener {
+ public List<ListEntry> newlyVisibleEntries;
+
+ @Override
+ public void onBeforeTransformGroups(List<ListEntry> list,
+ List<ListEntry> newlyVisibleEntries) {
+ this.newlyVisibleEntries = newlyVisibleEntries;
+ }
+ }
+
+ private static final String PACKAGE_1 = "com.test1";
+ private static final String PACKAGE_2 = "com.test2";
+ private static final String PACKAGE_3 = "org.test3";
+ private static final String PACKAGE_4 = "com.test4";
+ private static final String PACKAGE_5 = "com.test5";
+
+ private static final String GROUP_1 = "group_1";
+ private static final String GROUP_2 = "group_2";
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 71c2e11f2fc4..ba2887936cb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -139,7 +139,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Test
public void testInflationThrowsErrorDoesntCallUpdated() throws Exception {
mRow.getPrivateLayout().removeAllViews();
- mRow.getStatusBarNotification().getNotification().contentView
+ mRow.getEntry().getSbn().getNotification().contentView
= new RemoteViews(mContext.getPackageName(), R.layout.status_bar);
runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
true /* expectingException */, mNotificationInflater);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 0b123fc8ff7e..749dae5c9c8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -320,7 +320,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.setUserSentiment(USER_SENTIMENT_NEGATIVE)
.build();
when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+ StatusBarNotification statusBarNotification = row.getEntry().getSbn();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -352,7 +352,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.setUserSentiment(USER_SENTIMENT_NEGATIVE)
.build();
when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+ StatusBarNotification statusBarNotification = row.getEntry().getSbn();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -386,7 +386,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.build();
row.getEntry().setIsHighPriority(true);
when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+ StatusBarNotification statusBarNotification = row.getEntry().getSbn();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -418,7 +418,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.setUserSentiment(USER_SENTIMENT_NEGATIVE)
.build();
when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+ StatusBarNotification statusBarNotification = row.getEntry().getSbn();
NotificationEntry entry = row.getEntry();
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
@@ -452,7 +452,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.setUserSentiment(USER_SENTIMENT_NEGATIVE)
.build();
when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+ StatusBarNotification statusBarNotification = row.getEntry().getSbn();
NotificationEntry entry = row.getEntry();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
@@ -530,7 +530,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
NotificationMenuRowPlugin menuRow = new NotificationMenuRow(mContext);
- menuRow.createMenu(row, row.getStatusBarNotification());
+ menuRow.createMenu(row, row.getEntry().getSbn());
NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
assertNotNull(menuItem);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 5b624bc6e4b3..d20a37a18d2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -448,12 +448,12 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mock(ExpandableNotificationRow.LongPressListener.class));
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
- when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker(
+ when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
MetricsProto.MetricsEvent.VIEW_UNKNOWN));
mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock(
NotificationMenuRowPlugin.MenuItem.class));
- verify(row.getStatusBarNotification()).getLogMaker(); // This writes most of the log data
+ verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data
verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR,
MetricsProto.MetricsEvent.TYPE_ACTION));
}
@@ -463,11 +463,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void testOnMenuShownLogging() { ;
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
- when(row.getStatusBarNotification().getLogMaker()).thenReturn(new LogMaker(
+ when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
MetricsProto.MetricsEvent.VIEW_UNKNOWN));
mStackScroller.mMenuEventListener.onMenuShown(row);
- verify(row.getStatusBarNotification()).getLogMaker(); // This writes most of the log data
+ verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data
verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR,
MetricsProto.MetricsEvent.TYPE_ACTION));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index bc4e401c49e4..8decae33d45b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -34,8 +34,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.tuner.TunerService;
import org.junit.Before;
import org.junit.Test;
@@ -58,10 +56,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Before
public void setup() {
- mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
StatusBar statusBar = mock(StatusBar.class);
mDependency.injectTestDependency(StatusBar.class, statusBar);
- mSysuiContext.putComponent(TunerService.class, mock(TunerService.class));
mStatusBarStateController = mDependency
.injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
index 098a69f5a897..aae075777506 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
@@ -37,7 +36,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.After;
@@ -60,11 +58,9 @@ public class NavigationBarButtonTest extends SysuiTestCase {
@Before
public void setup() {
- mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
final Display display = createVirtualDisplay();
final SysuiTestableContext context =
(SysuiTestableContext) mContext.createDisplayContext(display);
- context.putComponent(CommandQueue.class, mock(CommandQueue.class));
mDependency.injectMockDependency(AssistManager.class);
mDependency.injectMockDependency(OverviewProxyService.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
index 991e49588417..80e33fbc6acb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
@@ -33,7 +33,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.statusbar.CommandQueue;
import org.junit.After;
import org.junit.Before;
@@ -52,7 +51,6 @@ public class NavigationBarInflaterViewTest extends SysuiTestCase {
@Before
public void setUp() {
- mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mDependency.injectMockDependency(AssistManager.class);
mDependency.injectMockDependency(OverviewProxyService.class);
mDependency.injectMockDependency(NavigationModeController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index a49ae35a8040..3ad1e39f441e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -87,7 +87,6 @@ public final class NotificationGroupTestHelper {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
- when(row.getStatusBarNotification()).thenReturn(entry.getSbn());
when(row.isInflationFlagSet(anyInt())).thenReturn(true);
return entry;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 24a5d932fd85..07be0d7efffb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -141,13 +141,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// Create standard notification with contentIntent
mNotificationRow = mNotificationTestHelper.createRow();
- StatusBarNotification sbn = mNotificationRow.getStatusBarNotification();
+ StatusBarNotification sbn = mNotificationRow.getEntry().getSbn();
sbn.getNotification().contentIntent = mContentIntent;
sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
// Create bubble notification row with contentIntent
mBubbleNotificationRow = mNotificationTestHelper.createBubble();
- StatusBarNotification bubbleSbn = mBubbleNotificationRow.getStatusBarNotification();
+ StatusBarNotification bubbleSbn = mBubbleNotificationRow.getEntry().getSbn();
bubbleSbn.getNotification().contentIntent = mContentIntent;
bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
@@ -194,7 +194,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
public void testOnNotificationClicked_keyGuardShowing()
throws PendingIntent.CanceledException, RemoteException {
// Given
- StatusBarNotification sbn = mNotificationRow.getStatusBarNotification();
+ StatusBarNotification sbn = mNotificationRow.getEntry().getSbn();
sbn.getNotification().contentIntent = mContentIntent;
sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
@@ -228,7 +228,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Test
public void testOnNotificationClicked_bubble_noContentIntent_noKeyGuard()
throws RemoteException {
- StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
+ StatusBarNotification sbn = mBubbleNotificationRow.getEntry().getSbn();
// Given
sbn.getNotification().contentIntent = null;
@@ -257,7 +257,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Test
public void testOnNotificationClicked_bubble_noContentIntent_keyGuardShowing()
throws RemoteException {
- StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
+ StatusBarNotification sbn = mBubbleNotificationRow.getEntry().getSbn();
// Given
sbn.getNotification().contentIntent = null;
@@ -287,7 +287,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Test
public void testOnNotificationClicked_bubble_withContentIntent_keyGuardShowing()
throws RemoteException {
- StatusBarNotification sbn = mBubbleNotificationRow.getStatusBarNotification();
+ StatusBarNotification sbn = mBubbleNotificationRow.getEntry().getSbn();
// Given
sbn.getNotification().contentIntent = mContentIntent;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 95929c3adcb8..1f2df653490c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -111,7 +111,6 @@ import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NewNotifPipeline;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -120,6 +119,7 @@ import com.android.systemui.statusbar.notification.NotificationInterruptionState
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -389,7 +389,6 @@ public class StatusBarTest extends SysuiTestCase {
// TODO: we should be able to call mStatusBar.start() and have all the below values
// initialized automatically.
- mStatusBar.mComponents = mContext.getComponents();
mStatusBar.mStatusBarWindow = mStatusBarWindowView;
mStatusBar.mNotificationPanel = mNotificationPanelView;
mStatusBar.mDozeScrimController = mDozeScrimController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 4d4e9ae47d18..e08551abdca2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -82,7 +82,6 @@ public class StatusBarWindowViewTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mView = new StatusBarWindowView(getContext(), null);
- mContext.putComponent(StatusBar.class, mStatusBar);
when(mStatusBar.isDozing()).thenReturn(false);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index 0d1e1bd0bba1..811e6a06e607 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -70,11 +70,11 @@ public class HotspotControllerImplTest extends SysuiTestCase {
mContext.addMockSystemService(WifiManager.class, mWifiManager);
doAnswer((InvocationOnMock invocation) -> {
- ((WifiManager.SoftApCallback) invocation.getArgument(0))
+ ((WifiManager.SoftApCallback) invocation.getArgument(1))
.onConnectedClientsChanged(new ArrayList<>());
return null;
- }).when(mWifiManager).registerSoftApCallback(any(WifiManager.SoftApCallback.class),
- any(Executor.class));
+ }).when(mWifiManager).registerSoftApCallback(any(Executor.class),
+ any(WifiManager.SoftApCallback.class));
mController = new HotspotControllerImpl(mContext, new Handler(mLooper.getLooper()));
mController.handleSetListening(true);
@@ -85,7 +85,7 @@ public class HotspotControllerImplTest extends SysuiTestCase {
mController.addCallback(mCallback1);
mController.addCallback(mCallback2);
- verify(mWifiManager, times(1)).registerSoftApCallback(eq(mController), any());
+ verify(mWifiManager, times(1)).registerSoftApCallback(any(), eq(mController));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 0bc7868aab9c..e15ca1da928d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -64,7 +64,8 @@ public class FakeSensorManager extends SensorManager {
.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (proxSensor == null) {
// No prox? Let's create a fake one!
- proxSensor = createSensor(Sensor.TYPE_PROXIMITY, null);
+ proxSensor =
+ createSensor(Sensor.TYPE_PROXIMITY, null, 1 /* SENSOR_FLAG_WAKE_UP_SENSOR */);
}
mSensors = new FakeGenericSensor[]{
@@ -92,18 +93,6 @@ public class FakeSensorManager extends SensorManager {
if (s != null) {
return s;
}
- switch(type) {
- case Sensor.TYPE_PROXIMITY:
- try {
- return createSensor(Sensor.TYPE_PROXIMITY, null);
- } catch (Exception e) {
- // fall through
- }
- break;
- default:
- break;
-
- }
// Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just
// return non-wakeup sensors if we can't find a wakeup sensor.
return getDefaultSensor(type, false /* wakeup */);
@@ -208,6 +197,10 @@ public class FakeSensorManager extends SensorManager {
}
private Sensor createSensor(int type, @Nullable String stringType) throws Exception {
+ return createSensor(type, stringType, 0 /* flags */);
+ }
+
+ private Sensor createSensor(int type, @Nullable String stringType, int flags) throws Exception {
Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
constr.setAccessible(true);
Sensor sensor = constr.newInstance();
@@ -225,7 +218,7 @@ public class FakeSensorManager extends SensorManager {
setSensorField(sensor, "mPower", 1);
setSensorField(sensor, "mMinDelay", 1000);
setSensorField(sensor, "mMaxDelay", 1000000000);
- setSensorField(sensor, "mFlags", 0);
+ setSensorField(sensor, "mFlags", flags);
setSensorField(sensor, "mId", -1);
return sensor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
new file mode 100644
index 000000000000..7b5417cd5c36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.time;
+
+public class FakeSystemClock implements SystemClock {
+ private boolean mAutoIncrement = true;
+
+ private long mUptimeMillis;
+ private long mElapsedRealtime;
+ private long mElapsedRealtimeNanos;
+ private long mCurrentThreadTimeMillis;
+ private long mCurrentThreadTimeMicro;
+ private long mCurrentTimeMicro;
+
+ @Override
+ public long uptimeMillis() {
+ long value = mUptimeMillis;
+ if (mAutoIncrement) {
+ mUptimeMillis++;
+ }
+ return value;
+ }
+
+ @Override
+ public long elapsedRealtime() {
+ long value = mElapsedRealtime;
+ if (mAutoIncrement) {
+ mElapsedRealtime++;
+ }
+ return value;
+ }
+
+ @Override
+ public long elapsedRealtimeNanos() {
+ long value = mElapsedRealtimeNanos;
+ if (mAutoIncrement) {
+ mElapsedRealtimeNanos++;
+ }
+ return value;
+ }
+
+ @Override
+ public long currentThreadTimeMillis() {
+ long value = mCurrentThreadTimeMillis;
+ if (mAutoIncrement) {
+ mCurrentThreadTimeMillis++;
+ }
+ return value;
+ }
+
+ @Override
+ public long currentThreadTimeMicro() {
+ long value = mCurrentThreadTimeMicro;
+ if (mAutoIncrement) {
+ mCurrentThreadTimeMicro++;
+ }
+ return value;
+ }
+
+ @Override
+ public long currentTimeMicro() {
+ long value = mCurrentTimeMicro;
+ if (mAutoIncrement) {
+ mCurrentTimeMicro++;
+ }
+ return value;
+ }
+
+ public void setUptimeMillis(long uptimeMillis) {
+ mUptimeMillis = uptimeMillis;
+ }
+
+ public void setElapsedRealtime(long elapsedRealtime) {
+ mElapsedRealtime = elapsedRealtime;
+ }
+
+ public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
+ mElapsedRealtimeNanos = elapsedRealtimeNanos;
+ }
+
+ public void setCurrentThreadTimeMillis(long currentThreadTimeMillis) {
+ mCurrentThreadTimeMillis = currentThreadTimeMillis;
+ }
+
+ public void setCurrentThreadTimeMicro(long currentThreadTimeMicro) {
+ mCurrentThreadTimeMicro = currentThreadTimeMicro;
+ }
+
+ public void setCurrentTimeMicro(long currentTimeMicro) {
+ mCurrentTimeMicro = currentTimeMicro;
+ }
+
+ /** If true, each call to get____ will be one higher than the previous call to that method. */
+ public void setAutoIncrement(boolean autoIncrement) {
+ mAutoIncrement = autoIncrement;
+ }
+}
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index e5f8b49c3f0c..9a7cb3f2eff2 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -143,8 +143,8 @@ public abstract class UserManagerInternal {
* <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
* createAndManageUser is called by the device owner.
*/
- public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags,
- String[] disallowedPackages);
+ public abstract UserInfo createUserEvenWhenDisallowed(String name, String userType,
+ int flags, String[] disallowedPackages);
/**
* Same as {@link UserManager#removeUser(int userId)}, but bypasses the check for
@@ -202,8 +202,7 @@ public abstract class UserManagerInternal {
/**
* Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
- * and that the {@code callingUserId} is not a managed profile and
- * {@code targetUserId} is enabled.
+ * and that the {@code callingUserId} is not a profile and {@code targetUserId} is enabled.
*
* @return TRUE if the {@code callingUserId} can access {@code targetUserId}. FALSE
* otherwise
@@ -215,8 +214,7 @@ public abstract class UserManagerInternal {
String debugMsg, boolean throwSecurityException);
/**
- * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return
- * itself.
+ * If {@code userId} is of a profile, return the parent user ID. Otherwise return itself.
*/
public abstract int getProfileParentId(int userId);
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 190e6cf2d35c..7b02b6e0ac11 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -18,7 +18,6 @@ package com.android.server;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.gsi.GsiInstallParams;
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsid;
@@ -47,6 +46,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
private static final int GSID_ROUGH_TIMEOUT_MS = 8192;
private static final String PATH_DEFAULT = "/data/gsi";
private Context mContext;
+ private String mInstallPath;
private volatile IGsiService mGsiService;
DynamicSystemService(Context context) {
@@ -115,8 +115,8 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
}
@Override
- public boolean startInstallation(String name, long size, boolean readOnly)
- throws RemoteException {
+ public boolean startInstallation() throws RemoteException {
+ IGsiService service = getGsiService();
// priority from high to low: sysprop -> sdcard -> /data
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
@@ -138,14 +138,19 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
}
Slog.i(TAG, "startInstallation -> " + path);
}
+ mInstallPath = path;
+ if (service.openInstall(path) != 0) {
+ Slog.i(TAG, "Failed to open " + path);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean createPartition(String name, long size, boolean readOnly)
+ throws RemoteException {
IGsiService service = getGsiService();
- GsiInstallParams installParams = new GsiInstallParams();
- installParams.installDir = path;
- installParams.name = name;
- installParams.size = size;
- installParams.wipe = readOnly;
- installParams.readOnly = readOnly;
- if (service.beginGsiInstall(installParams) != 0) {
+ if (service.createPartition(name, size, readOnly) != 0) {
Slog.i(TAG, "Failed to install " + name);
return false;
}
@@ -153,6 +158,16 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
}
@Override
+ public boolean finishInstallation() throws RemoteException {
+ IGsiService service = getGsiService();
+ if (service.closeInstall() != 0) {
+ Slog.i(TAG, "Failed to finish installation");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
public GsiProgress getInstallationProgress() throws RemoteException {
return getGsiService().getInstallProgress();
}
@@ -190,6 +205,8 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
@Override
public boolean remove() throws RemoteException {
+ IGsiService gsiService = getGsiService();
+ String install_dir = gsiService.getInstalledGsiImageDir();
return getGsiService().removeGsi();
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f8b0072e0017..dc61261876ff 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -20,6 +20,7 @@ import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED
import static java.util.Arrays.copyOf;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
@@ -112,6 +113,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
Context context;
String callingPackage;
+ String callingFeatureId;
IBinder binder;
@@ -145,7 +147,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
- context, subId, callerPid, callerUid, callingPackage);
+ context, subId, callerPid, callerUid, callingPackage, callingFeatureId);
} catch (SecurityException e) {
return false;
}
@@ -578,7 +580,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
- public void addOnSubscriptionsChangedListener(String callingPackage,
+ public void addOnSubscriptionsChangedListener(String callingPackage, String callingFeatureId,
IOnSubscriptionsChangedListener callback) {
int callerUserId = UserHandle.getCallingUserId();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
@@ -600,6 +602,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.context = mContext;
r.onSubscriptionsChangedListenerCallback = callback;
r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
r.events = 0;
@@ -632,7 +635,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
@Override
public void addOnOpportunisticSubscriptionsChangedListener(String callingPackage,
- IOnSubscriptionsChangedListener callback) {
+ String callingFeatureId, IOnSubscriptionsChangedListener callback) {
int callerUserId = UserHandle.getCallingUserId();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
if (VDBG) {
@@ -653,6 +656,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.context = mContext;
r.onOpportunisticSubscriptionsChangedListenerCallback = callback;
r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
r.events = 0;
@@ -728,21 +732,28 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
+ @Deprecated
@Override
- public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
+ public void listen(String callingPackage, IPhoneStateListener callback, int events,
boolean notifyNow) {
- listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, pkgForDebug, callback,
- events, notifyNow);
+ listenWithFeature(callingPackage, null, callback, events, notifyNow);
+ }
+
+ @Override
+ public void listenWithFeature(String callingPackage, String callingFeatureId,
+ IPhoneStateListener callback, int events, boolean notifyNow) {
+ listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage,
+ callingFeatureId, callback, events, notifyNow);
}
@Override
- public void listenForSubscriber(int subId, String pkgForDebug, IPhoneStateListener callback,
- int events, boolean notifyNow) {
- listen(pkgForDebug, callback, events, notifyNow, subId);
+ public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId,
+ IPhoneStateListener callback, int events, boolean notifyNow) {
+ listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId);
}
- private void listen(String callingPackage, IPhoneStateListener callback, int events,
- boolean notifyNow, int subId) {
+ private void listen(String callingPackage, @Nullable String callingFeatureId,
+ IPhoneStateListener callback, int events, boolean notifyNow, int subId) {
int callerUserId = UserHandle.getCallingUserId();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
String str = "listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
@@ -757,7 +768,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
// Checks permission and throws SecurityException for disallowed operations. For pre-M
// apps whose runtime permission has been revoked, we return immediately to skip sending
// events to the app without crashing it.
- if (!checkListenerPermission(events, subId, callingPackage, "listen")) {
+ if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
+ "listen")) {
return;
}
@@ -774,6 +786,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.context = mContext;
r.callback = callback;
r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
@@ -2374,8 +2387,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
== PackageManager.PERMISSION_GRANTED;
}
- private boolean checkListenerPermission(
- int events, int subId, String callingPackage, String message) {
+ private boolean checkListenerPermission(int events, int subId, String callingPackage,
+ @Nullable String callingFeatureId, String message) {
LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(callingPackage)
@@ -2410,7 +2423,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, subId, callingPackage, message)) {
+ mContext, subId, callingPackage, callingFeatureId, message)) {
return false;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4bb29f014d0e..3dc745b0cb95 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -346,7 +346,6 @@ import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
import com.android.server.appop.AppOpsService;
-import com.android.server.compat.CompatConfig;
import com.android.server.compat.PlatformCompat;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.firewall.IntentFirewall;
@@ -5032,8 +5031,9 @@ public class ActivityManagerService extends IActivityManager.Stub
bindApplicationTimeMillis = SystemClock.elapsedRealtime();
mAtmInternal.preBindApplication(app.getWindowProcessController());
final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
- long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info);
+ long[] disabledCompatChanges = {};
if (mPlatformCompat != null) {
+ disabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
mPlatformCompat.resetReporting(app.info);
}
if (app.isolatedEntryPoint != null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 1f56176bf00d..908ec6b844c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -37,11 +37,13 @@ import android.app.IStopUserCallback;
import android.app.IUidObserver;
import android.app.KeyguardManager;
import android.app.ProfilerInfo;
+import android.app.UserSwitchObserver;
import android.app.WaitResult;
import android.app.usage.AppStandbyInfo;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManager;
+import android.compat.Compatibility;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.Context;
@@ -80,15 +82,17 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.util.HexDump;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
-import com.android.server.compat.CompatConfig;
+import com.android.server.compat.PlatformCompat;
import java.io.BufferedReader;
import java.io.File;
@@ -1729,6 +1733,30 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ private void switchUserAndWaitForComplete(int userId) throws RemoteException {
+ // Register switch observer.
+ final CountDownLatch switchLatch = new CountDownLatch(1);
+ mInterface.registerUserSwitchObserver(
+ new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ if (userId == newUserId) {
+ switchLatch.countDown();
+ }
+ }
+ }, ActivityManagerShellCommand.class.getName());
+
+ // Switch.
+ mInterface.switchUser(userId);
+
+ // Wait.
+ try {
+ switchLatch.await(USER_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ getErrPrintWriter().println("Thread interrupted unexpectedly.");
+ }
+ }
+
int runSwitchUser(PrintWriter pw) throws RemoteException {
UserManager userManager = mInternal.mContext.getSystemService(UserManager.class);
final int userSwitchable = userManager.getUserSwitchability();
@@ -1736,8 +1764,23 @@ final class ActivityManagerShellCommand extends ShellCommand {
getErrPrintWriter().println("Error: " + userSwitchable);
return -1;
}
- String user = getNextArgRequired();
- mInterface.switchUser(Integer.parseInt(user));
+ boolean wait = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if ("-w".equals(opt)) {
+ wait = true;
+ } else {
+ getErrPrintWriter().println("Error: unknown option: " + opt);
+ return -1;
+ }
+ }
+
+ int userId = Integer.parseInt(getNextArgRequired());
+ if (wait) {
+ switchUserAndWaitForComplete(userId);
+ } else {
+ mInterface.switchUser(userId);
+ }
return 0;
}
@@ -2862,56 +2905,49 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
- private void killPackage(String packageName, PrintWriter pw) throws RemoteException {
- int uid = mPm.getPackageUid(packageName, 0, mUserId);
- if (uid < 0) {
- // uid is negative if the package wasn't found.
- pw.println("Didn't find package " + packageName + " on device.");
- } else {
- pw.println("Killing package " + packageName + " (UID " + uid + ").");
- final long origId = Binder.clearCallingIdentity();
- mInterface.killUid(UserHandle.getAppId(uid),
- UserHandle.USER_ALL, "killPackage");
- Binder.restoreCallingIdentity(origId);
- }
- }
-
private int runCompat(PrintWriter pw) throws RemoteException {
- final CompatConfig config = CompatConfig.get();
+ final PlatformCompat platformCompat = (PlatformCompat)
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
String toggleValue = getNextArgRequired();
long changeId;
String changeIdString = getNextArgRequired();
try {
changeId = Long.parseLong(changeIdString);
} catch (NumberFormatException e) {
- changeId = config.lookupChangeId(changeIdString);
+ changeId = platformCompat.lookupChangeId(changeIdString);
}
if (changeId == -1) {
pw.println("Unknown or invalid change: '" + changeIdString + "'.");
+ return -1;
}
String packageName = getNextArgRequired();
+ if (!platformCompat.isKnownChangeId(changeId)) {
+ pw.println("Warning! Change " + changeId + " is not known yet. Enabling/disabling it"
+ + " could have no effect.");
+ }
+ ArraySet<Long> enabled = new ArraySet<>();
+ ArraySet<Long> disabled = new ArraySet<>();
switch (toggleValue) {
case "enable":
- if (!config.addOverride(changeId, packageName, true)) {
- pw.println("Warning! Change " + changeId + " is not known yet. Enabling it"
- + " could have no effect.");
- }
+ enabled.add(changeId);
pw.println("Enabled change " + changeId + " for " + packageName + ".");
- killPackage(packageName, pw);
+ CompatibilityChangeConfig overrides =
+ new CompatibilityChangeConfig(
+ new Compatibility.ChangeConfig(enabled, disabled));
+ platformCompat.setOverrides(overrides, packageName);
return 0;
case "disable":
- if (!config.addOverride(changeId, packageName, false)) {
- pw.println("Warning! Change " + changeId + " is not known yet. Disabling it"
- + " could have no effect.");
- }
+ disabled.add(changeId);
pw.println("Disabled change " + changeId + " for " + packageName + ".");
- killPackage(packageName, pw);
+ overrides =
+ new CompatibilityChangeConfig(
+ new Compatibility.ChangeConfig(enabled, disabled));
+ platformCompat.setOverrides(overrides, packageName);
return 0;
case "reset":
- if (config.removeOverride(changeId, packageName)) {
+ if (platformCompat.clearOverride(changeId, packageName)) {
pw.println("Reset change " + changeId + " for " + packageName
+ " to default value.");
- killPackage(packageName, pw);
} else {
pw.println("No override exists for changeId " + changeId + ".");
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 436167635662..31ceb38d1607 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -384,7 +384,7 @@ class UserController implements Handler.Callback {
// We need to delay unlocking managed profiles until the parent user
// is also unlocked.
- if (mInjector.getUserManager().isManagedProfile(userId)) {
+ if (mInjector.getUserManager().isProfile(userId)) {
final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null
&& isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 09cbc5c67ab7..9a927785bd5b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2797,10 +2797,6 @@ public class AudioService extends IAudioService.Stub
setSystemAudioMute(mute);
AudioSystem.setMasterMute(mute);
sendMasterMuteUpdate(mute, flags);
-
- Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
- intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, mute);
- sendBroadcastToAll(intent);
}
}
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index d6ec22b078ea..490cce347188 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -43,13 +43,14 @@ import java.util.HashSet;
import java.util.Set;
import javax.xml.datatype.DatatypeConfigurationException;
+
/**
* This class maintains state relating to platform compatibility changes.
*
* <p>It stores the default configuration for each change, and any per-package overrides that have
* been configured.
*/
-public final class CompatConfig {
+final class CompatConfig {
private static final String TAG = "CompatConfig";
@@ -61,13 +62,13 @@ public final class CompatConfig {
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
@VisibleForTesting
- public CompatConfig() {
+ CompatConfig() {
}
/**
* @return The static instance of this class to be used within the system server.
*/
- public static CompatConfig get() {
+ static CompatConfig get() {
return sInstance;
}
@@ -77,7 +78,7 @@ public final class CompatConfig {
*
* @param change The change to add. Any change with the same ID will be overwritten.
*/
- public void addChange(CompatChange change) {
+ void addChange(CompatChange change) {
synchronized (mChanges) {
mChanges.put(change.getId(), change);
}
@@ -89,10 +90,10 @@ public final class CompatConfig {
*
* @param app The app in question
* @return A sorted long array of change IDs. We use a primitive array to minimize memory
- * footprint: Every app process will store this array statically so we aim to reduce
- * overhead as much as possible.
+ * footprint: Every app process will store this array statically so we aim to reduce
+ * overhead as much as possible.
*/
- public long[] getDisabledChanges(ApplicationInfo app) {
+ long[] getDisabledChanges(ApplicationInfo app) {
LongArray disabled = new LongArray();
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
@@ -113,7 +114,7 @@ public final class CompatConfig {
* @param name Name of the change to look up
* @return The change ID, or {@code -1} if no change with that name exists.
*/
- public long lookupChangeId(String name) {
+ long lookupChangeId(String name) {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
if (TextUtils.equals(mChanges.valueAt(i).getName(), name)) {
@@ -128,11 +129,11 @@ public final class CompatConfig {
* Find if a given change is enabled for a given application.
*
* @param changeId The ID of the change in question
- * @param app App to check for
+ * @param app App to check for
* @return {@code true} if the change is enabled for this app. Also returns {@code true} if the
- * change ID is not known, as unknown changes are enabled by default.
+ * change ID is not known, as unknown changes are enabled by default.
*/
- public boolean isChangeEnabled(long changeId, ApplicationInfo app) {
+ boolean isChangeEnabled(long changeId, ApplicationInfo app) {
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c == null) {
@@ -150,14 +151,15 @@ public final class CompatConfig {
*
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
- * @param changeId The ID of the change to be overridden. Note, this call will succeed even if
- * this change is not known; it will only have any effect if any code in the
- * platform is gated on the ID given.
+ * @param changeId The ID of the change to be overridden. Note, this call will succeed even
+ * if
+ * this change is not known; it will only have any effect if any code in the
+ * platform is gated on the ID given.
* @param packageName The app package name to override the change for.
- * @param enabled If the change should be enabled or disabled.
+ * @param enabled If the change should be enabled or disabled.
* @return {@code true} if the change existed before adding the override.
*/
- public boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean addOverride(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = true;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
@@ -172,15 +174,27 @@ public final class CompatConfig {
}
/**
+ * Check whether the change is known to the compat config.
+ *
+ * @return {@code true} if the change is known.
+ */
+ boolean isKnownChangeId(long changeId) {
+ synchronized (mChanges) {
+ CompatChange c = mChanges.get(changeId);
+ return c != null;
+ }
+ }
+
+ /**
* Removes an override previously added via {@link #addOverride(long, String, boolean)}. This
* restores the default behaviour for the given change and app, once any app processes have been
* restarted.
*
- * @param changeId The ID of the change that was overridden.
+ * @param changeId The ID of the change that was overridden.
* @param packageName The app package name that was overridden.
* @return {@code true} if an override existed;
*/
- public boolean removeOverride(long changeId, String packageName) {
+ boolean removeOverride(long changeId, String packageName) {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
@@ -191,22 +205,22 @@ public final class CompatConfig {
}
return overrideExists;
}
+
/**
* Overrides the enabled state for a given change and app. This method is intended to be used
* *only* for debugging purposes.
*
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
- * @param overrides list of overrides to default changes config.
+ * @param overrides list of overrides to default changes config.
* @param packageName app for which the overrides will be applied.
*/
- public void addOverrides(
- CompatibilityChangeConfig overrides, String packageName) {
+ void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
synchronized (mChanges) {
- for (Long changeId: overrides.enabledChanges()) {
+ for (Long changeId : overrides.enabledChanges()) {
addOverride(changeId, packageName, true);
}
- for (Long changeId: overrides.disabledChanges()) {
+ for (Long changeId : overrides.disabledChanges()) {
addOverride(changeId, packageName, false);
}
}
@@ -221,7 +235,7 @@ public final class CompatConfig {
*
* @param packageName The package for which the overrides should be purged.
*/
- public void removePackageOverrides(String packageName) {
+ void removePackageOverrides(String packageName) {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
mChanges.valueAt(i).removePackageOverride(packageName);
@@ -230,11 +244,11 @@ public final class CompatConfig {
}
/**
- * Dumps the current list of compatibility config information.
- *
- * @param pw The {@link PrintWriter} instance to which the information will be dumped.
- */
- public void dumpConfig(PrintWriter pw) {
+ * Dumps the current list of compatibility config information.
+ *
+ * @param pw The {@link PrintWriter} instance to which the information will be dumped.
+ */
+ void dumpConfig(PrintWriter pw) {
synchronized (mChanges) {
if (mChanges.size() == 0) {
pw.println("No compat overrides.");
@@ -252,10 +266,10 @@ public final class CompatConfig {
*
* @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped.
* @return A {@link CompatibilityChangeConfig} which contains the compat config info for the
- * given app.
+ * given app.
*/
- public CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) {
+ CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) {
Set<Long> enabled = new HashSet<>();
Set<Long> disabled = new HashSet<>();
synchronized (mChanges) {
@@ -276,15 +290,15 @@ public final class CompatConfig {
*
* @return An array of {@link CompatibilityChangeInfo} with the current changes.
*/
- public CompatibilityChangeInfo[] dumpChanges() {
+ CompatibilityChangeInfo[] dumpChanges() {
synchronized (mChanges) {
CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange change = mChanges.valueAt(i);
changeInfos[i] = new CompatibilityChangeInfo(change.getId(),
- change.getName(),
- change.getEnableAfterTargetSdk(),
- change.getDisabled());
+ change.getName(),
+ change.getEnableAfterTargetSdk(),
+ change.getDisabled());
}
return changeInfos;
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 75e2d220898d..709f3f82d4c5 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,9 +16,13 @@
package com.android.server.compat;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.util.StatsLog;
@@ -106,12 +110,26 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
CompatConfig.get().addOverrides(overrides, packageName);
+ killPackage(packageName);
+ }
+
+ @Override
+ public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
+ CompatConfig.get().addOverrides(overrides, packageName);
}
@Override
public void clearOverrides(String packageName) {
CompatConfig config = CompatConfig.get();
config.removePackageOverrides(packageName);
+ killPackage(packageName);
+ }
+
+ @Override
+ public boolean clearOverride(long changeId, String packageName) {
+ boolean existed = CompatConfig.get().removeOverride(changeId, packageName);
+ killPackage(packageName);
+ return existed;
}
@Override
@@ -124,6 +142,39 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return CompatConfig.get().dumpChanges();
}
+ /**
+ * Check whether the change is known to the compat config.
+ * @param changeId
+ * @return {@code true} if the change is known.
+ */
+ public boolean isKnownChangeId(long changeId) {
+ return CompatConfig.get().isKnownChangeId(changeId);
+
+ }
+
+ /**
+ * Retrieves the set of disabled changes for a given app. Any change ID not in the returned
+ * array is by default enabled for the app.
+ *
+ * @param appInfo The app in question
+ * @return A sorted long array of change IDs. We use a primitive array to minimize memory
+ * footprint: Every app process will store this array statically so we aim to reduce
+ * overhead as much as possible.
+ */
+ public long[] getDisabledChanges(ApplicationInfo appInfo) {
+ return CompatConfig.get().getDisabledChanges(appInfo);
+ }
+
+ /**
+ * Look up a change ID by name.
+ *
+ * @param name Name of the change to look up
+ * @return The change ID, or {@code -1} if no change with that name exists.
+ */
+ public long lookupChangeId(String name) {
+ return CompatConfig.get().lookupChangeId(name);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
@@ -151,4 +202,34 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private void reportChange(long changeId, int uid, int state) {
mChangeReporter.reportChange(uid, changeId, state);
}
+
+ private void killPackage(String packageName) {
+ int uid = -1;
+ try {
+ uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Didn't find package " + packageName + " on device.", e);
+ return;
+ }
+
+ Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ").");
+ killUid(UserHandle.getAppId(uid),
+ UserHandle.USER_ALL, "PlatformCompat overrides");
+ }
+
+ private void killUid(int appId, int userId, String reason) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ IActivityManager am = ActivityManager.getService();
+ if (am != null) {
+ try {
+ am.killUid(appId, userId, reason);
+ } catch (RemoteException e) {
+ /* ignore - same process */
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index b3804c4d7ec5..acedc3635730 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -286,8 +286,8 @@ public class Tethering extends BaseNetworkObserver {
private void startStateMachineUpdaters(Handler handler) {
mCarrierConfigChange.startListening();
- TelephonyManager.from(mContext).listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+ mContext.getSystemService(TelephonyManager.class).listen(
+ mPhoneStateListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index fb57d69f0db9..637bcae7007e 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -301,6 +301,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
// Timeout when holding wakelocks for downloading PSDS data.
private static final long DOWNLOAD_PSDS_DATA_TIMEOUT_MS = 60 * 1000;
+ private static final long WAKELOCK_TIMEOUT_MILLIS = 30 * 1000;
private final ExponentialBackOff mPsdsBackOff = new ExponentialBackOff(RETRY_INTERVAL,
MAX_RETRY_INTERVAL);
@@ -901,13 +902,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
if (mDownloadPsdsWakeLock.isHeld()) {
// This wakelock may have time-out, if a timeout was specified.
// Catch (and ignore) any timeout exceptions.
- try {
- mDownloadPsdsWakeLock.release();
- if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadPsdsData()");
- } catch (Exception e) {
- Log.i(TAG, "Wakelock timeout & release race exception in "
- + "handleDownloadPsdsData()", e);
- }
+ mDownloadPsdsWakeLock.release();
+ if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadPsdsData()");
} else {
Log.e(TAG, "WakeLock expired before release in "
+ "handleDownloadPsdsData()");
@@ -1482,39 +1478,35 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
Log.v(TAG, "SV count: " + info.mSvCount);
}
// Calculate number of satellites used in fix.
+ GnssStatus gnssStatus = GnssStatus.wrap(
+ info.mSvCount,
+ info.mSvidWithFlags,
+ info.mCn0s,
+ info.mSvElevations,
+ info.mSvAzimuths,
+ info.mSvCarrierFreqs);
int usedInFixCount = 0;
int maxCn0 = 0;
int meanCn0 = 0;
- for (int i = 0; i < info.mSvCount; i++) {
- if ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+ for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) {
+ if (gnssStatus.usedInFix(i)) {
++usedInFixCount;
- if (info.mCn0s[i] > maxCn0) {
- maxCn0 = (int) info.mCn0s[i];
+ if (gnssStatus.getCn0DbHz(i) > maxCn0) {
+ maxCn0 = (int) gnssStatus.getCn0DbHz(i);
}
- meanCn0 += info.mCn0s[i];
+ meanCn0 += gnssStatus.getCn0DbHz(i);
+ mGnssMetrics.logConstellationType(gnssStatus.getConstellationType(i));
}
if (VERBOSE) {
- Log.v(TAG, "svid: " + (info.mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
- " cn0: " + info.mCn0s[i] +
- " elev: " + info.mSvElevations[i] +
- " azimuth: " + info.mSvAzimuths[i] +
- " carrier frequency: " + info.mSvCarrierFreqs[i] +
- ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
- ? " " : " E") +
- ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
- ? " " : " A") +
- ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
- ? "" : "U") +
- ((info.mSvidWithFlags[i] &
- GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) == 0
- ? "" : "F"));
- }
-
- if ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
- int constellationType =
- (info.mSvidWithFlags[i] >> GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH)
- & GnssStatus.CONSTELLATION_TYPE_MASK;
- mGnssMetrics.logConstellationType(constellationType);
+ Log.v(TAG, "svid: " + gnssStatus.getSvid(i)
+ + " cn0: " + gnssStatus.getCn0DbHz(i)
+ + " elev: " + gnssStatus.getElevationDegrees(i)
+ + " azimuth: " + gnssStatus.getAzimuthDegrees(i)
+ + " carrier frequency: " + gnssStatus.getCn0DbHz(i)
+ + (gnssStatus.hasEphemerisData(i) ? " E" : " ")
+ + (gnssStatus.hasAlmanacData(i) ? " A" : " ")
+ + (gnssStatus.usedInFix(i) ? "U" : "")
+ + (gnssStatus.hasCarrierFrequencyHz(i) ? "F" : ""));
}
}
if (usedInFixCount > 0) {
@@ -1523,7 +1515,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
// return number of sats used in fix instead of total reported
mLocationExtras.set(usedInFixCount, meanCn0, maxCn0);
- mGnssMetrics.logSvStatus(info.mSvCount, info.mSvidWithFlags, info.mSvCarrierFreqs);
+ mGnssMetrics.logSvStatus(gnssStatus);
}
@NativeEntryPoint
@@ -2013,7 +2005,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
// hold a wake lock until this message is delivered
// note that this assumes the message will not be removed from the queue before
// it is handled (otherwise the wake lock would be leaked).
- mWakeLock.acquire();
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
if (DEBUG) {
Log.d(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
+ ", " + obj + ")");
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index 2e72fbd95931..93227bd78a81 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -321,7 +321,11 @@ class GnssNetworkConnectivityHandler {
private void handleUpdateNetworkState(Network network, boolean isConnected,
NetworkCapabilities capabilities) {
- boolean networkAvailable = isConnected && TelephonyManager.getDefault().getDataEnabled();
+ boolean networkAvailable = false;
+ TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+ if (telephonyManager != null) {
+ networkAvailable = isConnected && telephonyManager.getDataEnabled();
+ }
NetworkAttributes networkAttributes = updateTrackedNetworksState(isConnected, network,
capabilities);
String apn = networkAttributes.mApn;
diff --git a/services/core/java/com/android/server/location/NtpTimeHelper.java b/services/core/java/com/android/server/location/NtpTimeHelper.java
index 296b500eacee..67841aca1605 100644
--- a/services/core/java/com/android/server/location/NtpTimeHelper.java
+++ b/services/core/java/com/android/server/location/NtpTimeHelper.java
@@ -181,11 +181,7 @@ class NtpTimeHelper {
mHandler.postDelayed(this::retrieveAndInjectNtpTime, delay);
}
}
- try {
- // release wake lock held by task
- mWakeLock.release();
- } catch (Exception e) {
- // This happens when the WakeLock is already released.
- }
+ // release wake lock held by task
+ mWakeLock.release();
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 41806cabef3f..08c94267e969 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -337,7 +337,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager,
- wakeLock, getDefaultClock(), TelephonyManager.getDefault(),
+ wakeLock, getDefaultClock(), context.getSystemService(TelephonyManager.class),
new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir());
service.registerLocalService();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5b39fb687d0d..ec53157fd8e2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7724,7 +7724,7 @@ public class NotificationManagerService extends SystemService {
}
private void listenForCallState() {
- TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
+ getContext().getSystemService(TelephonyManager.class).listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (mCallState == state) return;
diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
index 9c1ac346ce32..947405ed2a78 100644
--- a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
+++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
@@ -56,16 +56,17 @@ public final class DeviceIdentifiersPolicyService extends SystemService {
// for any device / profile owner checks. The majority of requests for the serial number
// should use the getSerialForPackage method with the calling package specified.
if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
- /* callingPackage */ null, "getSerial")) {
+ /* callingPackage */ null, null, "getSerial")) {
return Build.UNKNOWN;
}
return SystemProperties.get("ro.serialno", Build.UNKNOWN);
}
@Override
- public @Nullable String getSerialForPackage(String callingPackage) throws RemoteException {
+ public @Nullable String getSerialForPackage(String callingPackage,
+ String callingFeatureId) throws RemoteException {
if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
- callingPackage, "getSerial")) {
+ callingPackage, callingFeatureId, "getSerial")) {
return Build.UNKNOWN;
}
return SystemProperties.get("ro.serialno", Build.UNKNOWN);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index bb5b04a08828..ec11a97cfc30 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -29,27 +29,24 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.ProviderInfo;
import android.net.Uri;
-import android.os.Build;
import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.Trace;
-import android.permission.IPermissionManager;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseSetArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.server.FgThread;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -60,7 +57,7 @@ import java.util.Set;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class AppsFilter {
- private static final String TAG = PackageManagerService.TAG;
+ private static final String TAG = "AppsFilter";
// Logs all filtering instead of enforcing
private static final boolean DEBUG_ALLOW_ALL = false;
@@ -69,52 +66,46 @@ public class AppsFilter {
private static final boolean DEBUG_LOGGING = false | DEBUG_ALLOW_ALL;
/**
- * This contains a list of packages that are implicitly queryable because another app explicitly
+ * This contains a list of app UIDs that are implicitly queryable because another app explicitly
* interacted with it. For example, if application A starts a service in application B,
* application B is implicitly allowed to query for application A; regardless of any manifest
* entries.
*/
- private final SparseArray<HashMap<String, Set<String>>> mImplicitlyQueryable =
- new SparseArray<>();
+ private final SparseSetArray<Integer> mImplicitlyQueryable = new SparseSetArray<>();
/**
- * A mapping from the set of packages that query other packages via package name to the
+ * A mapping from the set of App IDs that query other App IDs via package name to the
* list of packages that they can see.
*/
- private final HashMap<String, Set<String>> mQueriesViaPackage = new HashMap<>();
+ private final SparseSetArray<Integer> mQueriesViaPackage = new SparseSetArray<>();
/**
- * A mapping from the set of packages that query others via intent to the list
+ * A mapping from the set of App IDs that query others via intent to the list
* of packages that the intents resolve to.
*/
- private final HashMap<String, Set<String>> mQueriesViaIntent = new HashMap<>();
+ private final SparseSetArray<Integer> mQueriesViaIntent = new SparseSetArray<>();
/**
- * A set of packages that are always queryable by any package, regardless of their manifest
+ * A set of App IDs that are always queryable by any package, regardless of their manifest
* content.
*/
- private final HashSet<String> mForceQueryable;
+ private final ArraySet<Integer> mForceQueryable = new ArraySet<>();
+
/**
- * A set of packages that are always queryable by any package, regardless of their manifest
- * content.
+ * The set of package names provided by the device that should be force queryable regardless of
+ * their manifest contents.
*/
- private final Set<String> mForceQueryableByDevice;
+ private final String[] mForceQueryableByDevicePackageNames;
/** True if all system apps should be made queryable by default. */
private final boolean mSystemAppsQueryable;
- private final IPermissionManager mPermissionManager;
-
private final FeatureConfig mFeatureConfig;
- AppsFilter(FeatureConfig featureConfig, IPermissionManager permissionManager,
- String[] forceQueryableWhitelist, boolean systemAppsQueryable) {
+ AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
+ boolean systemAppsQueryable) {
mFeatureConfig = featureConfig;
- final HashSet<String> forceQueryableByDeviceSet = new HashSet<>();
- Collections.addAll(forceQueryableByDeviceSet, forceQueryableWhitelist);
- this.mForceQueryableByDevice = Collections.unmodifiableSet(forceQueryableByDeviceSet);
- this.mForceQueryable = new HashSet<>();
- mPermissionManager = permissionManager;
+ mForceQueryableByDevicePackageNames = forceQueryableWhitelist;
mSystemAppsQueryable = systemAppsQueryable;
}
@@ -127,7 +118,6 @@ public class AppsFilter {
/** @return true if the feature is enabled for the given package. */
boolean packageIsEnabled(PackageParser.Package pkg);
-
}
private static class FeatureConfigImpl implements FeatureConfig {
@@ -174,7 +164,6 @@ public class AppsFilter {
}
}
-
public static AppsFilter create(PackageManagerService.Injector injector) {
final boolean forceSystemAppsQueryable =
injector.getContext().getResources()
@@ -192,15 +181,12 @@ public class AppsFilter {
forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
}
}
- IPermissionManager permissionmgr =
- (IPermissionManager) ServiceManager.getService("permissionmgr");
-
- return new AppsFilter(featureConfig, permissionmgr, forcedQueryablePackageNames,
+ return new AppsFilter(featureConfig, forcedQueryablePackageNames,
forceSystemAppsQueryable);
}
/** Returns true if the querying package may query for the potential target package */
- private static boolean canQuery(PackageParser.Package querying,
+ private static boolean canQueryViaIntent(PackageParser.Package querying,
PackageParser.Package potentialTarget) {
if (querying.mQueriesIntents == null) {
return false;
@@ -274,22 +260,14 @@ public class AppsFilter {
* Grants access based on an interaction between a calling and target package, granting
* visibility of the caller from the target.
*
- * @param callingPackage the package initiating the interaction
- * @param targetPackage the package being interacted with and thus gaining visibility of the
- * initiating package.
- * @param userId the user in which this interaction was taking place
+ * @param callingUid the uid initiating the interaction
+ * @param targetUid the uid being interacted with and thus gaining visibility of the
+ * initiating uid.
*/
- public void grantImplicitAccess(
- String callingPackage, String targetPackage, int userId) {
- HashMap<String, Set<String>> currentUser = mImplicitlyQueryable.get(userId);
- if (currentUser == null) {
- currentUser = new HashMap<>();
- mImplicitlyQueryable.put(userId, currentUser);
+ public void grantImplicitAccess(int callingUid, int targetUid) {
+ if (mImplicitlyQueryable.add(targetUid, callingUid) && DEBUG_LOGGING) {
+ Slog.wtf(TAG, "implicit access granted: " + callingUid + " -> " + targetUid);
}
- if (!currentUser.containsKey(targetPackage)) {
- currentUser.put(targetPackage, new HashSet<>());
- }
- currentUser.get(targetPackage).add(callingPackage);
}
public void onSystemReady() {
@@ -299,50 +277,57 @@ public class AppsFilter {
/**
* Adds a package that should be considered when filtering visibility between apps.
*
- * @param newPkg the new package being added
- * @param existing all other packages currently on the device.
+ * @param newPkgSetting the new setting being added
+ * @param existingSettings all other settings currently on the device.
*/
- public void addPackage(PackageParser.Package newPkg,
- Map<String, PackageParser.Package> existing) {
+ public void addPackage(PackageSetting newPkgSetting,
+ ArrayMap<String, PackageSetting> existingSettings) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
try {
- // let's re-evaluate the ability of already added packages to see this new package
- if (newPkg.mForceQueryable
- || (mSystemAppsQueryable && (newPkg.isSystem()
- || newPkg.isUpdatedSystemApp()))) {
- mForceQueryable.add(newPkg.packageName);
- } else {
- for (String packageName : mQueriesViaIntent.keySet()) {
- if (packageName == newPkg.packageName) {
- continue;
- }
- final PackageParser.Package existingPackage = existing.get(packageName);
- if (canQuery(existingPackage, newPkg)) {
- mQueriesViaIntent.get(packageName).add(newPkg.packageName);
- }
- }
- }
- // if the new package declares them, let's evaluate its ability to see existing packages
- mQueriesViaIntent.put(newPkg.packageName, new HashSet<>());
- for (PackageParser.Package existingPackage : existing.values()) {
- if (existingPackage.packageName == newPkg.packageName) {
+ final PackageParser.Package newPkg = newPkgSetting.pkg;
+ if (newPkg == null) {
+ // nothing to add
+ return;
+ }
+
+ final boolean newIsForceQueryable =
+ mForceQueryable.contains(newPkgSetting.appId)
+ /* shared user that is already force queryable */
+ || newPkg.mForceQueryable
+ || (newPkgSetting.isSystem() && (mSystemAppsQueryable
+ || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
+ newPkg.packageName)));
+ if (newIsForceQueryable) {
+ mForceQueryable.add(newPkgSetting.appId);
+ }
+
+ for (int i = existingSettings.size() - 1; i >= 0; i--) {
+ final PackageSetting existingSetting = existingSettings.valueAt(i);
+ if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) {
continue;
}
- if (existingPackage.mForceQueryable
- || (mSystemAppsQueryable
- && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) {
- continue;
+ final PackageParser.Package existingPkg = existingSetting.pkg;
+ // let's evaluate the ability of already added packages to see this new package
+ if (!newIsForceQueryable) {
+ if (canQueryViaIntent(existingPkg, newPkg)) {
+ mQueriesViaIntent.add(existingSetting.appId, newPkgSetting.appId);
+ }
+ if (existingPkg.mQueriesPackages != null
+ && existingPkg.mQueriesPackages.contains(newPkg.packageName)) {
+ mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ }
}
- if (canQuery(newPkg, existingPackage)) {
- mQueriesViaIntent.get(newPkg.packageName).add(existingPackage.packageName);
+ // now we'll evaluate our new package's ability to see existing packages
+ if (!mForceQueryable.contains(existingSetting.appId)) {
+ if (canQueryViaIntent(newPkg, existingPkg)) {
+ mQueriesViaIntent.add(newPkgSetting.appId, existingSetting.appId);
+ }
+ if (newPkg.mQueriesPackages != null
+ && newPkg.mQueriesPackages.contains(existingPkg.packageName)) {
+ mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+ }
}
}
- final HashSet<String> queriesPackages = new HashSet<>(
- newPkg.mQueriesPackages == null ? 0 : newPkg.mQueriesPackages.size());
- if (newPkg.mQueriesPackages != null) {
- queriesPackages.addAll(newPkg.mQueriesPackages);
- }
- mQueriesViaPackage.put(newPkg.packageName, queriesPackages);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -351,24 +336,41 @@ public class AppsFilter {
/**
* Removes a package for consideration when filtering visibility between apps.
*
- * @param packageName the name of the package being removed.
+ * @param setting the setting of the package being removed.
+ * @param allUsers array of all current users on device.
*/
- public void removePackage(String packageName) {
- mForceQueryable.remove(packageName);
+ public void removePackage(PackageSetting setting, int[] allUsers,
+ ArrayMap<String, PackageSetting> existingSettings) {
+ mForceQueryable.remove(setting.appId);
- for (int i = 0; i < mImplicitlyQueryable.size(); i++) {
- mImplicitlyQueryable.valueAt(i).remove(packageName);
- for (Set<String> initiators : mImplicitlyQueryable.valueAt(i).values()) {
- initiators.remove(packageName);
+ for (int u = 0; u < allUsers.length; u++) {
+ final int userId = allUsers[u];
+ final int removingUid = UserHandle.getUid(userId, setting.appId);
+ mImplicitlyQueryable.remove(removingUid);
+ for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
+ mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid);
}
}
- mQueriesViaIntent.remove(packageName);
- for (Set<String> declarators : mQueriesViaIntent.values()) {
- declarators.remove(packageName);
+ mQueriesViaIntent.remove(setting.appId);
+ for (int i = mQueriesViaIntent.size() - 1; i >= 0; i--) {
+ mQueriesViaIntent.remove(mQueriesViaIntent.keyAt(i), setting.appId);
+ }
+ mQueriesViaPackage.remove(setting.appId);
+ for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
+ mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
}
- mQueriesViaPackage.remove(packageName);
+ // re-add other shared user members to re-establish visibility between them and other
+ // packages
+ if (setting.sharedUser != null) {
+ for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
+ if (setting.sharedUser.packages.valueAt(i) == setting) {
+ continue;
+ }
+ addPackage(setting.sharedUser.packages.valueAt(i), existingSettings);
+ }
+ }
}
/**
@@ -385,6 +387,25 @@ public class AppsFilter {
PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
try {
+ if (!shouldFilterApplicationInternal(callingUid, callingSetting,
+ targetPkgSetting,
+ userId)) {
+ return false;
+ }
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting,
+ DEBUG_ALLOW_ALL ? "ALLOWED" : "BLOCKED", new RuntimeException());
+ }
+ return !DEBUG_ALLOW_ALL;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private boolean shouldFilterApplicationInternal(int callingUid,
+ SettingBase callingSetting, PackageSetting targetPkgSetting, int userId) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
+ try {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
if (!featureEnabled) {
if (DEBUG_LOGGING) {
@@ -402,85 +423,37 @@ public class AppsFilter {
Slog.wtf(TAG, "No setting found for non system uid " + callingUid);
return true;
}
- PackageSetting callingPkgSetting = null;
+ final PackageSetting callingPkgSetting;
+ final ArraySet<PackageSetting> callingSharedPkgSettings;
if (callingSetting instanceof PackageSetting) {
callingPkgSetting = (PackageSetting) callingSetting;
- if (!shouldFilterApplicationInternal(callingPkgSetting, targetPkgSetting,
- userId)) {
+ callingSharedPkgSettings = null;
+ } else {
+ callingPkgSetting = null;
+ callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages;
+ }
+
+ if (callingPkgSetting != null) {
+ if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "DISABLED");
+ }
return false;
}
- } else if (callingSetting instanceof SharedUserSetting) {
- final ArraySet<PackageSetting> packageSettings =
- ((SharedUserSetting) callingSetting).packages;
- if (packageSettings != null && packageSettings.size() > 0) {
- for (int i = 0, max = packageSettings.size(); i < max; i++) {
- final PackageSetting packageSetting = packageSettings.valueAt(i);
- if (!shouldFilterApplicationInternal(packageSetting, targetPkgSetting,
- userId)) {
- return false;
- }
- if (callingPkgSetting == null && packageSetting.pkg != null) {
- callingPkgSetting = packageSetting;
+ } else {
+ for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
+ if (!mFeatureConfig.packageIsEnabled(callingSharedPkgSettings.valueAt(i).pkg)) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "DISABLED");
}
- }
- if (callingPkgSetting == null) {
- Slog.wtf(TAG, callingSetting + " does not have any non-null packages!");
- return true;
- }
- } else {
- Slog.wtf(TAG, callingSetting + " has no packages!");
- return true;
- }
- }
-
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting,
- DEBUG_ALLOW_ALL ? "ALLOWED" : "BLOCKED");
- }
- return !DEBUG_ALLOW_ALL;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- private boolean shouldFilterApplicationInternal(
- PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, int userId) {
- return shouldFilterApplicationInternal(callingPkgSetting, targetPkgSetting, userId,
- true /*expandSharedUser*/);
- }
-
- /**
- * @param expandSharedUser true if all members of the shared user a target may belong to should
- * be considered
- */
- private boolean shouldFilterApplicationInternal(
- PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, int userId,
- boolean expandSharedUser) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
- try {
- // special case shared user targets
- if (expandSharedUser && targetPkgSetting.sharedUser != null) {
- for (PackageSetting sharedMemberSetting : targetPkgSetting.sharedUser.packages) {
- if (!shouldFilterApplicationInternal(
- callingPkgSetting, sharedMemberSetting, userId,
- false /*expandSharedUser*/)) {
return false;
}
}
- return true;
}
- final String callingName = callingPkgSetting.pkg.packageName;
- final PackageParser.Package targetPkg = targetPkgSetting.pkg;
-
- if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "DISABLED");
- }
- return false;
- }
// This package isn't technically installed and won't be written to settings, so we can
// treat it as filtered until it's available again.
+ final PackageParser.Package targetPkg = targetPkgSetting.pkg;
if (targetPkg == null) {
if (DEBUG_LOGGING) {
Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
@@ -488,85 +461,64 @@ public class AppsFilter {
return true;
}
final String targetName = targetPkg.packageName;
- if (callingPkgSetting.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "caller pre-R");
- }
- return false;
- }
- if (callingPkgSetting.appId == targetPkgSetting.appId) {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "same app id");
- }
- return false;
+ final int callingAppId;
+ if (callingPkgSetting != null) {
+ callingAppId = callingPkgSetting.appId;
+ } else {
+ callingAppId = callingSharedPkgSettings.valueAt(0).appId; // all should be the same
}
- if (isImplicitlyQueryableSystemApp(targetPkgSetting)) {
+ final int targetAppId = targetPkgSetting.appId;
+ if (callingAppId == targetAppId) {
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "implicitly queryable sys");
+ log(callingSetting, targetPkgSetting, "same app id");
}
return false;
}
- if (targetPkg.mForceQueryable) {
+
+ if (callingSetting.getPermissionsState().hasPermission(
+ Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "manifest forceQueryable");
+ log(callingSetting, targetPkgSetting, "has query-all permission");
}
return false;
}
- if (mForceQueryable.contains(targetName)) {
+ if (mForceQueryable.contains(targetAppId)) {
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "whitelist forceQueryable");
+ log(callingSetting, targetPkgSetting, "force queryable");
}
return false;
}
- if (mQueriesViaPackage.containsKey(callingName)
- && mQueriesViaPackage.get(callingName).contains(
- targetName)) {
+ if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
// the calling package has explicitly declared the target package; allow
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "queries package");
+ log(callingSetting, targetPkgSetting, "queries package");
}
return false;
- } else if (mQueriesViaIntent.containsKey(callingName)
- && mQueriesViaIntent.get(callingName).contains(targetName)) {
+ } else if (mQueriesViaIntent.contains(callingAppId, targetAppId)) {
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "queries intent");
+ log(callingSetting, targetPkgSetting, "queries intent");
}
return false;
}
- if (mImplicitlyQueryable.get(userId) != null
- && mImplicitlyQueryable.get(userId).containsKey(callingName)
- && mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) {
+
+ final int targetUid = UserHandle.getUid(userId, targetAppId);
+ if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "implicitly queryable for user");
+ log(callingSetting, targetPkgSetting, "implicitly queryable for user");
}
return false;
}
- final ArrayList<PackageParser.Instrumentation> inst =
- callingPkgSetting.pkg.instrumentation;
- if (inst.size() > 0) {
- for (int i = 0, max = inst.size(); i < max; i++) {
- if (inst.get(i).info.targetPackage == targetName) {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "instrumentation");
- }
- return false;
- }
+ if (callingPkgSetting != null) {
+ if (callingPkgInstruments(callingPkgSetting, targetPkgSetting, targetName)) {
+ return false;
}
- }
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.checkPermission");
- try {
- if (mPermissionManager.checkPermission(
- Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId)
- == PackageManager.PERMISSION_GRANTED) {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "permission");
+ } else {
+ for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
+ if (callingPkgInstruments(callingSharedPkgSettings.valueAt(i),
+ targetPkgSetting, targetName)) {
+ return false;
}
- return false;
}
- } catch (RemoteException e) {
- return true;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
return true;
} finally {
@@ -574,63 +526,115 @@ public class AppsFilter {
}
}
- private static void log(PackageSetting callingPkgSetting, PackageSetting targetPkgSetting,
+ private static boolean callingPkgInstruments(PackageSetting callingPkgSetting,
+ PackageSetting targetPkgSetting,
+ String targetName) {
+ final ArrayList<PackageParser.Instrumentation> inst = callingPkgSetting.pkg.instrumentation;
+ for (int i = inst.size() - 1; i >= 0; i--) {
+ if (inst.get(i).info.targetPackage == targetName) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "instrumentation");
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void log(SettingBase callingPkgSetting, PackageSetting targetPkgSetting,
String description) {
- Slog.wtf(TAG,
- "interaction: " + callingPkgSetting.name + " -> " + targetPkgSetting.name + " "
- + description);
+ log(callingPkgSetting, targetPkgSetting, description, null);
}
- private boolean isImplicitlyQueryableSystemApp(PackageSetting targetPkgSetting) {
- return targetPkgSetting.isSystem() && (mSystemAppsQueryable
- || mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName));
+ private static void log(SettingBase callingPkgSetting, PackageSetting targetPkgSetting,
+ String description, Throwable throwable) {
+ Slog.wtf(TAG,
+ "interaction: " + callingPkgSetting.toString()
+ + " -> " + targetPkgSetting.name + " "
+ + description, throwable);
}
public void dumpQueries(
- PrintWriter pw, @Nullable String filteringPackageName, DumpState dumpState,
+ PrintWriter pw, PackageManagerService pms, @Nullable Integer filteringAppId,
+ DumpState dumpState,
int[] users) {
+ final SparseArray<String> cache = new SparseArray<>();
+ ToString<Integer> expandPackages = input -> {
+ String cachedValue = cache.get(input);
+ if (cachedValue == null) {
+ final String[] packagesForUid = pms.getPackagesForUid(input);
+ if (packagesForUid == null) {
+ cachedValue = "[unknown app id " + input + "]";
+ } else {
+ cachedValue = packagesForUid.length == 1 ? packagesForUid[0]
+ : "[" + TextUtils.join(",", packagesForUid) + "]";
+ }
+ cache.put(input, cachedValue);
+ }
+ return cachedValue;
+ };
pw.println();
pw.println("Queries:");
dumpState.onTitlePrinted();
+ if (!mFeatureConfig.isGloballyEnabled()) {
+ pw.println(" DISABLED");
+ if (!DEBUG_LOGGING) {
+ return;
+ }
+ }
pw.println(" system apps queryable: " + mSystemAppsQueryable);
- dumpPackageSet(pw, filteringPackageName, mForceQueryableByDevice, "System whitelist", " ");
- dumpPackageSet(pw, filteringPackageName, mForceQueryable, "forceQueryable", " ");
+ dumpPackageSet(pw, filteringAppId, mForceQueryable, "forceQueryable", " ", expandPackages);
pw.println(" queries via package name:");
- dumpQueriesMap(pw, filteringPackageName, mQueriesViaPackage, " ");
+ dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages);
pw.println(" queries via intent:");
- dumpQueriesMap(pw, filteringPackageName, mQueriesViaIntent, " ");
+ dumpQueriesMap(pw, filteringAppId, mQueriesViaIntent, " ", expandPackages);
pw.println(" queryable via interaction:");
for (int user : users) {
pw.append(" User ").append(Integer.toString(user)).println(":");
- final HashMap<String, Set<String>> queryMapForUser = mImplicitlyQueryable.get(user);
- if (queryMapForUser != null) {
- dumpQueriesMap(pw, filteringPackageName, queryMapForUser, " ");
- }
+ dumpQueriesMap(pw,
+ filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
+ mImplicitlyQueryable, " ", expandPackages);
}
}
- private static void dumpQueriesMap(PrintWriter pw, @Nullable String filteringPackageName,
- HashMap<String, Set<String>> queriesMap, String spacing) {
- for (String callingPkg : queriesMap.keySet()) {
- if (Objects.equals(callingPkg, filteringPackageName)) {
- // don't filter target package names if the calling is filteringPackageName
- dumpPackageSet(pw, null /*filteringPackageName*/, queriesMap.get(callingPkg),
- callingPkg, spacing);
+ private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId,
+ SparseSetArray<Integer> queriesMap, String spacing,
+ @Nullable ToString<Integer> toString) {
+ for (int i = 0; i < queriesMap.size(); i++) {
+ Integer callingId = queriesMap.keyAt(i);
+ if (Objects.equals(callingId, filteringId)) {
+ // don't filter target package names if the calling is filteringId
+ dumpPackageSet(
+ pw, null /*filteringId*/, queriesMap.get(callingId),
+ toString == null
+ ? callingId.toString()
+ : toString.toString(callingId),
+ spacing, toString);
} else {
- dumpPackageSet(pw, filteringPackageName, queriesMap.get(callingPkg), callingPkg,
- spacing);
+ dumpPackageSet(
+ pw, filteringId, queriesMap.get(callingId),
+ toString == null
+ ? callingId.toString()
+ : toString.toString(callingId),
+ spacing, toString);
}
}
}
- private static void dumpPackageSet(PrintWriter pw, @Nullable String filteringPackageName,
- Set<String> targetPkgSet, String subTitle, String spacing) {
+ private interface ToString<T> {
+ String toString(T input);
+ }
+
+ private static <T> void dumpPackageSet(PrintWriter pw, @Nullable T filteringId,
+ Set<T> targetPkgSet, String subTitle, String spacing,
+ @Nullable ToString<T> toString) {
if (targetPkgSet != null && targetPkgSet.size() > 0
- && (filteringPackageName == null || targetPkgSet.contains(filteringPackageName))) {
+ && (filteringId == null || targetPkgSet.contains(filteringId))) {
pw.append(spacing).append(subTitle).println(":");
- for (String pkgName : targetPkgSet) {
- if (filteringPackageName == null || Objects.equals(filteringPackageName, pkgName)) {
- pw.append(spacing).append(" ").println(pkgName);
+ for (T item : targetPkgSet) {
+ if (filteringId == null || Objects.equals(filteringId, item)) {
+ pw.append(spacing).append(" ")
+ .println(toString == null ? item : toString.toString(item));
}
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 2b6c347fe726..bd9566728b1b 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -304,7 +304,7 @@ public class LauncherAppsService extends SystemService {
long ident = injectClearCallingIdentity();
try {
final UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
- if (callingUserInfo != null && callingUserInfo.isManagedProfile()) {
+ if (callingUserInfo != null && callingUserInfo.isProfile()) {
Slog.w(TAG, message + " for another profile "
+ targetUserId + " from " + callingUserId + " not allowed");
return false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 6564e71936eb..93249e9e311c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -26,7 +26,6 @@ import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PackageDeleteObserver;
-import android.app.PackageInstallObserver;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -1015,74 +1014,57 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
- static class PackageInstallObserverAdapter extends PackageInstallObserver {
- private final Context mContext;
- private final IntentSender mTarget;
- private final int mSessionId;
- private final boolean mShowNotification;
- private final int mUserId;
-
- public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
- boolean showNotification, int userId) {
- mContext = context;
- mTarget = target;
- mSessionId = sessionId;
- mShowNotification = showNotification;
- mUserId = userId;
+ static void sendOnUserActionRequired(Context context, IntentSender target, int sessionId,
+ Intent intent) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_PENDING_USER_ACTION);
+ fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+ try {
+ target.sendIntent(context, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
}
+ }
- @Override
- public void onUserActionRequired(Intent intent) {
- final Intent fillIn = new Intent();
- fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_PENDING_USER_ACTION);
- fillIn.putExtra(Intent.EXTRA_INTENT, intent);
- try {
- mTarget.sendIntent(mContext, 0, fillIn, null, null);
- } catch (SendIntentException ignored) {
+ static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
+ boolean showNotification, int userId, String basePackageName, int returnCode,
+ String msg, Bundle extras) {
+ if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
+ boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
+ Notification notification = buildSuccessNotification(context,
+ context.getResources()
+ .getString(update ? R.string.package_updated_device_owner :
+ R.string.package_installed_device_owner),
+ basePackageName,
+ userId);
+ if (notification != null) {
+ NotificationManager notificationManager = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(basePackageName,
+ SystemMessage.NOTE_PACKAGE_STATE,
+ notification);
}
}
-
- @Override
- public void onPackageInstalled(String basePackageName, int returnCode, String msg,
- Bundle extras) {
- if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
- boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
- Notification notification = buildSuccessNotification(mContext,
- mContext.getResources()
- .getString(update ? R.string.package_updated_device_owner :
- R.string.package_installed_device_owner),
- basePackageName,
- mUserId);
- if (notification != null) {
- NotificationManager notificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(basePackageName,
- SystemMessage.NOTE_PACKAGE_STATE,
- notification);
- }
- }
- final Intent fillIn = new Intent();
- fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
- fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageManager.installStatusToPublicStatus(returnCode));
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
- PackageManager.installStatusToString(returnCode, msg));
- fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
- if (extras != null) {
- final String existing = extras.getString(
- PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
- if (!TextUtils.isEmpty(existing)) {
- fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
- }
- }
- try {
- mTarget.sendIntent(mContext, 0, fillIn, null, null);
- } catch (SendIntentException ignored) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
+ fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageManager.installStatusToPublicStatus(returnCode));
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ PackageManager.installStatusToString(returnCode, msg));
+ fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+ if (extras != null) {
+ final String existing = extras.getString(
+ PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
+ if (!TextUtils.isEmpty(existing)) {
+ fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
}
}
+ try {
+ target.sendIntent(context, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 38649a772b8f..d5f4ff236b24 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -18,7 +18,6 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -79,7 +78,6 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.Process;
-import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -107,7 +105,6 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
import com.android.server.pm.dex.DexManager;
import com.android.server.security.VerityUtils;
@@ -131,7 +128,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG = "PackageInstallerSession";
private static final boolean LOGD = true;
- private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
+ private static final String REMOVE_MARKER_EXTENSION = ".removed";
private static final int MSG_COMMIT = 1;
private static final int MSG_ON_PACKAGE_INSTALLED = 2;
@@ -261,7 +258,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private final ArrayList<FileBridge> mBridges = new ArrayList<>();
@GuardedBy("mLock")
- private IPackageInstallObserver2 mRemoteObserver;
+ private IntentSender mRemoteStatusReceiver;
/** Fields derived from commit parsing */
@GuardedBy("mLock")
@@ -298,9 +295,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private File mResolvedBaseFile;
@GuardedBy("mLock")
- private File mResolvedStageDir;
-
- @GuardedBy("mLock")
private final List<File> mResolvedStagedFiles = new ArrayList<>();
@GuardedBy("mLock")
private final List<File> mResolvedInheritedFiles = new ArrayList<>();
@@ -319,7 +313,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Installers can't stage directories, so it's fine to ignore
// entries like "lost+found".
if (file.isDirectory()) return false;
- if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+ if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
if (DexMetadataHelper.isDexMetadataFile(file)) return false;
if (VerityUtils.isFsveritySignatureFile(file)) return false;
return true;
@@ -329,7 +323,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public boolean accept(File file) {
if (file.isDirectory()) return false;
- if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+ if (!file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
return true;
}
};
@@ -346,14 +340,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String packageName = (String) args.arg1;
final String message = (String) args.arg2;
final Bundle extras = (Bundle) args.arg3;
- final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;
+ final IntentSender statusReceiver = (IntentSender) args.arg4;
final int returnCode = args.argi1;
args.recycle();
- try {
- observer.onPackageInstalled(packageName, returnCode, message, extras);
- } catch (RemoteException ignored) {
- }
+ PackageInstallerService.sendOnPackageInstalled(mContext,
+ statusReceiver, sessionId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId,
+ packageName, returnCode, message, extras);
break;
}
@@ -564,23 +558,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- /**
- * Resolve the actual location where staged data should be written. This
- * might point at an ASEC mount point, which is why we delay path resolution
- * until someone actively works with the session.
- */
- @GuardedBy("mLock")
- private File resolveStageDirLocked() throws IOException {
- if (mResolvedStageDir == null) {
- if (stageDir != null) {
- mResolvedStageDir = stageDir;
- } else {
- throw new IOException("Missing stageDir");
- }
- }
- return mResolvedStageDir;
- }
-
@Override
public void setClientProgress(float progress) {
synchronized (mLock) {
@@ -620,14 +597,32 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
- try {
- return resolveStageDirLocked().list();
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
- }
+ return getNamesLocked();
}
}
+ @GuardedBy("mLock")
+ private String[] getNamesLocked() {
+ return stageDir.list();
+ }
+
+ private static File[] filterFiles(File parent, String[] names, FileFilter filter) {
+ return Arrays.stream(names).map(name -> new File(parent, name)).filter(
+ file -> filter.accept(file)).toArray(File[]::new);
+ }
+
+ @GuardedBy("mLock")
+ private File[] getAddedFilesLocked() {
+ String[] names = getNamesLocked();
+ return filterFiles(stageDir, names, sAddedFilter);
+ }
+
+ @GuardedBy("mLock")
+ private File[] getRemovedFilesLocked() {
+ String[] names = getNamesLocked();
+ return filterFiles(stageDir, names, sRemovedFilter);
+ }
+
@Override
public void removeSplit(String splitName) {
if (TextUtils.isEmpty(params.appPackageName)) {
@@ -646,13 +641,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private static String getRemoveMarkerName(String name) {
+ final String markerName = name + REMOVE_MARKER_EXTENSION;
+ if (!FileUtils.isValidExtFilename(markerName)) {
+ throw new IllegalArgumentException("Invalid marker: " + markerName);
+ }
+ return markerName;
+ }
+
private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
try {
- final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
- if (!FileUtils.isValidExtFilename(markerName)) {
- throw new IllegalArgumentException("Invalid marker: " + markerName);
- }
- final File target = new File(resolveStageDirLocked(), markerName);
+ final File target = new File(stageDir, getRemoveMarkerName(splitName));
target.createNewFile();
Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
} catch (ErrnoException e) {
@@ -686,7 +685,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// will block any attempted install transitions.
final RevocableFileDescriptor fd;
final FileBridge bridge;
- final File stageDir;
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("openWrite");
@@ -700,8 +698,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
bridge = new FileBridge();
mBridges.add(bridge);
}
-
- stageDir = resolveStageDirLocked();
}
try {
@@ -807,7 +803,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(resolveStageDirLocked(), name);
+ final File target = new File(stageDir, name);
final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);
return new ParcelFileDescriptor(targetFd);
} catch (ErrnoException e) {
@@ -953,7 +949,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* This method may be called multiple times to update the status receiver validate caller
* permissions.
*/
- public boolean markAsCommitted(
+ private boolean markAsCommitted(
@NonNull IntentSender statusReceiver, boolean forTransfer) {
Preconditions.checkNotNull(statusReceiver);
@@ -964,10 +960,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotDestroyedLocked("commit");
- final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
- mContext, statusReceiver, sessionId,
- isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);
- mRemoteObserver = adapter.getBinder();
+ mRemoteStatusReceiver = statusReceiver;
if (forTransfer) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
@@ -991,12 +984,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!mSealed) {
try {
sealAndValidateLocked(childSessions);
- } catch (IOException e) {
- throw new IllegalArgumentException(e);
} catch (PackageManagerException e) {
- // Do now throw an exception here to stay compatible with O and older
- destroyInternal();
- dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
return false;
}
}
@@ -1096,52 +1084,59 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*/
@GuardedBy("mLock")
private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
- throws PackageManagerException, IOException {
- assertNoWriteFileTransfersOpenLocked();
- assertPreparedAndNotDestroyedLocked("sealing of session");
-
- mSealed = true;
+ throws PackageManagerException {
+ try {
+ assertNoWriteFileTransfersOpenLocked();
+ assertPreparedAndNotDestroyedLocked("sealing of session");
- if (childSessions != null) {
- assertMultiPackageConsistencyLocked(childSessions);
- }
+ mSealed = true;
- if (params.isStaged) {
- final PackageInstallerSession activeSession = mStagingManager.getActiveSession();
- final boolean anotherSessionAlreadyInProgress =
- activeSession != null && sessionId != activeSession.sessionId
- && mParentSessionId != activeSession.sessionId;
- if (anotherSessionAlreadyInProgress) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
- "There is already in-progress committed staged session "
- + activeSession.sessionId, null);
+ if (childSessions != null) {
+ assertMultiPackageConsistencyLocked(childSessions);
}
- }
- // Read transfers from the original owner stay open, but as the session's data
- // cannot be modified anymore, there is no leak of information. For staged sessions,
- // further validation is performed by the staging manager.
- if (!params.isMultiPackage) {
- final PackageInfo pkgInfo = mPm.getPackageInfo(
- params.appPackageName, PackageManager.GET_SIGNATURES
- | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+ if (params.isStaged) {
+ final PackageInstallerSession activeSession = mStagingManager.getActiveSession();
+ final boolean anotherSessionAlreadyInProgress =
+ activeSession != null && sessionId != activeSession.sessionId
+ && mParentSessionId != activeSession.sessionId;
+ if (anotherSessionAlreadyInProgress) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+ "There is already in-progress committed staged session "
+ + activeSession.sessionId, null);
+ }
+ }
- resolveStageDirLocked();
+ // Read transfers from the original owner stay open, but as the session's data
+ // cannot be modified anymore, there is no leak of information. For staged sessions,
+ // further validation is performed by the staging manager.
+ if (!params.isMultiPackage) {
+ final PackageInfo pkgInfo = mPm.getPackageInfo(
+ params.appPackageName, PackageManager.GET_SIGNATURES
+ | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
- try {
- if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
- validateApexInstallLocked();
- } else {
- validateApkInstallLocked(pkgInfo);
+ try {
+ if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+ validateApexInstallLocked();
+ } else {
+ validateApkInstallLocked(pkgInfo);
+ }
+ } catch (PackageManagerException e) {
+ throw e;
+ } catch (Throwable e) {
+ // Convert all exceptions into package manager exceptions as only those are
+ // handled in the code above.
+ throw new PackageManagerException(e);
}
- } catch (PackageManagerException e) {
- throw e;
- } catch (Throwable e) {
- // Convert all exceptions into package manager exceptions as only those are handled
- // in the code above
- throw new PackageManagerException(e);
}
+ } catch (PackageManagerException e) {
+ // Session is sealed but could not be verified, we need to destroy it.
+ destroyInternal();
+ // Dispatch message to remove session from PackageInstallerService
+ dispatchSessionFinished(
+ e.error, ExceptionUtils.getCompleteMessage(e), null);
+ throw e;
}
}
@@ -1164,15 +1159,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
synchronized (mLock) {
try {
sealAndValidateLocked(childSessions);
- } catch (IOException e) {
- throw new IllegalStateException(e);
} catch (PackageManagerException e) {
Slog.e(TAG, "Package not valid", e);
- // Session is sealed but could not be verified, we need to destroy it.
- destroyInternal();
- // Dispatch message to remove session from PackageInstallerService
- dispatchSessionFinished(
- e.error, ExceptionUtils.getCompleteMessage(e), null);
}
}
}
@@ -1213,13 +1201,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
sealAndValidateLocked(childSessions);
- } catch (IOException e) {
- throw new IllegalStateException(e);
} catch (PackageManagerException e) {
- // Session is sealed but could not be verified, we need to destroy it
- destroyInternal();
- dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
-
throw new IllegalArgumentException("Package is not valid", e);
}
@@ -1304,11 +1286,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
if (!success) {
- try {
- mRemoteObserver.onPackageInstalled(
- null, failure.error, failure.getLocalizedMessage(), null);
- } catch (RemoteException ignored) {
- }
+ PackageInstallerService.sendOnPackageInstalled(mContext,
+ mRemoteStatusReceiver, sessionId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
+ failure.error, failure.getLocalizedMessage(), null);
return;
}
mPm.installStage(activeChildSessions);
@@ -1352,10 +1333,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- try {
- mRemoteObserver.onUserActionRequired(intent);
- } catch (RemoteException ignored) {
- }
+
+ PackageInstallerService.sendOnUserActionRequired(mContext,
+ mRemoteStatusReceiver, sessionId, intent);
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
@@ -1368,7 +1348,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
try {
final List<File> fromFiles = mResolvedInheritedFiles;
- final File toDir = resolveStageDirLocked();
+ final File toDir = stageDir;
if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
@@ -1418,8 +1398,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
computeProgressLocked(true);
// Unpack native libraries
- extractNativeLibraries(mResolvedStageDir, params.abiOverride,
- mayInheritNativeLibs());
+ extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
}
// We've reached point of no return; call into PMS to install the stage.
@@ -1479,7 +1458,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private void validateApexInstallLocked()
throws PackageManagerException {
- final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+ final File[] addedFiles = getAddedFilesLocked();
if (ArrayUtils.isEmpty(addedFiles)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
@@ -1489,13 +1468,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Too many files for apex install");
}
- try {
- resolveStageDirLocked();
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to resolve stage location", e);
- }
-
File addedFile = addedFiles[0]; // there is only one file
// Ensure file name has proper suffix
@@ -1508,7 +1480,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Invalid filename: " + targetName);
}
- final File targetFile = new File(mResolvedStageDir, targetName);
+ final File targetFile = new File(stageDir, targetName);
resolveAndStageFile(addedFile, targetFile);
mResolvedBaseFile = targetFile;
@@ -1549,25 +1521,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
&& params.mode == SessionParams.MODE_INHERIT_EXISTING
&& VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
- try {
- resolveStageDirLocked();
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to resolve stage location", e);
- }
-
- final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
+ final File[] removedFiles = getRemovedFilesLocked();
final List<String> removeSplitList = new ArrayList<>();
if (!ArrayUtils.isEmpty(removedFiles)) {
for (File removedFile : removedFiles) {
final String fileName = removedFile.getName();
final String splitName = fileName.substring(
- 0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
+ 0, fileName.length() - REMOVE_MARKER_EXTENSION.length());
removeSplitList.add(splitName);
}
}
- final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+ final File[] addedFiles = getAddedFilesLocked();
if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
@@ -1611,7 +1576,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Invalid filename: " + targetName);
}
- final File targetFile = new File(mResolvedStageDir, targetName);
+ final File targetFile = new File(stageDir, targetName);
resolveAndStageFile(addedFile, targetFile);
// Base is coming from session
@@ -1626,7 +1591,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Invalid filename: " + dexMetadataFile);
}
- final File targetDexMetadataFile = new File(mResolvedStageDir,
+ final File targetDexMetadataFile = new File(stageDir,
DexMetadataHelper.buildDexMetadataPathForApk(targetName));
resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
}
@@ -2186,17 +2151,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
- final IPackageInstallObserver2 observer;
+ final IntentSender statusReceiver;
final String packageName;
synchronized (mLock) {
mFinalStatus = returnCode;
mFinalMessage = msg;
- observer = mRemoteObserver;
+ statusReceiver = mRemoteStatusReceiver;
packageName = mPackageName;
}
- if (observer != null) {
+ if (statusReceiver != null) {
// Execute observer.onPackageInstalled on different tread as we don't want callers
// inside the system server have to worry about catching the callbacks while they are
// calling into the session
@@ -2204,7 +2169,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
args.arg1 = packageName;
args.arg2 = msg;
args.arg3 = extras;
- args.arg4 = observer;
+ args.arg4 = statusReceiver;
args.argi1 = returnCode;
mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fe82f7c467ea..fa98c962afbf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11701,7 +11701,7 @@ public class PackageManagerService extends IPackageManager.Stub
ksms.addScannedPackageLPw(pkg);
mComponentResolver.addAllComponents(pkg, chatty);
- mAppsFilter.addPackage(pkg, mPackages);
+ mAppsFilter.addPackage(pkgSetting, mSettings.mPackages);
// Don't allow ephemeral applications to define new permissions groups.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
@@ -11889,31 +11889,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- void removeInstalledPackageLI(PackageParser.Package pkg, boolean chatty) {
- if (DEBUG_INSTALL) {
- if (chatty)
- Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName);
- }
-
- // writer
- synchronized (mLock) {
- // Remove the parent package
- mPackages.remove(pkg.applicationInfo.packageName);
- cleanPackageDataStructuresLILPw(pkg, chatty);
-
- // Remove the child packages
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- PackageParser.Package childPkg = pkg.childPackages.get(i);
- mPackages.remove(childPkg.applicationInfo.packageName);
- cleanPackageDataStructuresLILPw(childPkg, chatty);
- }
- }
- }
-
void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) {
mComponentResolver.removeAllComponents(pkg, chatty);
- mAppsFilter.removePackage(pkg.packageName);
+ mAppsFilter.removePackage((PackageSetting) pkg.mExtras,
+ mInjector.getUserManagerInternal().getUserIds(), mSettings.mPackages);
mPermissionManager.removeAllPermissions(pkg, chatty);
final int instrumentationSize = pkg.instrumentation.size();
@@ -20861,7 +20840,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
- mAppsFilter.dumpQueries(pw, packageName, dumpState, mUserManager.getUserIds());
+ final PackageSetting setting = mSettings.getPackageLPr(packageName);
+ Integer filteringAppId = setting == null ? null : setting.appId;
+ mAppsFilter.dumpQueries(
+ pw, this, filteringAppId, dumpState,
+ mUserManager.getUserIds());
}
if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
@@ -23195,8 +23178,9 @@ public class PackageManagerService extends IPackageManager.Stub
int callingUid, int targetAppId) {
synchronized (mLock) {
final PackageParser.Package callingPackage = getPackage(callingUid);
+ final int targetUid = UserHandle.getUid(userId, targetAppId);
final PackageParser.Package targetPackage =
- getPackage(UserHandle.getUid(userId, targetAppId));
+ getPackage(targetUid);
if (callingPackage == null || targetPackage == null) {
return;
}
@@ -23207,8 +23191,7 @@ public class PackageManagerService extends IPackageManager.Stub
mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
UserHandle.getAppId(callingUid), targetAppId);
} else {
- mAppsFilter.grantImplicitAccess(
- callingPackage.packageName, targetPackage.packageName, userId);
+ mAppsFilter.grantImplicitAccess(callingUid, targetUid);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 525d3578ccff..232374c6773e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2453,27 +2453,40 @@ class PackageManagerShellCommand extends ShellCommand {
String name;
int userId = -1;
int flags = 0;
+ String userType = null;
String opt;
boolean preCreateOnly = false;
while ((opt = getNextOption()) != null) {
+ String newUserType = null;
if ("--profileOf".equals(opt)) {
userId = UserHandle.parseUserArg(getNextArgRequired());
} else if ("--managed".equals(opt)) {
- flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ newUserType = UserManager.USER_TYPE_PROFILE_MANAGED;
} else if ("--restricted".equals(opt)) {
- flags |= UserInfo.FLAG_RESTRICTED;
- } else if ("--ephemeral".equals(opt)) {
- flags |= UserInfo.FLAG_EPHEMERAL;
+ newUserType = UserManager.USER_TYPE_FULL_RESTRICTED;
} else if ("--guest".equals(opt)) {
- flags |= UserInfo.FLAG_GUEST;
+ newUserType = UserManager.USER_TYPE_FULL_GUEST;
} else if ("--demo".equals(opt)) {
- flags |= UserInfo.FLAG_DEMO;
+ newUserType = UserManager.USER_TYPE_FULL_DEMO;
+ } else if ("--ephemeral".equals(opt)) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
} else if ("--pre-create-only".equals(opt)) {
preCreateOnly = true;
+ } else if ("--user-type".equals(opt)) {
+ newUserType = getNextArgRequired();
} else {
getErrPrintWriter().println("Error: unknown option " + opt);
return 1;
}
+ // Ensure only one user-type was specified.
+ if (newUserType != null) {
+ if (userType != null && !userType.equals(newUserType)) {
+ getErrPrintWriter().println("Error: more than one user type was specified ("
+ + userType + " and " + newUserType + ")");
+ return 1;
+ }
+ userType = newUserType;
+ }
}
String arg = getNextArg();
if (arg == null && !preCreateOnly) {
@@ -2490,16 +2503,20 @@ class PackageManagerShellCommand extends ShellCommand {
ServiceManager.getService(Context.USER_SERVICE));
IAccountManager accm = IAccountManager.Stub.asInterface(
ServiceManager.getService(Context.ACCOUNT_SERVICE));
- if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
+ if (userType == null) {
+ userType = UserInfo.getDefaultUserType(flags);
+ }
+ if (UserManager.isUserTypeRestricted(userType)) {
// In non-split user mode, userId can only be SYSTEM
int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
info = um.createRestrictedProfile(name, parentUserId);
accm.addSharedAccountsFromParentUser(parentUserId, userId,
(Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
} else if (userId < 0) {
- info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
+ info = preCreateOnly ?
+ um.preCreateUser(userType) : um.createUser(name, userType, flags);
} else {
- info = um.createProfileForUser(name, flags, userId, null);
+ info = um.createProfileForUser(name, userType, flags, userId, null);
}
if (info != null) {
@@ -3421,9 +3438,15 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" Lists the current users.");
pw.println("");
pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
- pw.println(" [--guest] [--pre-create-only] USER_NAME");
+ pw.println(" [--guest] [--pre-create-only] [--user-type USER_TYPE] USER_NAME");
pw.println(" Create a new user with the given USER_NAME, printing the new user identifier");
pw.println(" of the user.");
+ // TODO(b/142482943): Consider fetching the list of user types from UMS.
+ pw.println(" USER_TYPE is the name of a user type, e.g. android.os.usertype.profile.MANAGED.");
+ pw.println(" If not specified, the default user type is android.os.usertype.full.SECONDARY.");
+ pw.println(" --managed is shorthand for '--user-type android.os.usertype.profile.MANAGED'.");
+ pw.println(" --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'.");
+ pw.println(" --guest is shorthand for '--user-type android.os.usertype.full.GUEST'.");
pw.println("");
pw.println(" remove-user USER_ID");
pw.println(" Remove the user with the given USER_IDENTIFIER, deleting all data");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5c9b9c93e4fe..8ddfad9ec81c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,8 +20,11 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.Manifest;
+import android.annotation.ColorRes;
+import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
@@ -77,6 +80,7 @@ import android.os.storage.StorageManager;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IntArray;
@@ -152,6 +156,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String TAG_NAME = "name";
private static final String TAG_ACCOUNT = "account";
private static final String ATTR_FLAGS = "flags";
+ private static final String ATTR_TYPE = "type";
private static final String ATTR_ICON_PATH = "icon";
private static final String ATTR_ID = "id";
private static final String ATTR_CREATION_TIME = "created";
@@ -220,15 +225,10 @@ public class UserManagerService extends IUserManager.Stub {
@VisibleForTesting
static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100;
- private static final int USER_VERSION = 8;
+ private static final int USER_VERSION = 9;
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
- // Maximum number of managed profiles permitted per user is 1. This cannot be increased
- // without first making sure that the rest of the framework is prepared for it.
- @VisibleForTesting
- static final int MAX_MANAGED_PROFILES = 1;
-
static final int WRITE_USER_MSG = 1;
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
@@ -304,6 +304,12 @@ public class UserManagerService extends IUserManager.Stub {
private final SparseArray<UserData> mUsers = new SparseArray<>();
/**
+ * Map of user type names to their corresponding {@link UserTypeDetails}.
+ * Should not be modified after UserManagerService constructor finishes.
+ */
+ private final ArrayMap<String, UserTypeDetails> mUserTypes;
+
+ /**
* User restrictions set via UserManager. This doesn't include restrictions set by
* device owner / profile owners. Only non-empty restriction bundles are stored.
*
@@ -543,6 +549,7 @@ public class UserManagerService extends IUserManager.Stub {
mPackagesLock = packagesLock;
mHandler = new MainHandler();
mUserDataPreparer = userDataPreparer;
+ mUserTypes = UserTypeFactory.getUserTypes();
synchronized (mPackagesLock) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
@@ -700,22 +707,37 @@ public class UserManagerService extends IUserManager.Stub {
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mUsersLock) {
- return getProfilesLU(userId, enabledOnly, returnFullInfo);
+ return getProfilesLU(userId, /* userType */ null, enabledOnly, returnFullInfo);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+ // TODO(b/142482943): Will probably need a getProfiles(userType). But permissions may vary.
+
@Override
public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
+ return getProfileIds(userId, null, enabledOnly);
+ }
+
+ // TODO(b/142482943): Probably @Override and make this accessible in UserManager.
+ /**
+ * Returns all the users of type userType that are in the same profile group as userId
+ * (including userId itself, if it is of the appropriate user type).
+ *
+ * <p>If userType is non-{@code null}, only returns users that are of type userType.
+ * If enabledOnly, only returns users that are not {@link UserInfo#FLAG_DISABLED}.
+ */
+ public int[] getProfileIds(@UserIdInt int userId, @Nullable String userType,
+ boolean enabledOnly) {
if (userId != UserHandle.getCallingUserId()) {
checkManageOrCreateUsersPermission("getting profiles related to user " + userId);
}
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mUsersLock) {
- return getProfileIdsLU(userId, enabledOnly).toArray();
+ return getProfileIdsLU(userId, userType, enabledOnly).toArray();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -724,9 +746,9 @@ public class UserManagerService extends IUserManager.Stub {
/** Assume permissions already checked and caller's identity cleared */
@GuardedBy("mUsersLock")
- private List<UserInfo> getProfilesLU(@UserIdInt int userId, boolean enabledOnly,
- boolean fullInfo) {
- IntArray profileIds = getProfileIdsLU(userId, enabledOnly);
+ private List<UserInfo> getProfilesLU(@UserIdInt int userId, @Nullable String userType,
+ boolean enabledOnly, boolean fullInfo) {
+ IntArray profileIds = getProfileIdsLU(userId, userType, enabledOnly);
ArrayList<UserInfo> users = new ArrayList<>(profileIds.size());
for (int i = 0; i < profileIds.size(); i++) {
int profileId = profileIds.get(i);
@@ -746,9 +768,12 @@ public class UserManagerService extends IUserManager.Stub {
/**
* Assume permissions already checked and caller's identity cleared
+ * <p>If userType is {@code null}, returns all profiles for user; else, only returns
+ * profiles of that type.
*/
@GuardedBy("mUsersLock")
- private IntArray getProfileIdsLU(@UserIdInt int userId, boolean enabledOnly) {
+ private IntArray getProfileIdsLU(@UserIdInt int userId, @Nullable String userType,
+ boolean enabledOnly) {
UserInfo user = getUserInfoLU(userId);
IntArray result = new IntArray(mUsers.size());
if (user == null) {
@@ -770,6 +795,9 @@ public class UserManagerService extends IUserManager.Stub {
if (profile.partial) {
continue;
}
+ if (userType != null && !userType.equals(profile.userType)) {
+ continue;
+ }
result.add(profile.id);
}
return result;
@@ -1116,6 +1144,45 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ /**
+ * Returns the user type, e.g. {@link UserManager#USER_TYPE_FULL_GUEST}, of the given userId,
+ * or null if the user doesn't exist.
+ */
+ @Override
+ public @Nullable String getUserTypeForUser(@UserIdInt int userId) {
+ // TODO(b/142482943): Decide on the appropriate permission requirements.
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserTypeForUser");
+ return getUserTypeNoChecks(userId);
+ }
+
+ /**
+ * Returns the user type of the given userId, or null if the user doesn't exist.
+ * <p>No permissions checks are made (but userId checks may be made).
+ */
+ private @Nullable String getUserTypeNoChecks(@UserIdInt int userId) {
+ synchronized (mUsersLock) {
+ final UserInfo userInfo = getUserInfoLU(userId);
+ return userInfo != null ? userInfo.userType : null;
+ }
+ }
+
+ /**
+ * Returns the UserTypeDetails of the given userId's user type, or null if the no such user.
+ * <p>No permissions checks are made (but userId checks may be made).
+ */
+ private @Nullable UserTypeDetails getUserTypeDetailsNoChecks(@UserIdInt int userId) {
+ final String typeStr = getUserTypeNoChecks(userId);
+ return typeStr != null ? mUserTypes.get(typeStr) : null;
+ }
+
+ /**
+ * Returns the UserTypeDetails of the given userInfo's user type (or null for a null userInfo).
+ */
+ private @Nullable UserTypeDetails getUserTypeDetails(@Nullable UserInfo userInfo) {
+ final String typeStr = userInfo != null ? userInfo.userType : null;
+ return typeStr != null ? mUserTypes.get(typeStr) : null;
+ }
+
@Override
public UserInfo getUserInfo(@UserIdInt int userId) {
checkManageOrCreateUsersPermission("query user");
@@ -1139,11 +1206,78 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public int getManagedProfileBadge(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getManagedProfileBadge");
+ public boolean hasBadge(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasBadge");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ return userTypeDetails != null && userTypeDetails.hasBadge();
+ }
+
+ @Override
+ public @StringRes int getUserBadgeLabelResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeLabelResId");
+ final UserInfo userInfo = getUserInfoNoChecks(userId);
+ final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+ if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge label for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ final int badgeIndex = userInfo.profileBadge;
+ return userTypeDetails.getBadgeLabel(badgeIndex);
+ }
+
+ @Override
+ public @ColorRes int getUserBadgeColorResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeColorResId");
+ final UserInfo userInfo = getUserInfoNoChecks(userId);
+ final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+ if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge color for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ final int badgeIndex = userInfo.profileBadge;
+ return userTypeDetails.getBadgeColor(badgeIndex);
+ }
+
+ @Override
+ public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested icon badge for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ return userTypeDetails.getIconBadge();
+ }
+
+ @Override
+ public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ return userTypeDetails.getBadgePlain();
+ }
+
+ @Override
+ public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId,
+ "getUserBadgeNoBackgroundResId");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge (no background) for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ return userTypeDetails.getBadgeNoBackground();
+ }
+
+ @Override
+ public boolean isProfile(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
- return userInfo != null ? userInfo.profileBadge : 0;
+ return userInfo != null && userInfo.isProfile();
}
}
@@ -1896,33 +2030,93 @@ public class UserManagerService extends IUserManager.Stub {
return count >= UserManager.getMaxSupportedUsers();
}
+ /**
+ * Returns whether more users of the given type can be added (based on how many users of that
+ * type already exist).
+ *
+ * <p>For checking whether more profiles can be added to a particular parent use
+ * {@link #canAddMoreProfilesToUser}.
+ */
+ private boolean canAddMoreUsersOfType(UserTypeDetails userTypeDetails) {
+ final int max = userTypeDetails.getMaxAllowed();
+ if (max == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+ return true; // Indicates that there is no max.
+ }
+ return getNumberOfUsersOfType(userTypeDetails.getName()) < max;
+ }
+
+ /**
+ * Gets the number of users of the given user type.
+ * Does not include users that are about to die.
+ */
+ private int getNumberOfUsersOfType(String userType) {
+ int count = 0;
+ synchronized (mUsersLock) {
+ final int size = mUsers.size();
+ for (int i = 0; i < size; i++) {
+ final UserInfo user = mUsers.valueAt(i).info;
+ if (user.userType.equals(userType)
+ && !user.guestToRemove
+ && !mRemovingUserIds.get(user.id)
+ && !user.preCreated) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
@Override
public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
- checkManageUsersPermission("check if more managed profiles can be added.");
- if (ActivityManager.isLowRamDeviceStatic()) {
- return false;
- }
- if (!mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_MANAGED_USERS)) {
+ return canAddMoreProfilesToUser(UserManager.USER_TYPE_PROFILE_MANAGED, userId,
+ allowedToRemoveOne);
+ }
+
+ /** Returns whether more profiles of the given type can be added to the given parent userId. */
+ @Override
+ public boolean canAddMoreProfilesToUser(String userType, @UserIdInt int userId,
+ boolean allowedToRemoveOne) {
+ checkManageUsersPermission("check if more profiles can be added.");
+ final UserTypeDetails type = mUserTypes.get(userType);
+ if (type == null) {
return false;
}
- // Limit number of managed profiles that can be created
- final int managedProfilesCount = getProfiles(userId, false).size() - 1;
- final int profilesRemovedCount = managedProfilesCount > 0 && allowedToRemoveOne ? 1 : 0;
- if (managedProfilesCount - profilesRemovedCount >= getMaxManagedProfiles()) {
- return false;
+ // Managed profiles have their own specific rules.
+ final boolean isManagedProfile = type.isManagedProfile();
+ if (isManagedProfile) {
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ return false;
+ }
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_MANAGED_USERS)) {
+ return false;
+ }
}
- synchronized(mUsersLock) {
+ synchronized (mUsersLock) {
+ // Check if the parent exists and its type is even allowed to have a profile.
UserInfo userInfo = getUserInfoLU(userId);
if (userInfo == null || !userInfo.canHaveProfile()) {
return false;
}
- int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
- - profilesRemovedCount;
- // We allow creating a managed profile in the special case where there is only one user.
- return usersCountAfterRemoving == 1
- || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+
+ // Limit the number of profiles that can be created
+ final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
+ if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+ final int userTypeCount = getProfileIds(userId, userType, false).length;
+ final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
+ if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+ return false;
+ }
+ // Allow creating a managed profile in the special case where there is only one user
+ if (isManagedProfile) {
+ int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+ - profilesRemovedCount;
+ return usersCountAfterRemoving == 1
+ || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+ }
+ }
}
+ return true;
}
@GuardedBy("mUsersLock")
@@ -2207,9 +2401,18 @@ public class UserManagerService extends IUserManager.Stub {
*/
@GuardedBy({"mRestrictionsLock", "mPackagesLock"})
private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
+ upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion);
+ }
+
+ /**
+ * Version of {@link #upgradeIfNecessaryLP(Bundle)} that takes in the userVersion for testing
+ * purposes. For non-tests, use {@link #upgradeIfNecessaryLP(Bundle)}.
+ */
+ @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
+ @VisibleForTesting
+ void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) {
Set<Integer> userIdsToWrite = new ArraySet<>();
final int originalVersion = mUserVersion;
- int userVersion = mUserVersion;
if (userVersion < 1) {
// Assign a proper name for the owner, if not initialized correctly before
UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
@@ -2298,6 +2501,44 @@ public class UserManagerService extends IUserManager.Stub {
userVersion = 8;
}
+ if (userVersion < 9) {
+ // Convert from UserInfo flags to UserTypes. Apply FLAG_PROFILE to FLAG_MANAGED_PROFILE.
+ synchronized (mUsersLock) {
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserData userData = mUsers.valueAt(i);
+ final int flags = userData.info.flags;
+ if ((flags & UserInfo.FLAG_SYSTEM) != 0) {
+ if ((flags & UserInfo.FLAG_FULL) != 0) {
+ userData.info.userType = UserManager.USER_TYPE_FULL_SYSTEM;
+ } else {
+ userData.info.userType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
+ }
+ } else {
+ try {
+ userData.info.userType = UserInfo.getDefaultUserType(flags);
+ } catch (IllegalArgumentException e) {
+ // TODO(b/142482943): What should we do here? Delete user? Crashloop?
+ throw new IllegalStateException("Cannot upgrade user with flags "
+ + Integer.toHexString(flags) + " because it doesn't correspond "
+ + "to a valid user type.", e);
+ }
+ }
+ // OEMs are responsible for their own custom upgrade logic here.
+
+ final UserTypeDetails userTypeDetails = mUserTypes.get(userData.info.userType);
+ if (userTypeDetails == null) {
+ throw new IllegalStateException(
+ "Cannot upgrade user with flags " + Integer.toHexString(flags)
+ + " because " + userData.info.userType + " isn't defined"
+ + " on this device!");
+ }
+ userData.info.flags |= userTypeDetails.getDefaultUserInfoFlags();
+ userIdsToWrite.add(userData.info.id);
+ }
+ }
+ userVersion = 9;
+ }
+
if (userVersion < USER_VERSION) {
Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
+ USER_VERSION);
@@ -2320,12 +2561,11 @@ public class UserManagerService extends IUserManager.Stub {
private void fallbackToSingleUserLP() {
int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN
| UserInfo.FLAG_PRIMARY;
- // In headless system user mode, headless system user is not a full user.
- if (!UserManager.isHeadlessSystemUserMode()) {
- flags |= UserInfo.FLAG_FULL;
- }
// Create the system user
- UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags);
+ String systemUserType = UserManager.isHeadlessSystemUserMode() ?
+ UserManager.USER_TYPE_SYSTEM_HEADLESS : UserManager.USER_TYPE_FULL_SYSTEM;
+ flags |= mUserTypes.get(systemUserType).getDefaultUserInfoFlags();
+ UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags, systemUserType);
UserData userData = putUserInfo(system);
mNextSerialNumber = MIN_USER_ID;
mUserVersion = USER_VERSION;
@@ -2410,6 +2650,7 @@ public class UserManagerService extends IUserManager.Stub {
serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
+ serializer.attribute(null, ATTR_TYPE, userInfo.userType);
serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
Long.toString(userInfo.lastLoggedInTime));
@@ -2570,6 +2811,7 @@ public class UserManagerService extends IUserManager.Stub {
UserData readUserLP(int id, InputStream is) throws IOException,
XmlPullParserException {
int flags = 0;
+ String userType = null;
int serialNumber = id;
String name = null;
String account = null;
@@ -2613,6 +2855,8 @@ public class UserManagerService extends IUserManager.Stub {
}
serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id);
flags = readIntAttribute(parser, ATTR_FLAGS, 0);
+ userType = parser.getAttributeValue(null, ATTR_TYPE);
+ userType = userType != null ? userType.intern() : null;
iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
@@ -2678,7 +2922,7 @@ public class UserManagerService extends IUserManager.Stub {
}
// Create the UserInfo object that gets passed around
- UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
+ UserInfo userInfo = new UserInfo(id, name, iconPath, flags, userType);
userInfo.serialNumber = serialNumber;
userInfo.creationTime = creationTime;
userInfo.lastLoggedInTime = lastLoggedInTime;
@@ -2745,108 +2989,135 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ /**
+ * Creates a profile user. Used for actual profiles, like
+ * {@link UserManager#USER_TYPE_PROFILE_MANAGED}, as well as for
+ * {@link UserManager#USER_TYPE_FULL_RESTRICTED}.
+ */
@Override
- public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId,
- String[] disallowedPackages) {
+ public UserInfo createProfileForUser(String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
checkManageOrCreateUsersPermission(flags);
- return createUserInternal(name, flags, userId, disallowedPackages);
+ return createUserInternal(name, userType, flags, userId, disallowedPackages);
}
+ /** @see #createProfileForUser */
@Override
- public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags,
- @UserIdInt int userId, String[] disallowedPackages) {
+ public UserInfo createProfileForUserEvenWhenDisallowed(String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
checkManageOrCreateUsersPermission(flags);
- return createUserInternalUnchecked(name, flags, userId, /* preCreate= */ false,
- disallowedPackages);
- }
-
- @Override
- public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
- checkManageOrCreateUsersPermission("Only the system can remove users");
- return removeUserUnchecked(userId);
+ return createUserInternalUnchecked(name, userType, flags, userId,
+ /* preCreate= */ false, disallowedPackages);
}
@Override
- public UserInfo createUser(String name, int flags) {
+ public UserInfo createUser(String name, @NonNull String userType, @UserInfoFlag int flags) {
checkManageOrCreateUsersPermission(flags);
- return createUserInternal(name, flags, UserHandle.USER_NULL);
+ return createUserInternal(name, userType, flags, UserHandle.USER_NULL,
+ /* disallowedPackages= */ null);
}
@Override
- public UserInfo preCreateUser(int flags) {
- checkManageOrCreateUsersPermission(flags);
+ public UserInfo preCreateUser(String userType) {
+ final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
+ final int flags = userTypeDetails != null ? userTypeDetails.getDefaultUserInfoFlags() : 0;
- Preconditions.checkArgument(!UserInfo.isManagedProfile(flags),
- "cannot pre-create managed profiles");
+ checkManageOrCreateUsersPermission(flags);
- Slog.i(LOG_TAG, "Pre-creating user with flags " + UserInfo.flagsToString(flags));
+ Preconditions.checkArgument(isUserTypeEligibleForPreCreation(userTypeDetails),
+ "cannot pre-create user of type " + userType);
+ Slog.i(LOG_TAG, "Pre-creating user of type " + userType);
- return createUserInternalUnchecked(/* name= */ null, flags,
+ return createUserInternalUnchecked(/* name= */ null, userType, flags,
/* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
/* disallowedPackages= */ null);
}
- private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
- @UserIdInt int parentId) {
- return createUserInternal(name, flags, parentId, null);
- }
-
- private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
- @UserIdInt int parentId, @Nullable String[] disallowedPackages) {
- String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
+ private UserInfo createUserInternal(@Nullable String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int parentId,
+ @Nullable String[] disallowedPackages) {
+ String restriction = (UserManager.isUserTypeManagedProfile(userType))
? UserManager.DISALLOW_ADD_MANAGED_PROFILE
: UserManager.DISALLOW_ADD_USER;
if (hasUserRestriction(restriction, UserHandle.getCallingUserId())) {
Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
return null;
}
- return createUserInternalUnchecked(name, flags, parentId, /* preCreate= */ false,
- disallowedPackages);
+ return createUserInternalUnchecked(name, userType, flags, parentId,
+ /* preCreate= */ false, disallowedPackages);
}
- private UserInfo createUserInternalUnchecked(@Nullable String name, @UserInfoFlag int flags,
- @UserIdInt int parentId, boolean preCreate,
- @Nullable String[] disallowedPackages) {
+ private UserInfo createUserInternalUnchecked(@Nullable String name,
+ @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
+ boolean preCreate, @Nullable String[] disallowedPackages) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("createUser-" + flags);
try {
- return createUserInternalUncheckedNoTracing(name, flags, parentId, preCreate,
- disallowedPackages, t);
+ return createUserInternalUncheckedNoTracing(name, userType, flags, parentId,
+ preCreate, disallowedPackages, t);
} finally {
t.traceEnd();
}
}
private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
- @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate,
- @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t) {
+ @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
+ boolean preCreate, @Nullable String[] disallowedPackages,
+ @NonNull TimingsTraceAndSlog t) {
+
+ final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
+ if (userTypeDetails == null) {
+ Slog.e(LOG_TAG, "Cannot create user of invalid user type: " + userType);
+ return null;
+ }
+ userType = userType.intern(); // Now that we know it's valid, we can intern it.
+ flags |= userTypeDetails.getDefaultUserInfoFlags();
+ if (!checkUserTypeConsistency(flags)) {
+ Slog.e(LOG_TAG, "Cannot add user. Flags (" + Integer.toHexString(flags)
+ + ") and userTypeDetails (" + userType + ") are inconsistent.");
+ return null;
+ }
+ if ((flags & UserInfo.FLAG_SYSTEM) != 0) {
+ Slog.e(LOG_TAG, "Cannot add user. Flags (" + Integer.toHexString(flags)
+ + ") indicated SYSTEM user, which cannot be created.");
+ return null;
+ }
+ synchronized (mUsersLock) {
+ if (mForceEphemeralUsers) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ }
+ }
// First try to use a pre-created user (if available).
- // NOTE: currently we don't support pre-created managed profiles
- if (!preCreate && (parentId < 0 && !UserInfo.isManagedProfile(flags))) {
+ // TODO(b/142482943): Move this to its own function later.
+ if (!preCreate
+ && (parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails))) {
final UserData preCreatedUserData;
synchronized (mUsersLock) {
- preCreatedUserData = getPreCreatedUserLU(flags);
+ preCreatedUserData = getPreCreatedUserLU(userType);
}
if (preCreatedUserData != null) {
final UserInfo preCreatedUser = preCreatedUserData.info;
- Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " for flags + "
- + UserInfo.flagsToString(flags));
- if (DBG) {
- Log.d(LOG_TAG, "pre-created user flags: "
- + UserInfo.flagsToString(preCreatedUser.flags)
- + " new-user flags: " + UserInfo.flagsToString(flags));
+ final int newFlags = preCreatedUser.flags | flags;
+ if (!checkUserTypeConsistency(newFlags)) {
+ Slog.wtf(LOG_TAG, "Cannot reuse pre-created user " + preCreatedUser.id
+ + " of type " + userType + " because flags are inconsistent. "
+ + "Flags (" + Integer.toHexString(flags) + "); preCreatedUserFlags ( "
+ + Integer.toHexString(preCreatedUser.flags) + ").");
+ } else {
+ Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " of type "
+ + userType + " and bestowing on it flags "
+ + UserInfo.flagsToString(flags));
+ preCreatedUser.name = name;
+ preCreatedUser.flags = newFlags;
+ preCreatedUser.preCreated = false;
+ preCreatedUser.creationTime = getCreationTime();
+
+ dispatchUserAddedIntent(preCreatedUser);
+ writeUserLP(preCreatedUserData);
+ writeUserListLP();
+ return preCreatedUser;
}
- preCreatedUser.name = name;
- preCreatedUser.preCreated = false;
- preCreatedUser.creationTime = getCreationTime();
-
- dispatchUserAddedIntent(preCreatedUser);
-
- writeUserLP(preCreatedUserData);
- writeUserListLP();
-
- return preCreatedUser;
}
}
@@ -2857,10 +3128,11 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
- final boolean isGuest = UserInfo.isGuest(flags);
- final boolean isManagedProfile = UserInfo.isManagedProfile(flags);
- final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
- final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
+ final boolean isProfile = userTypeDetails.isProfile();
+ final boolean isGuest = UserManager.isUserTypeGuest(userType);
+ final boolean isRestricted = UserManager.isUserTypeRestricted(userType);
+ final boolean isDemo = UserManager.isUserTypeDemo(userType);
+
final long ident = Binder.clearCallingIdentity();
UserInfo userInfo;
UserData userData;
@@ -2874,19 +3146,21 @@ public class UserManagerService extends IUserManager.Stub {
}
if (parent == null) return null;
}
- if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) {
- Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId);
+ if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
+ Log.e(LOG_TAG, "Cannot add more users of type " + userType
+ + ". Maximum number of that type already exists.");
return null;
}
- if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
- // If we're not adding a guest/demo user or a managed profile,
- // and the limit has been reached, cannot add a user.
- Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
+ // TODO(b/142482943): Perhaps let the following code apply to restricted users too.
+ if (isProfile && !canAddMoreProfilesToUser(userType, parentId, false)) {
+ Log.e(LOG_TAG, "Cannot add more profiles of type " + userType
+ + " for user " + parentId);
return null;
}
- // If we're adding a guest and there already exists one, bail.
- if (isGuest && !preCreate && findCurrentGuestUser() != null) {
- Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists.");
+ if (!isGuest && !isProfile && !isDemo && isUserLimitReached()) {
+ // If we're not adding a guest/demo user or a profile and the 'user limit' has
+ // been reached, cannot add a user.
+ Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
return null;
}
// In legacy mode, restricted profile's parent can only be the owner user
@@ -2908,30 +3182,23 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- if (!isManagedProfile) {
- // New users cannot be system, and it's not a profile, so per-force it's FULL.
- flags |= UserInfo.FLAG_FULL;
- }
-
userId = getNextAvailableId();
Environment.getUserSystemDirectory(userId).mkdirs();
- boolean ephemeralGuests = areGuestUsersEphemeral();
synchronized (mUsersLock) {
- // Add ephemeral flag to guests/users if required. Also inherit it from parent.
- if ((isGuest && ephemeralGuests) || mForceEphemeralUsers
- || (parent != null && parent.info.isEphemeral())) {
+ // Inherit ephemeral flag from parent.
+ if (parent != null && parent.info.isEphemeral()) {
flags |= UserInfo.FLAG_EPHEMERAL;
}
- userInfo = new UserInfo(userId, name, null, flags);
+ userInfo = new UserInfo(userId, name, null, flags, userType);
userInfo.serialNumber = mNextSerialNumber++;
userInfo.creationTime = getCreationTime();
userInfo.partial = true;
userInfo.preCreated = preCreate;
userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
- if (isManagedProfile && parentId != UserHandle.USER_NULL) {
- userInfo.profileBadge = getFreeProfileBadgeLU(parentId);
+ if (userTypeDetails.hasBadge() && parentId != UserHandle.USER_NULL) {
+ userInfo.profileBadge = getFreeProfileBadgeLU(parentId, userType);
}
userData = new UserData();
userData.info = userInfo;
@@ -2940,7 +3207,7 @@ public class UserManagerService extends IUserManager.Stub {
writeUserLP(userData);
writeUserListLP();
if (parent != null) {
- if (isManagedProfile) {
+ if (isProfile) {
if (parent.info.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
parent.info.profileGroupId = parent.info.id;
writeUserLP(parent);
@@ -2966,7 +3233,7 @@ public class UserManagerService extends IUserManager.Stub {
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
t.traceEnd();
- final Set<String> installablePackages =
+ final Set<String> installablePackages = // TODO(b/142482943): use userType
mSystemPackageInstaller.getInstallablePackagesForUserType(flags);
t.traceBegin("PM.createNewUser");
mPm.createNewUser(userId, installablePackages, disallowedPackages);
@@ -2978,6 +3245,7 @@ public class UserManagerService extends IUserManager.Stub {
}
updateUserIds();
Bundle restrictions = new Bundle();
+ // TODO(b/142482943): Generalize this using UserTypeDetails default restrictions.
if (isGuest) {
synchronized (mGuestRestrictions) {
restrictions.putAll(mGuestRestrictions);
@@ -3026,6 +3294,22 @@ public class UserManagerService extends IUserManager.Stub {
return userInfo;
}
+ /** Checks that the flags do not contain mutually exclusive types/properties. */
+ static boolean checkUserTypeConsistency(@UserInfoFlag int flags) {
+ // Mask to check that flags don't refer to multiple user types.
+ final int userTypeFlagMask = UserInfo.FLAG_GUEST | UserInfo.FLAG_DEMO
+ | UserInfo.FLAG_RESTRICTED | UserInfo.FLAG_PROFILE;
+ return isAtMostOneFlag(flags & userTypeFlagMask)
+ && isAtMostOneFlag(flags & (UserInfo.FLAG_PROFILE | UserInfo.FLAG_FULL))
+ && isAtMostOneFlag(flags & (UserInfo.FLAG_PROFILE | UserInfo.FLAG_SYSTEM));
+ }
+
+ /** Returns whether the given flags contains at most one 1. */
+ private static boolean isAtMostOneFlag(int flags) {
+ return (flags & (flags - 1)) == 0;
+ // If !=0, this means that flags is not a power of 2, and therefore is multiple types.
+ }
+
/** Install/uninstall system packages for all users based on their user-type, as applicable. */
boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
return mSystemPackageInstaller.installWhitelistedSystemPackages(isFirstBoot, isUpgrade);
@@ -3045,39 +3329,23 @@ public class UserManagerService extends IUserManager.Stub {
: (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
}
- private boolean areGuestUsersEphemeral() {
- return Resources.getSystem()
- .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
- }
-
/**
- * Gets a pre-created user for the given flag.
+ * Gets a pre-created user for the given user type.
*
* <p>Should be used only during user creation, so the pre-created user can be used (instead of
* creating and initializing a new user from scratch).
*/
// TODO(b/143092698): add unit test
@GuardedBy("mUsersLock")
- private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
- if (DBG) {
- Slog.d(LOG_TAG, "getPreCreatedUser(): initialFlags= " + UserInfo.flagsToString(flags));
- }
- flags |= UserInfo.FLAG_FULL;
- if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) {
- flags |= UserInfo.FLAG_EPHEMERAL;
- }
- if (DBG) {
- Slog.d(LOG_TAG, "getPreCreatedUser(): targetFlags= " + UserInfo.flagsToString(flags));
- }
+ private @Nullable UserData getPreCreatedUserLU(String userType) {
+ if (DBG) Slog.d(LOG_TAG, "getPreCreatedUser(): userType= " + userType);
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
final UserData user = mUsers.valueAt(i);
if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
- if (user.info.preCreated
- && (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) {
+ if (user.info.preCreated && user.info.userType.equals(userType)) {
if (!user.info.isInitialized()) {
- Slog.w(LOG_TAG, "found pre-created user for flags "
- + "" + UserInfo.flagsToString(flags)
+ Slog.w(LOG_TAG, "found pre-created user of type " + userType
+ ", but it's not initialized yet: " + user.info.toFullString());
continue;
}
@@ -3087,6 +3355,18 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
+ /**
+ * Returns whether a user with the given userTypeDetails is eligible to be
+ * {@link UserInfo#preCreated}.
+ */
+ private static boolean isUserTypeEligibleForPreCreation(UserTypeDetails userTypeDetails) {
+ if (userTypeDetails == null) {
+ return false;
+ }
+ return !userTypeDetails.isProfile()
+ && !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED);
+ }
+
@VisibleForTesting
UserData putUserInfo(UserInfo userInfo) {
final UserData userData = new UserData();
@@ -3111,7 +3391,7 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo createRestrictedProfile(String name, int parentUserId) {
checkManageOrCreateUsersPermission("setupRestrictedProfile");
final UserInfo user = createProfileForUser(
- name, UserInfo.FLAG_RESTRICTED, parentUserId, null);
+ name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
if (user == null) {
return null;
}
@@ -3217,6 +3497,12 @@ public class UserManagerService extends IUserManager.Stub {
return removeUserUnchecked(userId);
}
+ @Override
+ public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
+ checkManageOrCreateUsersPermission("Only the system can remove users");
+ return removeUserUnchecked(userId);
+ }
+
private boolean removeUserUnchecked(@UserIdInt int userId) {
long ident = Binder.clearCallingIdentity();
try {
@@ -3264,6 +3550,7 @@ public class UserManagerService extends IUserManager.Stub {
Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e);
}
+ // TODO(b/142482943): Send some sort of broadcast for profiles even if non-managed?
if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& userData.info.isManagedProfile()) {
// Send broadcast to notify system that the user removed was a
@@ -4035,6 +4322,7 @@ public class UserManagerService extends IUserManager.Stub {
pw.print(" <pre-created>");
}
pw.println();
+ pw.print(" Type: "); pw.println(userInfo.userType);
pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" (");
pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")");
pw.print(" State: ");
@@ -4119,11 +4407,20 @@ public class UserManagerService extends IUserManager.Stub {
pw.print(" Max users: " + UserManager.getMaxSupportedUsers());
pw.println(" (limit reached: " + isUserLimitReached() + ")");
pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
- pw.println(" All guests ephemeral: " + areGuestUsersEphemeral());
+ pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.config_guestUserEphemeral));
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
pw.println(" User version: " + mUserVersion);
+ // Dump UserTypes
+ pw.println();
+ pw.println(" User types (" + mUserTypes.size() + " types):");
+ for (int i = 0; i < mUserTypes.size(); i++) {
+ pw.println(" " + mUserTypes.keyAt(i) + ": ");
+ mUserTypes.valueAt(i).dump(pw);
+ }
+
// Dump package whitelist
pw.println();
mSystemPackageInstaller.dump(pw);
@@ -4322,10 +4619,10 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public UserInfo createUserEvenWhenDisallowed(String name, int flags,
- String[] disallowedPackages) {
- UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
- /* preCreated= */ false, disallowedPackages);
+ public UserInfo createUserEvenWhenDisallowed(String name, @NonNull String userType,
+ @UserInfoFlag int flags, String[] disallowedPackages) {
+ UserInfo user = createUserInternalUnchecked(name, userType, flags,
+ UserHandle.USER_NULL, /* preCreated= */ false, disallowedPackages);
// Keep this in sync with UserManager.createUser
if (user != null && !user.isAdmin() && !user.isDemo()) {
setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
@@ -4410,7 +4707,7 @@ public class UserManagerService extends IUserManager.Stub {
}
synchronized (mUsersLock) {
UserInfo callingUserInfo = getUserInfoLU(callingUserId);
- if (callingUserInfo == null || callingUserInfo.isManagedProfile()) {
+ if (callingUserInfo == null || callingUserInfo.isProfile()) {
if (throwSecurityException) {
throw new SecurityException(
debugMsg + " for another profile "
@@ -4529,36 +4826,53 @@ public class UserManagerService extends IUserManager.Stub {
(DBG_WITH_STACKTRACE ? " called at\n" + Debug.getCallers(10, " ") : ""));
}
+ /** @see #getMaxUsersOfTypePerParent(UserTypeDetails) */
@VisibleForTesting
- static int getMaxManagedProfiles() {
- // Allow overriding max managed profiles on debuggable builds for testing
- // of multiple profiles.
+ int getMaxUsersOfTypePerParent(String userType) {
+ final UserTypeDetails type = mUserTypes.get(userType);
+ if (type == null) {
+ return 0;
+ }
+ return getMaxUsersOfTypePerParent(type);
+ }
+
+ /**
+ * Returns the maximum number of users allowed for the given userTypeDetails per parent user.
+ * This is applicable for user types that are {@link UserTypeDetails#isProfile()}.
+ * If there is no maximum, {@link UserTypeDetails#UNLIMITED_NUMBER_OF_USERS} is returned.
+ */
+ private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) {
+ final int defaultMax = userTypeDetails.getMaxAllowedPerParent();
if (!Build.IS_DEBUGGABLE) {
- return MAX_MANAGED_PROFILES;
+ return defaultMax;
} else {
- return SystemProperties.getInt("persist.sys.max_profiles",
- MAX_MANAGED_PROFILES);
+ if (userTypeDetails.isManagedProfile()) {
+ return SystemProperties.getInt("persist.sys.max_profiles", defaultMax);
+ }
}
+ return defaultMax;
}
@GuardedBy("mUsersLock")
@VisibleForTesting
- int getFreeProfileBadgeLU(int parentUserId) {
- int maxManagedProfiles = getMaxManagedProfiles();
- boolean[] usedBadges = new boolean[maxManagedProfiles];
+ int getFreeProfileBadgeLU(int parentUserId, String userType) {
+ Set<Integer> usedBadges = new ArraySet<>();
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
// Check which badge indexes are already used by this profile group.
- if (ui.isManagedProfile()
+ if (ui.userType.equals(userType)
&& ui.profileGroupId == parentUserId
- && !mRemovingUserIds.get(ui.id)
- && ui.profileBadge < maxManagedProfiles) {
- usedBadges[ui.profileBadge] = true;
+ && !mRemovingUserIds.get(ui.id)) {
+ usedBadges.add(ui.profileBadge);
}
}
- for (int i = 0; i < maxManagedProfiles; i++) {
- if (!usedBadges[i]) {
+ int maxUsersOfType = getMaxUsersOfTypePerParent(userType);
+ if (maxUsersOfType == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+ maxUsersOfType = Integer.MAX_VALUE;
+ }
+ for (int i = 0; i < maxUsersOfType; i++) {
+ if (!usedBadges.contains(i)) {
return i;
}
}
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index ef6b24cd198c..95197b07deaf 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -389,7 +389,7 @@ class UserSystemPackageInstaller {
final ArrayMap<String, Integer> result = new ArrayMap<>(whitelist.size() + 1);
// First, do the whitelisted user types.
for (int i = 0; i < whitelist.size(); i++) {
- final String pkgName = whitelist.keyAt(i);
+ final String pkgName = whitelist.keyAt(i).intern();
final int flags = getFlagsFromUserTypes(whitelist.valueAt(i));
if (flags != 0) {
result.put(pkgName, flags);
@@ -402,7 +402,7 @@ class UserSystemPackageInstaller {
final ArrayMap<String, Set<String>> blacklist =
sysConfig.getAndClearPackageToUserTypeBlacklist();
for (int i = 0; i < blacklist.size(); i++) {
- final String pkgName = blacklist.keyAt(i);
+ final String pkgName = blacklist.keyAt(i).intern();
final int nonFlags = getFlagsFromUserTypes(blacklist.valueAt(i));
final Integer flags = result.get(pkgName);
if (flags != null) {
@@ -411,12 +411,13 @@ class UserSystemPackageInstaller {
}
// Regardless of the whitelists/blacklists, ensure mandatory packages.
result.put("android",
- UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
+ UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.FLAG_PROFILE);
return result;
}
/** Converts a user types, as used in SystemConfig, to a UserInfo flag. */
private static int getFlagsFromUserTypes(Iterable<String> userTypes) {
+ // TODO(b/142482943): Update all this for the new UserTypes.
int flags = 0;
for (String type : userTypes) {
switch (type) {
@@ -442,7 +443,7 @@ class UserSystemPackageInstaller {
flags |= UserInfo.FLAG_SYSTEM;
break;
case "PROFILE":
- flags |= UserInfo.PROFILE_FLAGS_MASK;
+ flags |= UserInfo.FLAG_PROFILE;
break;
default:
Slog.w(TAG, "SystemConfig contained an invalid user type: " + type);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index 505a0e22eac4..b68d5418d0eb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -23,6 +23,7 @@ import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
@@ -30,7 +31,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import com.android.internal.annotations.GuardedBy;
/**
* This class encapsulates the permissions for a package or a shared user.
@@ -708,7 +708,11 @@ public final class PermissionsState {
}
private static final class PermissionData {
+
+ private final Object mLock = new Object();
+
private final BasePermission mPerm;
+ @GuardedBy("mLock")
private SparseArray<PermissionState> mUserStates = new SparseArray<>();
public PermissionData(BasePermission perm) {
@@ -717,11 +721,14 @@ public final class PermissionsState {
public PermissionData(PermissionData other) {
this(other.mPerm);
- final int otherStateCount = other.mUserStates.size();
- for (int i = 0; i < otherStateCount; i++) {
- final int otherUserId = other.mUserStates.keyAt(i);
- PermissionState otherState = other.mUserStates.valueAt(i);
- mUserStates.put(otherUserId, new PermissionState(otherState));
+
+ synchronized (mLock) {
+ final int otherStateCount = other.mUserStates.size();
+ for (int i = 0; i < otherStateCount; i++) {
+ final int otherUserId = other.mUserStates.keyAt(i);
+ PermissionState otherState = other.mUserStates.valueAt(i);
+ mUserStates.put(otherUserId, new PermissionState(otherState));
+ }
}
}
@@ -730,71 +737,83 @@ public final class PermissionsState {
}
public boolean isGranted(int userId) {
- if (isInstallPermission()) {
- userId = UserHandle.USER_ALL;
- }
+ synchronized (mLock) {
+ if (isInstallPermission()) {
+ userId = UserHandle.USER_ALL;
+ }
- PermissionState userState = mUserStates.get(userId);
- if (userState == null) {
- return false;
- }
+ PermissionState userState = mUserStates.get(userId);
+ if (userState == null) {
+ return false;
+ }
- return userState.mGranted;
+ return userState.mGranted;
+ }
}
public boolean grant(int userId) {
- if (!isCompatibleUserId(userId)) {
- return false;
- }
+ synchronized (mLock) {
+ if (!isCompatibleUserId(userId)) {
+ return false;
+ }
- if (isGranted(userId)) {
- return false;
- }
+ if (isGranted(userId)) {
+ return false;
+ }
- PermissionState userState = mUserStates.get(userId);
- if (userState == null) {
- userState = new PermissionState(mPerm.getName());
- mUserStates.put(userId, userState);
- }
+ PermissionState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new PermissionState(mPerm.getName());
+ mUserStates.put(userId, userState);
+ }
- userState.mGranted = true;
+ userState.mGranted = true;
- return true;
+ return true;
+ }
}
public boolean revoke(int userId) {
- if (!isCompatibleUserId(userId)) {
- return false;
- }
+ synchronized (mLock) {
+ if (!isCompatibleUserId(userId)) {
+ return false;
+ }
- if (!isGranted(userId)) {
- return false;
- }
+ if (!isGranted(userId)) {
+ return false;
+ }
- PermissionState userState = mUserStates.get(userId);
- userState.mGranted = false;
+ PermissionState userState = mUserStates.get(userId);
+ userState.mGranted = false;
- if (userState.isDefault()) {
- mUserStates.remove(userId);
- }
+ if (userState.isDefault()) {
+ mUserStates.remove(userId);
+ }
- return true;
+ return true;
+ }
}
public PermissionState getPermissionState(int userId) {
- return mUserStates.get(userId);
+ synchronized (mLock) {
+ return mUserStates.get(userId);
+ }
}
public int getFlags(int userId) {
- PermissionState userState = mUserStates.get(userId);
- if (userState != null) {
- return userState.mFlags;
+ synchronized (mLock) {
+ PermissionState userState = mUserStates.get(userId);
+ if (userState != null) {
+ return userState.mFlags;
+ }
+ return 0;
}
- return 0;
}
public boolean isDefault() {
- return mUserStates.size() <= 0;
+ synchronized (mLock) {
+ return mUserStates.size() <= 0;
+ }
}
public static boolean isInstallPermissionKey(int userId) {
@@ -802,32 +821,34 @@ public final class PermissionsState {
}
public boolean updateFlags(int userId, int flagMask, int flagValues) {
- if (isInstallPermission()) {
- userId = UserHandle.USER_ALL;
- }
+ synchronized (mLock) {
+ if (isInstallPermission()) {
+ userId = UserHandle.USER_ALL;
+ }
- if (!isCompatibleUserId(userId)) {
- return false;
- }
+ if (!isCompatibleUserId(userId)) {
+ return false;
+ }
- final int newFlags = flagValues & flagMask;
+ final int newFlags = flagValues & flagMask;
- PermissionState userState = mUserStates.get(userId);
- if (userState != null) {
- final int oldFlags = userState.mFlags;
- userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
- if (userState.isDefault()) {
- mUserStates.remove(userId);
+ PermissionState userState = mUserStates.get(userId);
+ if (userState != null) {
+ final int oldFlags = userState.mFlags;
+ userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
+ if (userState.isDefault()) {
+ mUserStates.remove(userId);
+ }
+ return userState.mFlags != oldFlags;
+ } else if (newFlags != 0) {
+ userState = new PermissionState(mPerm.getName());
+ userState.mFlags = newFlags;
+ mUserStates.put(userId, userState);
+ return true;
}
- return userState.mFlags != oldFlags;
- } else if (newFlags != 0) {
- userState = new PermissionState(mPerm.getName());
- userState.mFlags = newFlags;
- mUserStates.put(userId, userState);
- return true;
- }
- return false;
+ return false;
+ }
}
private boolean isCompatibleUserId(int userId) {
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 3aafd5e35dc4..c779ebf4a995 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -403,7 +403,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn
public String getStatus() {
return mContext.getString(
com.android.internal.R.string.bugreport_status,
- Build.VERSION.RELEASE_OR_CODENAME,
+ Build.VERSION.RELEASE,
Build.ID);
}
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 9b9f93f7b5c4..3c4e3f64a52e 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -29,8 +29,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -169,6 +171,23 @@ public final class PermissionPolicyService extends SystemService {
} catch (RemoteException doesNotHappen) {
Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
}
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addDataScheme("package");
+
+ getContext().registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UserHandle user =
+ UserHandle.getUserHandleForUid(intent.getIntExtra(Intent.EXTRA_UID, -1));
+ new PermissionControllerManager(
+ getUserContext(getContext(), user), FgThread.getHandler())
+ .updateUserSensitive();
+ }
+ }, UserHandle.ALL, intentFilter, null, null);
+
}
/**
diff --git a/services/core/java/com/android/server/power/InattentiveSleepWarningController.java b/services/core/java/com/android/server/power/InattentiveSleepWarningController.java
new file mode 100644
index 000000000000..db8a63f44cbc
--- /dev/null
+++ b/services/core/java/com/android/server/power/InattentiveSleepWarningController.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
+
+/**
+ * Communicates with System UI to show/hide the inattentive sleep warning overlay.
+ */
+@VisibleForTesting
+public class InattentiveSleepWarningController {
+ private static final String TAG = "InattentiveSleepWarning";
+
+ private final Handler mHandler = new Handler();
+
+ private boolean mIsShown;
+ private IStatusBarService mStatusBarService;
+
+ InattentiveSleepWarningController() {
+ }
+
+ /**
+ * Returns true if the warning is currently being displayed, false otherwise.
+ */
+ @GuardedBy("PowerManagerService.mLock")
+ public boolean isShown() {
+ return mIsShown;
+ }
+
+ /**
+ * Show the warning.
+ */
+ @GuardedBy("PowerManagerService.mLock")
+ public void show() {
+ if (isShown()) {
+ return;
+ }
+
+ mHandler.post(this::showInternal);
+ mIsShown = true;
+ }
+
+ private void showInternal() {
+ try {
+ getStatusBar().showInattentiveSleepWarning();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to show inattentive sleep warning", e);
+ mIsShown = false;
+ }
+ }
+
+ /**
+ * Dismiss the warning.
+ */
+ @GuardedBy("PowerManagerService.mLock")
+ public void dismiss() {
+ if (!isShown()) {
+ return;
+ }
+
+ mHandler.post(this::dismissInternal);
+ mIsShown = false;
+ }
+
+ private void dismissInternal() {
+ try {
+ getStatusBar().dismissInattentiveSleepWarning();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to dismiss inattentive sleep warning", e);
+ }
+ }
+
+ private IStatusBarService getStatusBar() {
+ if (mStatusBarService == null) {
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ return mStatusBarService;
+ }
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index b67d9b285acd..7fc9fdc0180d 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -536,6 +536,7 @@ public class Notifier {
case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
return WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+ case PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE:
return WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
default:
return WindowManagerPolicy.OFF_BECAUSE_OF_USER;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index befe4e968b12..00e0f7124714 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -128,6 +128,8 @@ public final class PowerManagerService extends SystemService
private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3;
// Message: Polling to look for long held wake locks.
private static final int MSG_CHECK_FOR_LONG_WAKELOCKS = 4;
+ // Message: Sent when an attentive timeout occurs to update the power state.
+ private static final int MSG_ATTENTIVE_TIMEOUT = 5;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -157,6 +159,8 @@ public final class PowerManagerService extends SystemService
private static final int DIRTY_QUIESCENT = 1 << 12;
// Dirty bit: VR Mode enabled changed
private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
+ // Dirty bit: attentive timer may have timed out
+ private static final int DIRTY_ATTENTIVE = 1 << 14;
// Summarizes the state of all active wakelocks.
private static final int WAKE_LOCK_CPU = 1 << 0;
@@ -249,6 +253,8 @@ public final class PowerManagerService extends SystemService
private DreamManagerInternal mDreamManager;
private Light mAttentionLight;
+ private InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
+
private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER);
// A bitfield that indicates what parts of the power state have
@@ -381,6 +387,9 @@ public final class PowerManagerService extends SystemService
// True if the device should suspend when the screen is off due to proximity.
private boolean mSuspendWhenScreenOffDueToProximityConfig;
+ // Default value for attentive timeout.
+ private int mAttentiveTimeoutConfig;
+
// True if dreams are supported on this device.
private boolean mDreamsSupportedConfig;
@@ -441,9 +450,16 @@ public final class PowerManagerService extends SystemService
// The screen off timeout setting value in milliseconds.
private long mScreenOffTimeoutSetting;
+ // Default for attentive warning duration.
+ private long mAttentiveWarningDurationConfig;
+
// The sleep timeout setting value in milliseconds.
private long mSleepTimeoutSetting;
+ // How long to show a warning message to user before the device goes to sleep
+ // after long user inactivity, even if wakelocks are held.
+ private long mAttentiveTimeoutSetting;
+
// The maximum allowable screen off timeout according to the device
// administration policy. Overrides other settings.
private long mMaximumScreenOffTimeoutFromDeviceAdmin = Long.MAX_VALUE;
@@ -735,6 +751,10 @@ public final class PowerManagerService extends SystemService
AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
return new AmbientDisplayConfiguration(context);
}
+
+ InattentiveSleepWarningController createInattentiveSleepWarningController() {
+ return new InattentiveSleepWarningController();
+ }
}
final Constants mConstants;
@@ -779,6 +799,9 @@ public final class PowerManagerService extends SystemService
mBatterySaverStateMachine = new BatterySaverStateMachine(
mLock, mContext, mBatterySaverController);
+ mInattentiveSleepWarningOverlayController =
+ mInjector.createInattentiveSleepWarningController();
+
synchronized (mLock) {
mWakeLockSuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
@@ -902,6 +925,9 @@ public final class PowerManagerService extends SystemService
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SLEEP_TIMEOUT),
false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.ATTENTIVE_TIMEOUT),
+ false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
false, mSettingsObserver, UserHandle.USER_ALL);
@@ -966,6 +992,10 @@ public final class PowerManagerService extends SystemService
com.android.internal.R.bool.config_allowTheaterModeWakeFromUnplug);
mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean(
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);
+ mAttentiveTimeoutConfig = resources.getInteger(
+ com.android.internal.R.integer.config_attentiveTimeout);
+ mAttentiveWarningDurationConfig = resources.getInteger(
+ com.android.internal.R.integer.config_attentiveWarningDuration);
mDreamsSupportedConfig = resources.getBoolean(
com.android.internal.R.bool.config_dreamsSupported);
mDreamsEnabledByDefaultConfig = resources.getBoolean(
@@ -1015,6 +1045,9 @@ public final class PowerManagerService extends SystemService
mSleepTimeoutSetting = Settings.Secure.getIntForUser(resolver,
Settings.Secure.SLEEP_TIMEOUT, DEFAULT_SLEEP_TIMEOUT,
UserHandle.USER_CURRENT);
+ mAttentiveTimeoutSetting = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ATTENTIVE_TIMEOUT, mAttentiveTimeoutConfig,
+ UserHandle.USER_CURRENT);
mStayOnWhilePluggedInSetting = Settings.Global.getInt(resolver,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC);
mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(),
@@ -1700,6 +1733,7 @@ public final class PowerManagerService extends SystemService
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
+ updateAttentiveStateLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
@@ -2042,8 +2076,10 @@ public final class PowerManagerService extends SystemService
if (mWakefulness == WAKEFULNESS_AWAKE
|| mWakefulness == WAKEFULNESS_DREAMING
|| mWakefulness == WAKEFULNESS_DOZING) {
- final long sleepTimeout = getSleepTimeoutLocked();
- final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
+ final long attentiveTimeout = getAttentiveTimeoutLocked();
+ final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
+ final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
+ attentiveTimeout);
final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
final long nextProfileTimeout = getNextProfileTimeoutLocked(now);
@@ -2134,6 +2170,12 @@ public final class PowerManagerService extends SystemService
mHandler.sendMessageAtTime(msg, timeMs);
}
+ private void scheduleAttentiveTimeout(long timeMs) {
+ final Message msg = mHandler.obtainMessage(MSG_ATTENTIVE_TIMEOUT);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, timeMs);
+ }
+
/**
* Finds the next profile timeout time or returns -1 if there are no profiles to be locked.
*/
@@ -2150,6 +2192,69 @@ public final class PowerManagerService extends SystemService
return nextTimeout;
}
+ private void updateAttentiveStateLocked(long now, int dirty) {
+ long attentiveTimeout = getAttentiveTimeoutLocked();
+ long goToSleepTime = mLastUserActivityTime + attentiveTimeout;
+ long showWarningTime = goToSleepTime - mAttentiveWarningDurationConfig;
+
+ boolean warningDismissed = maybeHideInattentiveSleepWarningLocked(now, showWarningTime);
+
+ if (attentiveTimeout >= 0 && (warningDismissed
+ || (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST
+ | DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED
+ | DIRTY_SETTINGS)) != 0)) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "Updating attentive state");
+ }
+
+ mHandler.removeMessages(MSG_ATTENTIVE_TIMEOUT);
+
+ if (isBeingKeptFromShowingInattentiveSleepWarningLocked()) {
+ return;
+ }
+
+ long nextTimeout = -1;
+
+ if (now < showWarningTime) {
+ nextTimeout = showWarningTime;
+ } else if (now < goToSleepTime) {
+ if (DEBUG) {
+ long timeToSleep = goToSleepTime - now;
+ Slog.d(TAG, "Going to sleep in " + timeToSleep
+ + "ms if there is no user activity");
+ }
+ mInattentiveSleepWarningOverlayController.show();
+ nextTimeout = goToSleepTime;
+ } else {
+ if (DEBUG && mWakefulness != WAKEFULNESS_ASLEEP) {
+ Slog.i(TAG, "Going to sleep now due to long user inactivity");
+ }
+ }
+
+ if (nextTimeout >= 0) {
+ scheduleAttentiveTimeout(nextTimeout);
+ }
+ }
+ }
+
+ private boolean maybeHideInattentiveSleepWarningLocked(long now, long showWarningTime) {
+ long attentiveTimeout = getAttentiveTimeoutLocked();
+
+ if (mInattentiveSleepWarningOverlayController.isShown() && (attentiveTimeout < 0
+ || isBeingKeptFromShowingInattentiveSleepWarningLocked()
+ || now < showWarningTime)) {
+ mInattentiveSleepWarningOverlayController.dismiss();
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isAttentiveTimeoutExpired(long now) {
+ long attentiveTimeout = getAttentiveTimeoutLocked();
+ return attentiveTimeout >= 0 && now > mLastUserActivityTime + attentiveTimeout;
+ }
+
/**
* Called when a user activity timeout has occurred.
* Simply indicates that something about user activity has changed so that the new
@@ -2169,15 +2274,38 @@ public final class PowerManagerService extends SystemService
}
}
- private long getSleepTimeoutLocked() {
- final long timeout = mSleepTimeoutSetting;
+ private void handleAttentiveTimeout() { // runs on handler thread
+ synchronized (mLock) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "handleAttentiveTimeout");
+ }
+
+ mDirty |= DIRTY_ATTENTIVE;
+ updatePowerStateLocked();
+ }
+ }
+
+ private long getAttentiveTimeoutLocked() {
+ long timeout = mAttentiveTimeoutSetting;
+ if (timeout <= 0) {
+ return -1;
+ }
+
+ return Math.max(timeout, mMinimumScreenOffTimeoutConfig);
+ }
+
+ private long getSleepTimeoutLocked(long attentiveTimeout) {
+ long timeout = mSleepTimeoutSetting;
if (timeout <= 0) {
return -1;
}
+ if (attentiveTimeout >= 0) {
+ timeout = Math.min(timeout, attentiveTimeout);
+ }
return Math.max(timeout, mMinimumScreenOffTimeoutConfig);
}
- private long getScreenOffTimeoutLocked(long sleepTimeout) {
+ private long getScreenOffTimeoutLocked(long sleepTimeout, long attentiveTimeout) {
long timeout = mScreenOffTimeoutSetting;
if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
@@ -2188,6 +2316,9 @@ public final class PowerManagerService extends SystemService
if (sleepTimeout >= 0) {
timeout = Math.min(timeout, sleepTimeout);
}
+ if (attentiveTimeout >= 0) {
+ timeout = Math.min(timeout, attentiveTimeout);
+ }
return Math.max(timeout, mMinimumScreenOffTimeoutConfig);
}
@@ -2209,13 +2340,16 @@ public final class PowerManagerService extends SystemService
boolean changed = false;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
- | DIRTY_DOCK_STATE)) != 0) {
+ | DIRTY_DOCK_STATE | DIRTY_ATTENTIVE)) != 0) {
if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
}
final long time = SystemClock.uptimeMillis();
- if (shouldNapAtBedTimeLocked()) {
+ if (isAttentiveTimeoutExpired(time)) {
+ changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+ } else if (shouldNapAtBedTimeLocked()) {
changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
} else {
changed = goToSleepNoUpdateLocked(time,
@@ -2242,7 +2376,16 @@ public final class PowerManagerService extends SystemService
* to being fully awake or else go to sleep for good.
*/
private boolean isItBedTimeYetLocked() {
- return mBootCompleted && !isBeingKeptAwakeLocked();
+ if (!mBootCompleted) {
+ return false;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ if (isAttentiveTimeoutExpired(now)) {
+ return !isBeingKeptFromInattentiveSleepLocked();
+ } else {
+ return !isBeingKeptAwakeLocked();
+ }
}
/**
@@ -2263,11 +2406,29 @@ public final class PowerManagerService extends SystemService
}
/**
+ * Returns true if the device is prevented from going into inattentive sleep by the stay on
+ * while powered setting. We also keep the device awake when the proximity sensor returns a
+ * positive result so that the device does not lock while in a phone call. This function only
+ * controls whether the device will go to sleep which is independent of whether it will be
+ * allowed to suspend.
+ */
+ private boolean isBeingKeptFromInattentiveSleepLocked() {
+ return mStayOn || mScreenBrightnessBoostInProgress || mProximityPositive
+ || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
+ | USER_ACTIVITY_SCREEN_DIM)) != 0;
+ }
+
+ private boolean isBeingKeptFromShowingInattentiveSleepWarningLocked() {
+ return mStayOn || mScreenBrightnessBoostInProgress || mProximityPositive || !mBootCompleted;
+ }
+
+ /**
* Determines whether to post a message to the sandman to update the dream state.
*/
private void updateDreamLocked(int dirty, boolean displayBecameReady) {
if ((dirty & (DIRTY_WAKEFULNESS
| DIRTY_USER_ACTIVITY
+ | DIRTY_ATTENTIVE
| DIRTY_WAKE_LOCKS
| DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS
@@ -2350,6 +2511,7 @@ public final class PowerManagerService extends SystemService
}
// Determine whether the dream should continue.
+ long now = SystemClock.uptimeMillis();
if (wakefulness == WAKEFULNESS_DREAMING) {
if (isDreaming && canDreamLocked()) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
@@ -2371,11 +2533,15 @@ public final class PowerManagerService extends SystemService
// Dream has ended or will be stopped. Update the power state.
if (isItBedTimeYetLocked()) {
- goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
+ int flags = 0;
+ if (isAttentiveTimeoutExpired(now)) {
+ flags |= PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE;
+ }
+ goToSleepNoUpdateLocked(now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags,
+ Process.SYSTEM_UID);
updatePowerStateLocked();
} else {
- wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
+ wakeUpNoUpdateLocked(now,
PowerManager.WAKE_REASON_UNKNOWN,
"android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
@@ -2387,7 +2553,7 @@ public final class PowerManagerService extends SystemService
}
// Doze has ended or will be stopped. Update the power state.
- reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
+ reallyGoToSleepNoUpdateLocked(now, Process.SYSTEM_UID);
updatePowerStateLocked();
}
}
@@ -3474,6 +3640,9 @@ public final class PowerManagerService extends SystemService
pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);
+ pw.println(" mAttentiveTimeoutConfig=" + mAttentiveTimeoutConfig);
+ pw.println(" mAttentiveTimeoutSetting=" + mAttentiveTimeoutSetting);
+ pw.println(" mAttentiveWarningDurationConfig=" + mAttentiveWarningDurationConfig);
pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting);
pw.println(" mSleepTimeoutSetting=" + mSleepTimeoutSetting);
pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin="
@@ -3501,10 +3670,12 @@ public final class PowerManagerService extends SystemService
pw.println(" mForegroundProfile=" + mForegroundProfile);
pw.println(" mUserId=" + mUserId);
- final long sleepTimeout = getSleepTimeoutLocked();
- final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
+ final long attentiveTimeout = getAttentiveTimeoutLocked();
+ final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
+ final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, attentiveTimeout);
final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
pw.println();
+ pw.println("Attentive timeout: " + attentiveTimeout + " ms");
pw.println("Sleep timeout: " + sleepTimeout + " ms");
pw.println("Screen off timeout: " + screenOffTimeout + " ms");
pw.println("Screen dim duration: " + screenDimDuration + " ms");
@@ -3772,6 +3943,16 @@ public final class PowerManagerService extends SystemService
PowerServiceSettingsAndConfigurationDumpProto.SLEEP_TIMEOUT_SETTING_MS,
mSleepTimeoutSetting);
proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ATTENTIVE_TIMEOUT_SETTING_MS,
+ mAttentiveTimeoutSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ATTENTIVE_TIMEOUT_CONFIG_MS,
+ mAttentiveTimeoutConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .ATTENTIVE_WARNING_DURATION_CONFIG_MS,
+ mAttentiveWarningDurationConfig);
+ proto.write(
PowerServiceSettingsAndConfigurationDumpProto
.MAXIMUM_SCREEN_OFF_TIMEOUT_FROM_DEVICE_ADMIN_MS,
// Clamp to int32
@@ -3853,9 +4034,11 @@ public final class PowerManagerService extends SystemService
mIsVrModeEnabled);
proto.end(settingsAndConfigurationToken);
- final long sleepTimeout = getSleepTimeoutLocked();
- final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
+ final long attentiveTimeout = getAttentiveTimeoutLocked();
+ final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
+ final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, attentiveTimeout);
final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ proto.write(PowerManagerServiceDumpProto.ATTENTIVE_TIMEOUT_MS, attentiveTimeout);
proto.write(PowerManagerServiceDumpProto.SLEEP_TIMEOUT_MS, sleepTimeout);
proto.write(PowerManagerServiceDumpProto.SCREEN_OFF_TIMEOUT_MS, screenOffTimeout);
proto.write(PowerManagerServiceDumpProto.SCREEN_DIM_DURATION_MS, screenDimDuration);
@@ -4009,6 +4192,9 @@ public final class PowerManagerService extends SystemService
case MSG_CHECK_FOR_LONG_WAKELOCKS:
checkForLongWakeLocks();
break;
+ case MSG_ATTENTIVE_TIMEOUT:
+ handleAttentiveTimeout();
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 25c41f5cdd6b..c256b8499ee8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1375,6 +1375,28 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
this, in, out, err, args, callback, resultReceiver);
}
+ @Override
+ public void showInattentiveSleepWarning() {
+ enforceStatusBarService();
+ if (mBar != null) {
+ try {
+ mBar.showInattentiveSleepWarning();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
+ public void dismissInattentiveSleepWarning() {
+ enforceStatusBarService();
+ if (mBar != null) {
+ try {
+ mBar.dismissInattentiveSleepWarning();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
public String[] getStatusBarIcons() {
return mContext.getResources().getStringArray(R.array.config_statusBarIcons);
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0a861ade2900..73034b020a76 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -548,7 +548,7 @@ class ActivityMetricsLogger {
private static boolean hasActivityToBeDrawn(Task t) {
for (int i = t.getChildCount() - 1; i >= 0; --i) {
final ActivityRecord r = t.getChildAt(i);
- if (r.mVisibleRequested && !r.mDrawn && !r.finishing) {
+ if (r.visible && !r.mDrawn && !r.finishing) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2162bdeead05..e80c9b33d57b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,7 +121,6 @@ import static com.android.server.am.ActivityRecordProto.PROC_ID;
import static com.android.server.am.ActivityRecordProto.STATE;
import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.am.ActivityRecordProto.VISIBLE_REQUESTED;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
@@ -173,10 +172,12 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_W
import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
-import static com.android.server.wm.AppWindowTokenProto.CLIENT_VISIBLE;
+import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
import static com.android.server.wm.AppWindowTokenProto.DEFER_HIDING_CLIENT;
import static com.android.server.wm.AppWindowTokenProto.FILLS_PARENT;
import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.IS_ANIMATING;
import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN;
@@ -191,7 +192,6 @@ import static com.android.server.wm.AppWindowTokenProto.STARTING_DISPLAYED;
import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
-import static com.android.server.wm.AppWindowTokenProto.VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
@@ -462,16 +462,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private boolean keysPaused; // has key dispatching been paused for it?
int launchMode; // the launch mode activity attribute.
int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
- private boolean mVisible; // Should this token's windows be visible?
+ boolean visible; // does this activity's window need to be shown?
boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
// might hide this activity?
- // True if the visible state of this token was forced to true due to a transferred starting
+ // True if the hidden state of this token was forced to false due to a transferred starting
// window.
- private boolean mVisibleSetFromTransferredStartingWindow;
- // TODO: figure out how to consolidate with the same variable in ActivityRecord.
+ private boolean mHiddenSetFromTransferredStartingWindow;
+ // TODO: figureout how to consolidate with the same variable in ActivityRecord.
private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
// process that it is hidden.
- private boolean mLastDeferHidingClient; // If true we will defer setting mClientVisible to false
+ private boolean mLastDeferHidingClient; // If true we will defer setting mClientHidden to true
// and reporting to the client that it is hidden.
boolean sleeping; // have we told the activity to sleep?
boolean nowVisible; // is this activity's window visible?
@@ -536,8 +536,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private Task mLastParent;
- // Have we told the window clients to show themselves?
- private boolean mClientVisible;
+ // Have we told the window clients to hide themselves?
+ private boolean mClientHidden;
boolean firstWindowDrawn;
// Last drawn state we reported to the app token.
@@ -622,11 +622,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// case do not clear allDrawn until the animation completes.
boolean deferClearAllDrawn;
- // Is this window's surface needed? This is almost like visible, except
- // it will sometimes be true a little earlier: when the activity record has
+ // Is this window's surface needed? This is almost like hidden, except
+ // it will sometimes be true a little earlier: when the token has
// been shown, but is still waiting for its app transition to execute
// before making its windows shown.
- boolean mVisibleRequested;
+ boolean hiddenRequested;
// Last visibility state we reported to the app token.
boolean reportedVisible;
@@ -836,6 +836,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
+ pw.print(" visible="); pw.print(visible);
pw.print(" sleeping="); pw.print(sleeping);
pw.print(" idle="); pw.print(idle);
pw.print(" mStartingWindowState=");
@@ -854,14 +855,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(requestedVrComponent);
}
super.dump(pw, prefix, dumpAll);
- pw.print(" visible="); pw.print(mVisible);
if (appToken != null) {
pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction);
}
pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
pw.print(" mOrientation="); pw.println(mOrientation);
- pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
- + " mClientVisible=" + mClientVisible
+ pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
+ " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible);
if (paused) {
@@ -886,13 +885,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" mIsExiting="); pw.println(mIsExiting);
}
if (startingWindow != null || startingSurface != null
- || startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
+ || startingDisplayed || startingMoved || mHiddenSetFromTransferredStartingWindow) {
pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
pw.print(" startingSurface="); pw.print(startingSurface);
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
pw.print(" startingMoved="); pw.print(startingMoved);
pw.println(" mHiddenSetFromTransferredStartingWindow="
- + mVisibleSetFromTransferredStartingWindow);
+ + mHiddenSetFromTransferredStartingWindow);
}
if (!mFrozenBounds.isEmpty()) {
pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
@@ -1160,7 +1159,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
this.task = task;
}
- TaskStack getStack() {
+ ActivityStack getStack() {
return task != null ? task.getTaskStack() : null;
}
@@ -1202,7 +1201,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else if (mLastParent != null && mLastParent.getTaskStack() != null) {
task.getTaskStack().mExitingActivities.remove(this);
}
- final TaskStack stack = getStack();
+ final ActivityStack stack = getStack();
// If we reparent, make sure to remove ourselves from the old animation registry.
if (mAnimatingActivityRegistry != null) {
@@ -1266,7 +1265,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (prevDc.mFocusedApp == this) {
prevDc.setFocusedApp(null);
- final TaskStack stack = dc.getTopStack();
+ final ActivityStack stack = dc.getTopStack();
if (stack != null) {
final Task task = stack.getTopChild();
if (task != null && task.getTopChild() == this) {
@@ -1488,8 +1487,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// Application tokens start out hidden.
- setVisible(false);
- mVisibleRequested = false;
+ setHidden(true);
+ hiddenRequested = true;
ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
ColorDisplayService.ColorDisplayServiceInternal.class);
@@ -1518,9 +1517,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
deferRelaunchUntilPaused = false;
keysPaused = false;
inHistory = false;
+ visible = false;
nowVisible = false;
mDrawn = false;
- mClientVisible = true;
idle = false;
hasBeenLaunched = false;
mStackSupervisor = supervisor;
@@ -2201,7 +2200,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* 2. App is delayed closing since it might enter PIP.
*/
boolean isClosingOrEnteringPip() {
- return (isAnimating(TRANSITION | PARENTS) && !mVisibleRequested) || mWillCloseOrEnterPip;
+ return (isAnimating(TRANSITION | PARENTS) && hiddenRequested) || mWillCloseOrEnterPip;
}
/**
* @return Whether AppOps allows this package to enter picture-in-picture.
@@ -2460,7 +2459,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.getLockTaskController().clearLockedTask(task);
}
} else if (!isState(PAUSING)) {
- if (mVisibleRequested) {
+ if (visible) {
// Prepare and execute close transition.
prepareActivityHideTransitionAnimation(transit);
}
@@ -2539,13 +2538,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
final ActivityRecord next = getDisplay().topRunningActivity(
true /* considerKeyguardState */);
- final boolean isVisible = mVisibleRequested || nowVisible;
+ final boolean isVisible = visible || nowVisible;
// isNextNotYetVisible is to check if the next activity is invisible, or it has been
// requested to be invisible but its windows haven't reported as invisible. If so, it
// implied that the current finishing activity should be added into stopping list rather
// than destroy immediately.
- final boolean isNextNotYetVisible = next != null
- && (!next.nowVisible || !next.mVisibleRequested);
+ final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible);
if (isVisible && isNextNotYetVisible) {
// Add this activity to the list of stopping activities. It will be processed and
// destroyed when the next activity reports idle.
@@ -3023,7 +3021,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
}
- final TaskStack stack = getStack();
+ final ActivityStack stack = getStack();
if (delayed && !isEmpty()) {
// set the token aside because it has an active animation to be finished
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
@@ -3213,7 +3211,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
"Removing starting %s from %s", tStartingWindow, fromActivity);
fromActivity.removeChild(tStartingWindow);
fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow);
- fromActivity.mVisibleSetFromTransferredStartingWindow = false;
+ fromActivity.mHiddenSetFromTransferredStartingWindow = false;
addWindow(tStartingWindow);
// Propagate other interesting state between the tokens. If the old token is displayed,
@@ -3226,12 +3224,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (fromActivity.firstWindowDrawn) {
firstWindowDrawn = true;
}
- if (fromActivity.isVisible()) {
- setVisible(true);
- mVisibleRequested = true;
- mVisibleSetFromTransferredStartingWindow = true;
+ if (!fromActivity.isHidden()) {
+ setHidden(false);
+ hiddenRequested = false;
+ mHiddenSetFromTransferredStartingWindow = true;
}
- setClientVisible(fromActivity.mClientVisible);
+ setClientHidden(fromActivity.mClientHidden);
transferAnimation(fromActivity);
@@ -3277,7 +3275,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (fromActivity == this) {
return;
}
- if (!fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token)) {
+ if (fromActivity.hiddenRequested && transferStartingWindow(fromActivity.token)) {
return;
}
}
@@ -3753,7 +3751,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Notify the pinned stack upon all windows drawn. If there was an animation in
// progress then this signal will resume that animation.
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
if (pinnedStack != null) {
pinnedStack.onAllWindowsDrawn();
}
@@ -3792,10 +3790,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return opts;
}
- boolean allowMoveToFront() {
- return pendingOptions == null || !pendingOptions.getAvoidMoveToFront();
- }
-
void removeUriPermissionsLocked() {
if (uriPermissions != null) {
uriPermissions.removeUriPermissions();
@@ -3832,7 +3826,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
mDeferHidingClient = deferHidingClient;
- if (!mDeferHidingClient && !mVisibleRequested) {
+ if (!mDeferHidingClient && !visible) {
// Hiding the client is no longer deferred and the app isn't visible still, go ahead and
// update the visibility.
setVisibility(false);
@@ -3843,14 +3837,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean isVisible() {
// If the activity isn't hidden then it is considered visible and there is no need to check
// its children windows to see if they are visible.
- return mVisible;
- }
-
- void setVisible(boolean visible) {
- if (visible != mVisible) {
- mVisible = visible;
- scheduleAnimation();
- }
+ return !isHidden();
}
void setVisibility(boolean visible) {
@@ -3859,17 +3846,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ appToken);
return;
}
- if (visible) {
- mDeferHidingClient = false;
- }
setVisibility(visible, mDeferHidingClient);
mAtmService.addWindowLayoutReasons(
ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
+ }
+
+ // TODO: Look into merging with #commitVisibility()
+ void setVisible(boolean newVisible) {
+ visible = newVisible;
+ mDeferHidingClient = !visible && mDeferHidingClient;
+ setVisibility(visible);
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
}
- @VisibleForTesting
void setVisibility(boolean visible, boolean deferHidingClient) {
final AppTransition appTransition = getDisplayContent().mAppTransition;
@@ -3880,20 +3870,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// transition can be selected.
// TODO: Probably a good idea to separate the concept of opening/closing apps from the
// concept of setting visibility...
- if (!visible && !mVisibleRequested) {
+ if (!visible && hiddenRequested) {
if (!deferHidingClient && mLastDeferHidingClient) {
// We previously deferred telling the client to hide itself when visibility was
// initially set to false. Now we would like it to hide, so go ahead and set it.
mLastDeferHidingClient = deferHidingClient;
- setClientVisible(false);
+ setClientHidden(true);
}
return;
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
- appToken, visible, appTransition, isVisible(), mVisibleRequested,
+ "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+ appToken, visible, appTransition, isHidden(), hiddenRequested,
Debug.getCallers(6));
final DisplayContent displayContent = getDisplayContent();
@@ -3904,7 +3894,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
displayContent.mChangingApps.remove(this);
waitingToShow = false;
- mVisibleRequested = visible;
+ hiddenRequested = !visible;
mLastDeferHidingClient = deferHidingClient;
if (!visible) {
@@ -3923,15 +3913,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
startingMoved = false;
// If the token is currently hidden (should be the common case), or has been
// stopped, then we need to set up to wait for its windows to be ready.
- if (!isVisible() || mAppStopped) {
+ if (isHidden() || mAppStopped) {
clearAllDrawn();
// If the app was already visible, don't reset the waitingToShow state.
- if (!isVisible()) {
+ if (isHidden()) {
waitingToShow = true;
// If the client isn't hidden, we don't need to reset the drawing state.
- if (!isClientVisible()) {
+ if (isClientHidden()) {
// Let's reset the draw state in order to prevent the starting window to be
// immediately dismissed when the app still has the surface.
forAllWindows(w -> {
@@ -3951,7 +3941,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// we still need to tell the client to make its windows visible so they get drawn.
// Otherwise, we will wait on performing the transition until all windows have been
// drawn, they never will be, and we are sad.
- setClientVisible(true);
+ setClientHidden(false);
requestUpdateWallpaperIfNeeded();
@@ -3997,9 +3987,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
boolean delayed = false;
- // Reset the state of mVisibleSetFromTransferredStartingWindow since visibility is actually
+ // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
// been set by the app now.
- mVisibleSetFromTransferredStartingWindow = false;
+ mHiddenSetFromTransferredStartingWindow = false;
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
@@ -4009,7 +3999,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// * or the token is the opening app and visible while opening task behind existing one.
final DisplayContent displayContent = getDisplayContent();
boolean visibilityChanged = false;
- if (isVisible() != visible || (!isVisible() && mIsExiting)
+ if (isHidden() == visible || (isHidden() && mIsExiting)
|| (visible && waitingForReplacement())
|| (visible && displayContent.mOpeningApps.contains(this)
&& displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
@@ -4017,7 +4007,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mWmService.mAccessibilityController;
boolean changed = false;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Changing app %s visible=%b performLayout=%b", this, isVisible(),
+ "Changing app %s hidden=%b performLayout=%b", this, isHidden(),
performLayout);
boolean runningAppAnimation = false;
@@ -4042,8 +4032,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
}
- setVisible(visible);
- mVisibleRequested = visible;
+ setHidden(!visible);
+ hiddenRequested = !visible;
visibilityChanged = true;
if (!visible) {
stopFreezingScreen(true, true);
@@ -4061,8 +4051,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "commitVisibility: %s: visible=%b visibleRequested=%b", this,
- isVisible(), mVisibleRequested);
+ "commitVisibility: %s: hidden=%b hiddenRequested=%b", this,
+ isHidden(), hiddenRequested);
if (changed) {
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
@@ -4099,7 +4089,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
if (visible || !isAnimating()) {
- setClientVisible(visible);
+ setClientHidden(!visible);
}
if (!displayContent.mClosingApps.contains(this)
@@ -4126,7 +4116,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// no animation but there will still be a transition set.
// We still need to delay hiding the surface such that it
// can be synchronized with showing the next surface in the transition.
- if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
+ if (isHidden() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
SurfaceControl.openTransaction();
for (int i = mChildren.size() - 1; i >= 0; i--) {
mChildren.get(i).mWinAnimator.hide("immediately hidden");
@@ -4139,6 +4129,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
+ void setHidden(boolean hidden) {
+ super.setHidden(hidden);
+ scheduleAnimation();
+ }
+
+ @Override
void onAppTransitionDone() {
sendingToBottom = false;
}
@@ -4401,7 +4397,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
updateOptionsLocked(returningOptions);
stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
}
- setVisibility(true);
+ setVisible(true);
sleeping = false;
app.postPendingUiCleanMsg(true);
if (reportToClient) {
@@ -4437,7 +4433,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void makeInvisible() {
- if (!mVisibleRequested) {
+ if (!visible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
return;
}
@@ -4459,7 +4455,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean deferHidingClient = canEnterPictureInPicture
&& !isState(STOPPING, STOPPED, PAUSED);
setDeferHidingClient(deferHidingClient);
- setVisibility(false);
+ setVisible(false);
switch (getState()) {
case STOPPING:
@@ -4646,8 +4642,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* state to match that fact.
*/
void completeResumeLocked() {
- final boolean wasVisible = mVisibleRequested;
- setVisibility(true);
+ final boolean wasVisible = visible;
+ setVisible(true);
if (!wasVisible) {
// Visibility has changed, so take a note of it so we call the TaskStackChangedListener
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
@@ -4731,16 +4727,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
setState(STOPPING, "stopIfPossible");
if (DEBUG_VISIBILITY) {
- Slog.v(TAG_VISIBILITY, "Stopping visibleRequested="
- + mVisibleRequested + " for " + this);
+ Slog.v(TAG_VISIBILITY, "Stopping visible=" + visible + " for " + this);
}
- if (!mVisibleRequested) {
- setVisibility(false);
+ if (!visible) {
+ setVisible(false);
}
EventLogTags.writeAmStopActivity(
mUserId, System.identityHashCode(this), shortComponentName);
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
- StopActivityItem.obtain(mVisibleRequested, configChangeFlags));
+ StopActivityItem.obtain(visible, configChangeFlags));
if (stack.shouldSleepOrShutDownActivities()) {
setSleeping(true);
}
@@ -4903,10 +4898,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void startFreezingScreen() {
ProtoLog.i(WM_DEBUG_ORIENTATION,
- "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
- appToken, isVisible(), mFreezingScreen, mVisibleRequested,
+ "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+ appToken, isHidden(), mFreezingScreen, hiddenRequested,
new RuntimeException().fillInStackTrace());
- if (mVisibleRequested) {
+ if (!hiddenRequested) {
if (!mFreezingScreen) {
mFreezingScreen = true;
mWmService.registerAppFreezeListener(this);
@@ -4942,8 +4937,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Clear freezing of %s: visible=%b freezing=%b", appToken,
- isVisible(), isFreezingScreen());
+ "Clear freezing of %s: hidden=%b freezing=%b", appToken,
+ isHidden(), isFreezingScreen());
stopFreezingScreen(true, force);
}
}
@@ -5103,7 +5098,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean nowGone = mReportedVisibilityResults.nowGone;
boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
- boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && isVisible();
+ boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !isHidden();
if (!nowGone) {
// If the app is not yet gone, then it can only become visible/drawn.
if (!nowDrawn) {
@@ -5131,18 +5126,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- boolean isClientVisible() {
- return mClientVisible;
+ boolean isClientHidden() {
+ return mClientHidden;
}
- void setClientVisible(boolean clientVisible) {
- if (mClientVisible == clientVisible || (!clientVisible && mDeferHidingClient)) {
+ void setClientHidden(boolean hideClient) {
+ if (mClientHidden == hideClient || (hideClient && mDeferHidingClient)) {
return;
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
+ "setClientHidden: %s clientHidden=%b Callers=%s", this, hideClient,
Debug.getCallers(5));
- mClientVisible = clientVisible;
+ mClientHidden = hideClient;
sendAppVisibilityToClients();
}
@@ -5185,7 +5180,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+ " pv=" + w.isVisibleByPolicy()
+ " mDrawState=" + winAnimator.drawStateToString()
- + " ph=" + w.isParentWindowHidden() + " th=" + mVisibleRequested
+ + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
+ " a=" + isAnimating(TRANSITION));
}
}
@@ -5293,7 +5288,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* currently pausing, or is resumed.
*/
public boolean isInterestingToUserLocked() {
- return mVisibleRequested || nowVisible || mState == PAUSING || mState == RESUMED;
+ return visible || nowVisible || mState == PAUSING || mState == RESUMED;
}
void setSleeping(boolean _sleeping) {
@@ -5367,7 +5362,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// We're not ready for this kind of thing.
return false;
}
- if (mVisibleRequested) {
+ if (visible) {
// The user would notice this!
return false;
}
@@ -5464,11 +5459,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// well there is no point now.
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Nulling last startingData");
mStartingData = null;
- if (mVisibleSetFromTransferredStartingWindow) {
- // We set the visible state to true for the token from a transferred starting
- // window. We now reset it back to false since the starting window was the last
- // window in the token.
- setVisible(false);
+ if (mHiddenSetFromTransferredStartingWindow) {
+ // We set the hidden state to false for the token from a transferred starting window.
+ // We now reset it back to true since the starting window was the last window in the
+ // token.
+ setHidden(true);
}
} else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) {
// If this is the last window except for a starting transition window,
@@ -5786,7 +5781,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getTransit(), task)) {
task.getBounds(mTmpRect);
} else {
- final TaskStack stack = getStack();
+ final ActivityStack stack = getStack();
if (stack == null) {
return;
}
@@ -5806,7 +5801,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
void prepareSurfaces() {
- final boolean show = isVisible() || isAnimating();
+ final boolean show = !isHidden() || isAnimating();
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
@@ -5925,7 +5920,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
"AppWindowToken");
clearThumbnail();
- setClientVisible(isVisible() || mVisibleRequested);
+ setClientHidden(isHidden() && hiddenRequested);
getDisplayContent().computeImeTargetIfNeeded(this);
@@ -6522,7 +6517,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (display == null) {
return;
}
- if (mVisibleRequested) {
+ if (visible) {
// It may toggle the UI for user to restart the size compatibility mode activity.
display.handleActivitySizeCompatModeIfNeeded(this);
} else if (mCompatDisplayInsets != null) {
@@ -6546,7 +6541,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void savePinnedStackBounds() {
// Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
// for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
if (pinnedStack == null) return;
final Rect stackBounds;
if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) {
@@ -6819,7 +6814,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching " + this);
- if (DEBUG_STATES && !mVisibleRequested) {
+ if (DEBUG_STATES && !visible) {
Slog.v(TAG_STATES, "Config is relaunching invisible activity " + this
+ " called by " + Debug.getCallers(4));
}
@@ -7005,7 +7000,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Reset the existing override configuration so it can be updated according to the latest
// configuration.
clearSizeCompatMode();
- if (mVisibleRequested) {
+ if (visible) {
// Configuration will be ensured when becoming visible, so if it is already visible,
// then the manual update is needed.
updateSizeCompatMode();
@@ -7018,7 +7013,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The restarting state avoids removing this record when process is died.
setState(RESTARTING_PROCESS, "restartActivityProcess");
- if (!mVisibleRequested || mHaveState) {
+ if (!visible || mHaveState) {
// Kill its process immediately because the activity should be in background.
// The activity state will be update to {@link #DESTROYED} in
// {@link ActivityStack#cleanUp} when handling process died.
@@ -7309,13 +7304,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
writeToProto(proto, APP_WINDOW_TOKEN, WindowTraceLogLevel.ALL);
writeIdentifierToProto(proto, IDENTIFIER);
proto.write(STATE, mState.toString());
- proto.write(VISIBLE_REQUESTED, mVisibleRequested);
+ proto.write(VISIBLE, visible);
proto.write(FRONT_OF_TASK, isRootOfTask());
if (hasProcess()) {
proto.write(PROC_ID, app.getPid());
}
proto.write(TRANSLUCENT, !occludesParent());
- proto.write(VISIBLE, mVisible);
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -7346,8 +7340,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
proto.write(FILLS_PARENT, mOccludesParent);
proto.write(APP_STOPPED, mAppStopped);
- proto.write(com.android.server.wm.AppWindowTokenProto.VISIBLE_REQUESTED, mVisibleRequested);
- proto.write(CLIENT_VISIBLE, mClientVisible);
+ proto.write(HIDDEN_REQUESTED, hiddenRequested);
+ proto.write(CLIENT_HIDDEN, mClientHidden);
proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
proto.write(REPORTED_DRAWN, reportedDrawn);
proto.write(REPORTED_VISIBLE, reportedVisible);
@@ -7361,12 +7355,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
proto.write(STARTING_DISPLAYED, startingDisplayed);
proto.write(STARTING_MOVED, startingMoved);
- proto.write(VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW,
- mVisibleSetFromTransferredStartingWindow);
+ proto.write(HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW,
+ mHiddenSetFromTransferredStartingWindow);
for (Rect bounds : mFrozenBounds) {
bounds.writeToProto(proto, FROZEN_BOUNDS);
}
- proto.write(com.android.server.wm.AppWindowTokenProto.VISIBLE, mVisible);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 6e75f9c9167f..c56a9e2ac560 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -73,7 +73,7 @@ public class ActivityServiceConnectionsHolder<T> {
public boolean isActivityVisible() {
synchronized (mService.mGlobalLock) {
- return mActivity.mVisibleRequested || mActivity.isState(RESUMED, PAUSING);
+ return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2aea81724627..a91998aa2b04 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -16,12 +16,15 @@
package com.android.server.wm;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -33,10 +36,17 @@ import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
@@ -48,13 +58,10 @@ 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 com.android.server.am.ActivityStackProto.BOUNDS;
import static com.android.server.am.ActivityStackProto.DISPLAY_ID;
import static com.android.server.am.ActivityStackProto.FULLSCREEN;
-import static com.android.server.am.ActivityStackProto.ID;
import static com.android.server.am.ActivityStackProto.RESUMED_ACTIVITY;
import static com.android.server.am.ActivityStackProto.STACK;
-import static com.android.server.am.ActivityStackProto.TASKS;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
@@ -97,8 +104,24 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.BoundsAnimationController.FADE_IN;
+import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.RootActivityContainer.FindTaskResult;
+import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
+import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
+import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
+import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
+import static com.android.server.wm.StackProto.ANIMATING_BOUNDS;
+import static com.android.server.wm.StackProto.DEFER_REMOVAL;
+import static com.android.server.wm.StackProto.FILLS_PARENT;
+import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
+import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -111,6 +134,7 @@ import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
+import android.app.RemoteAction;
import android.app.ResultInfo;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
@@ -124,7 +148,9 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
import android.net.Uri;
import android.os.Binder;
import android.os.Debug;
@@ -138,17 +164,24 @@ import android.os.Trace;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.logging.MetricsLoggerWrapper;
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
@@ -167,7 +200,8 @@ import java.util.function.Consumer;
/**
* State and management of a single stack of activities.
*/
-class ActivityStack extends TaskStack {
+class ActivityStack extends WindowContainer<Task> implements BoundsAnimationTarget,
+ ConfigurationContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -228,52 +262,12 @@ class ActivityStack extends TaskStack {
/** Stack is completely invisible. */
static final int STACK_VISIBILITY_INVISIBLE = 2;
- // TODO(display-unify): Remove after display unification.
- protected void onParentChanged(ActivityDisplay newParent, ActivityDisplay oldParent) {
- onParentChanged(
- newParent != null ? newParent.mDisplayContent : null,
- oldParent != null ? oldParent.mDisplayContent : null);
- }
-
- @Override
- protected void onParentChanged(
- ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- final ActivityDisplay display = newParent != null
- ? ((WindowContainer) newParent).getDisplayContent().mActivityDisplay : null;
- final ActivityDisplay oldDisplay = oldParent != null
- ? ((WindowContainer) oldParent).getDisplayContent().mActivityDisplay : null;
-
- mDisplayId = (display != null) ? display.mDisplayId : INVALID_DISPLAY;
- mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
-
- if (display != null) {
- // Rotations are relative to the display. This means if there are 2 displays rotated
- // differently (eg. 2 monitors with one landscape and one portrait), moving a stack
- // from one to the other could look like a rotation change. To prevent this
- // apparent rotation change (and corresponding bounds rotation), pretend like our
- // current rotation is already the same as the new display.
- // Note, if ActivityStack or related logic ever gets nested, this logic will need
- // to move to onConfigurationChanged.
- getConfiguration().windowConfiguration.setRotation(
- display.getWindowConfiguration().getRotation());
- }
- super.onParentChanged(newParent, oldParent);
- if (display != null && inSplitScreenPrimaryWindowingMode()) {
- // If we created a docked stack we want to resize it so it resizes all other stacks
- // in the system.
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
- mTmpRect2, null, null, PRESERVE_WINDOWS);
- }
- mRootActivityContainer.updateUIDsPresentOnDisplay();
+ /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+ * restrict IME adjustment so that a min portion of top stack remains visible.*/
+ private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
- // Resume next focusable stack after reparenting to another display if we aren't removing
- // the prevous display.
- if (oldDisplay != null && oldDisplay.isRemoving()) {
- postReparent();
- }
- }
+ /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
+ private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
enum ActivityState {
INITIALIZING,
@@ -362,11 +356,71 @@ class ActivityStack extends TaskStack {
// Id of the previous display the stack was on.
int mPrevDisplayId = INVALID_DISPLAY;
+ /** Unique identifier */
+ final int mStackId;
+
+ /** For comparison with DisplayContent bounds. */
+ private Rect mTmpRect = new Rect();
+ private Rect mTmpRect2 = new Rect();
+ private Rect mTmpRect3 = new Rect();
+
+ /** For Pinned stack controlling. */
+ private Rect mTmpToBounds = new Rect();
+
+ /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
+ private final Rect mAdjustedBounds = new Rect();
+
+ /**
+ * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
+ * represent the state when the animation has ended.
+ */
+ private final Rect mFullyAdjustedImeBounds = new Rect();
+
+ /** ActivityRecords that are exiting, but still on screen for animations. */
+ final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>();
+
+ /** Detach this stack from its display when animation completes. */
+ // TODO: maybe tie this to WindowContainer#removeChild some how...
+ private boolean mDeferRemoval;
+
+ private final Rect mTmpAdjustedBounds = new Rect();
+ private boolean mAdjustedForIme;
+ private boolean mImeGoingAway;
+ private WindowState mImeWin;
+ private float mMinimizeAmount;
+ private float mAdjustImeAmount;
+ private float mAdjustDividerAmount;
+ private final int mDockedStackMinimizeThickness;
+
+ // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
+ // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
+ // would otherwise apply while resizing, while resizing in the bounds animating mode.
+ private boolean mBoundsAnimating = false;
+ // Set when an animation has been requested but has not yet started from the UI thread. This is
+ // cleared when the animation actually starts.
+ private boolean mBoundsAnimatingRequested = false;
+ private boolean mBoundsAnimatingToFullscreen = false;
+ private boolean mCancelCurrentBoundsAnimation = false;
+ private Rect mBoundsAnimationTarget = new Rect();
+ private Rect mBoundsAnimationSourceHintBounds = new Rect();
+ private @BoundsAnimationController.AnimationType int mAnimationType;
+
+ Rect mPreAnimationBounds = new Rect();
+
+ private Dimmer mDimmer = new Dimmer(this);
+
+ /**
+ * For {@link #prepareSurfaces}.
+ */
+ private final Rect mTmpDimBoundsRect = new Rect();
+ private final Point mLastSurfaceSize = new Point();
+
+ private final AnimatingActivityRegistry mAnimatingActivityRegistry =
+ new AnimatingActivityRegistry();
+
/** Stores the override windowing-mode from before a transient mode change (eg. split) */
private int mRestoreOverrideWindowingMode = WINDOWING_MODE_UNDEFINED;
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpRect2 = new Rect();
private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
/** List for processing through a set of activities */
@@ -474,12 +528,18 @@ class ActivityStack extends TaskStack {
ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
int windowingMode, int activityType, boolean onTop) {
- super(supervisor.mService.mWindowManager, stackId);
+ super(supervisor.mService.mWindowManager);
+ mStackId = stackId;
+ mDockedStackMinimizeThickness =
+ supervisor.mService.mWindowManager.mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_minimize_thickness);
+ EventLog.writeEvent(com.android.server.EventLogTags.WM_STACK_CREATED, stackId);
mStackSupervisor = supervisor;
mService = supervisor.mService;
mRootActivityContainer = mService.mRootActivityContainer;
mHandler = new ActivityStackHandler(supervisor.mLooper);
mWindowManager = mService.mWindowManager;
+ mRemoteToken = new RemoteToken(this);
mCurrentUser = mService.mAmInternal.getCurrentUserId();
// Set display id before setting activity and window type to make sure it won't affect
// stacks on a wrong display.
@@ -529,6 +589,27 @@ class ActivityStack extends TaskStack {
getBounds(newBounds);
super.onConfigurationChanged(newParentConfig);
+
+ // Only need to update surface size here since the super method will handle updating
+ // surface position.
+ updateSurfaceSize(getPendingTransaction());
+
+ if (mDisplayContent == null) {
+ return;
+ }
+
+ if (prevWindowingMode != getWindowingMode()) {
+ mDisplayContent.onStackWindowingModeChanged(this);
+
+ if (inSplitScreenSecondaryWindowingMode()) {
+ // When the stack is resized due to entering split screen secondary, offset the
+ // windows to compensate for the new stack position.
+ forAllWindows(w -> {
+ w.mWinAnimator.setOffsetPositionForStackResize(true);
+ }, true);
+ }
+ }
+
final ActivityDisplay display = getDisplay();
if (display == null ) {
return;
@@ -835,20 +916,6 @@ class ActivityStack extends TaskStack {
return mRootActivityContainer.getActivityDisplay(mDisplayId);
}
- void positionChildAtTop(Task child) {
- positionChildAtTop(child, true /* includingParents */);
- }
-
- private void positionChildAtBottom(Task child) {
- // If there are other focusable stacks on the display, the z-order of the display should not
- // be changed just because a task was placed at the bottom. E.g. if it is moving the topmost
- // task to bottom, the next focusable stack on the same display should be focused.
- final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
- child.getStack(), true /* ignoreCurrent */);
- positionChildAtBottom(child, nextFocusableStack == null /* includingParents */);
- child.updateTaskMovement(true);
- }
-
/**
* Defers updating the bounds of the stack. If the stack was resized/repositioned while
* deferring, the bounds will update in {@link #continueUpdateBounds()}.
@@ -1629,8 +1696,7 @@ class ActivityStack extends TaskStack {
prev = prev.completeFinishing("completePausedLocked");
} else if (prev.hasProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
- + " wasStopping=" + wasStopping
- + " visibleRequested=" + prev.mVisibleRequested);
+ + " wasStopping=" + wasStopping + " visible=" + prev.visible);
if (prev.deferRelaunchUntilPaused) {
// Complete the deferred relaunch that was waiting for pause to complete.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
@@ -1640,7 +1706,7 @@ class ActivityStack extends TaskStack {
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
prev.setState(STOPPING, "completePausedLocked");
- } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+ } else if (!prev.visible || shouldSleepOrShutDownActivities()) {
// Clear out any deferred client hide we might currently have.
prev.setDeferHidingClient(false);
// If we were visible then resumeTopActivities will release resources before
@@ -1761,7 +1827,7 @@ class ActivityStack extends TaskStack {
boolean isTopActivityVisible() {
final ActivityRecord topActivity = getTopActivity();
- return topActivity != null && topActivity.mVisibleRequested;
+ return topActivity != null && topActivity.visible;
}
/**
@@ -1902,7 +1968,7 @@ class ActivityStack extends TaskStack {
for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
final Task task = getChildAt(taskNdx);
ActivityRecord r = task.topRunningActivityLocked();
- if (r == null || r.finishing || !r.mVisibleRequested) {
+ if (r == null || r.finishing || !r.visible) {
task.mLayerRank = -1;
} else {
task.mLayerRank = baseLayer + layer++;
@@ -1991,7 +2057,7 @@ class ActivityStack extends TaskStack {
if (!r.attachedToProcess()) {
makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
resumeTopActivity && isTop, r);
- } else if (r.mVisibleRequested) {
+ } else if (r.visible) {
// If this activity is already visible, then there is nothing to do here.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Skipping: already visible at " + r);
@@ -2168,16 +2234,16 @@ class ActivityStack extends TaskStack {
// invisible. If the app is already visible, it must have died while it was visible. In this
// case, we'll show the dead window but will not restart the app. Otherwise we could end up
// thrashing.
- if (isTop || !r.mVisibleRequested) {
+ if (isTop || !r.visible) {
// This activity needs to be visible, but isn't even running...
// get it started and resume if no other stack in this stack is resumed.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
if (r != starting) {
r.startFreezingScreenLocked(configChanges);
}
- if (!r.mVisibleRequested || r.mLaunchTaskBehind) {
+ if (!r.visible || r.mLaunchTaskBehind) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
- r.setVisibility(true);
+ r.setVisible(true);
}
if (r != starting) {
mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
@@ -2620,8 +2686,7 @@ class ActivityStack extends TaskStack {
if (next.attachedToProcess()) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
- + " stopped=" + next.stopped
- + " visibleRequested=" + next.mVisibleRequested);
+ + " stopped=" + next.stopped + " visible=" + next.visible);
// If the previous activity is translucent, force a visibility update of
// the next activity, so that it's added to WM's opening app list, and
@@ -2636,7 +2701,7 @@ class ActivityStack extends TaskStack {
&& !lastFocusedStack.mLastPausedActivity.occludesParent()));
// This activity is now becoming visible.
- if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+ if (!next.visible || next.stopped || lastActivityTranslucent) {
next.setVisibility(true);
}
@@ -2691,7 +2756,7 @@ class ActivityStack extends TaskStack {
// Do over!
mStackSupervisor.scheduleResumeTopActivities();
}
- if (!next.mVisibleRequested || next.stopped) {
+ if (!next.visible || next.stopped) {
next.setVisibility(true);
}
next.completeResumeLocked();
@@ -3360,7 +3425,7 @@ class ActivityStack extends TaskStack {
final ActivityRecord top = stack.topRunningActivityLocked();
- if (stack.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) {
+ if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
// If we will be focusing on the home stack next and its current top activity isn't
// visible, then use the move the home stack task to top to make the activity visible.
stack.getDisplay().moveHomeActivityToTop(reason);
@@ -3857,7 +3922,7 @@ class ActivityStack extends TaskStack {
"Record #" + targetIndex + " " + r + ": app=" + r.app);
if (r.app == app) {
- if (r.mVisibleRequested) {
+ if (r.visible) {
hasVisibleActivities = true;
}
final boolean remove;
@@ -3873,8 +3938,8 @@ class ActivityStack extends TaskStack {
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
remove = true;
- } else if (!r.mVisibleRequested && r.launchCount > 2
- && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+ } else if (!r.visible && r.launchCount > 2 &&
+ r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
// We have launched this activity too many times since it was
// able to run, so give up and remove it.
// (Note if the activity is visible, we don't remove the record.
@@ -3910,7 +3975,7 @@ class ActivityStack extends TaskStack {
// it died, we leave the dead window on screen so it's basically visible.
// This is needed when user later tap on the dead window, we need to stop
// other apps when user transfers focus to the restarted activity.
- r.nowVisible = r.mVisibleRequested;
+ r.nowVisible = r.visible;
}
r.cleanUp(true /* cleanServices */, true /* setState */);
if (remove) {
@@ -4092,7 +4157,7 @@ class ActivityStack extends TaskStack {
* Ensures all visible activities at or below the input activity have the right configuration.
*/
void ensureVisibleActivitiesConfigurationLocked(ActivityRecord start, boolean preserveWindow) {
- if (start == null || !start.mVisibleRequested) {
+ if (start == null || !start.visible) {
return;
}
@@ -4487,7 +4552,7 @@ class ActivityStack extends TaskStack {
final ActivityRecord a = task.getChildAt(activityNdx);
if (a.info.packageName.equals(packageName)) {
a.forceNewConfig = true;
- if (starting != null && a == starting && a.mVisibleRequested) {
+ if (starting != null && a == starting && a.visible) {
a.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
}
}
@@ -4586,11 +4651,6 @@ class ActivityStack extends TaskStack {
// We only want to move the parents to the parents if we are creating this task at the
// top of its stack.
addChild(task, toTop ? MAX_VALUE : 0, showForAllUsers, toTop /*moveParents*/);
-
- if (toTop) {
- // TODO(stack-merge): figure-out a way to remove this call.
- positionChildAtTop(task);
- }
}
void positionChildAt(Task task, int position) {
@@ -4604,15 +4664,19 @@ class ActivityStack extends TaskStack {
final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
- // TODO(stack-merge): Can all of these be consolidated to just making the super call in the
- // else?
- if (position >= getChildCount()) {
- positionChildAtTop(task);
- } else if (position <= 0) {
- positionChildAtBottom(task);
- } else {
- super.positionChildAt(task, position);
+ boolean toTop = position >= getChildCount();
+ boolean includingParents = toTop || getDisplay().getNextFocusableStack(this,
+ true /* ignoreCurrent */) == null;
+ if (WindowManagerDebugConfig.DEBUG_STACK) {
+ Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
+ }
+ positionChildAt(position, task, includingParents);
+ task.updateTaskMovement(toTop);
+ if (getDisplayContent().mAppTransition.isTransitionSet()) {
+ task.setSendingToBottom(!toTop);
}
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+
// TODO: Investigate if this random code is really needed.
if (task.voiceSession != null) {
@@ -4694,8 +4758,66 @@ class ActivityStack extends TaskStack {
return;
}
}
- super.animateResizePinnedStack(
- toBounds, sourceHintBounds, animationDuration, fromFullscreen);
+
+ // Get the from-bounds
+ final Rect fromBounds = new Rect();
+ getBounds(fromBounds);
+
+ // Get non-null fullscreen to-bounds for animating if the bounds are null
+ @BoundsAnimationController.SchedulePipModeChangedState int schedulePipModeChangedState =
+ NO_PIP_MODE_CHANGED_CALLBACKS;
+ final boolean toFullscreen = toBounds == null;
+ if (toFullscreen) {
+ if (fromFullscreen) {
+ throw new IllegalArgumentException("Should not defer scheduling PiP mode"
+ + " change on animation to fullscreen.");
+ }
+ schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
+
+ mWmService.getStackBounds(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
+ if (!mTmpToBounds.isEmpty()) {
+ // If there is a fullscreen bounds, use that
+ toBounds = new Rect(mTmpToBounds);
+ } else {
+ // Otherwise, use the display bounds
+ toBounds = new Rect();
+ getDisplayContent().getBounds(toBounds);
+ }
+ } else if (fromFullscreen) {
+ schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
+ }
+
+ setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
+
+ final Rect finalToBounds = toBounds;
+ final @BoundsAnimationController.SchedulePipModeChangedState int
+ finalSchedulePipModeChangedState = schedulePipModeChangedState;
+ final DisplayContent displayContent = getDisplayContent();
+ @BoundsAnimationController.AnimationType int intendedAnimationType =
+ displayContent.mBoundsAnimationController.getAnimationType();
+ if (intendedAnimationType == FADE_IN) {
+ if (fromFullscreen) {
+ setPinnedStackAlpha(0f);
+ }
+ if (toBounds.width() == fromBounds.width()
+ && toBounds.height() == fromBounds.height()) {
+ intendedAnimationType = BoundsAnimationController.BOUNDS;
+ } else if (!fromFullscreen && !toBounds.equals(fromBounds)) {
+ // intendedAnimationType may have been reset at the end of RecentsAnimation,
+ // force it to BOUNDS type if we know for certain we're animating to
+ // a different bounds, especially for expand and collapse of PiP window.
+ intendedAnimationType = BoundsAnimationController.BOUNDS;
+ }
+ }
+
+ final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
+ mCancelCurrentBoundsAnimation = false;
+ displayContent.mBoundsAnimationController.getHandler().post(() -> {
+ displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
+ finalToBounds, animationDuration, finalSchedulePipModeChangedState,
+ fromFullscreen, toFullscreen, animationType);
+ });
}
void dismissPip() {
@@ -4749,6 +4871,1580 @@ class ActivityStack extends TaskStack {
return mStackId;
}
+ Task findHomeTask() {
+ if (!isActivityTypeHome() || mChildren.isEmpty()) {
+ return null;
+ }
+ return mChildren.get(mChildren.size() - 1);
+ }
+
+ void prepareFreezingTaskBounds() {
+ for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = mChildren.get(taskNdx);
+ task.prepareFreezingBounds();
+ }
+ }
+
+ /**
+ * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
+ * the normal task bounds.
+ *
+ * @param bounds The adjusted bounds.
+ */
+ private void setAdjustedBounds(Rect bounds) {
+ if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
+ return;
+ }
+
+ mAdjustedBounds.set(bounds);
+ final boolean adjusted = !mAdjustedBounds.isEmpty();
+ Rect insetBounds = null;
+ if (adjusted && isAdjustedForMinimizedDockedStack()) {
+ insetBounds = getRawBounds();
+ } else if (adjusted && mAdjustedForIme) {
+ if (mImeGoingAway) {
+ insetBounds = getRawBounds();
+ } else {
+ insetBounds = mFullyAdjustedImeBounds;
+ }
+ }
+ alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : getRawBounds(), insetBounds);
+ mDisplayContent.setLayoutNeeded();
+
+ updateSurfaceBounds();
+ }
+
+ private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
+ if (matchParentBounds()) {
+ return;
+ }
+
+ final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
+
+ // Update bounds of containing tasks.
+ for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = mChildren.get(taskNdx);
+ task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
+ }
+ }
+
+ @Override
+ public int setBounds(Rect bounds) {
+ return setBounds(getRequestedOverrideBounds(), bounds);
+ }
+
+ private int setBounds(Rect existing, Rect bounds) {
+ if (equivalentBounds(existing, bounds)) {
+ return BOUNDS_CHANGE_NONE;
+ }
+
+ final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
+
+ updateAdjustedBounds();
+
+ updateSurfaceBounds();
+ return result;
+ }
+
+ /** Bounds of the stack without adjusting for other factors in the system like visibility
+ * of docked stack.
+ * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a
+ * it takes into consideration other system factors. */
+ void getRawBounds(Rect out) {
+ out.set(getRawBounds());
+ }
+
+ private Rect getRawBounds() {
+ return super.getBounds();
+ }
+
+ @Override
+ public void getBounds(Rect bounds) {
+ bounds.set(getBounds());
+ }
+
+ @Override
+ public Rect getBounds() {
+ // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
+ // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
+ // stack is visible since it is already what we want to represent to the rest of the
+ // system.
+ if (!mAdjustedBounds.isEmpty()) {
+ return mAdjustedBounds;
+ } else {
+ return super.getBounds();
+ }
+ }
+
+ /**
+ * Sets the bounds animation target bounds ahead of an animation. This can't currently be done
+ * in onAnimationStart() since that is started on the UiThread.
+ */
+ private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
+ boolean toFullscreen) {
+ if (mAnimationType == BoundsAnimationController.BOUNDS) {
+ mBoundsAnimatingRequested = true;
+ }
+ mBoundsAnimatingToFullscreen = toFullscreen;
+ if (destBounds != null) {
+ mBoundsAnimationTarget.set(destBounds);
+ } else {
+ mBoundsAnimationTarget.setEmpty();
+ }
+ if (sourceHintBounds != null) {
+ mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
+ } else if (!mBoundsAnimating) {
+ // If the bounds are already animating, we don't want to reset the source hint. This is
+ // because the source hint is sent when starting the animation from the client that
+ // requested to enter pip. Other requests can adjust the pip bounds during an animation,
+ // but could accidentally reset the source hint bounds.
+ mBoundsAnimationSourceHintBounds.setEmpty();
+ }
+
+ mPreAnimationBounds.set(getRawBounds());
+ }
+
+ /**
+ * @return the final bounds for the bounds animation.
+ */
+ void getFinalAnimationBounds(Rect outBounds) {
+ outBounds.set(mBoundsAnimationTarget);
+ }
+
+ /**
+ * @return the final source bounds for the bounds animation.
+ */
+ void getFinalAnimationSourceHintBounds(Rect outBounds) {
+ outBounds.set(mBoundsAnimationSourceHintBounds);
+ }
+
+ /**
+ * @return the final animation bounds if the task stack is currently being animated, or the
+ * current stack bounds otherwise.
+ */
+ void getAnimationOrCurrentBounds(Rect outBounds) {
+ if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
+ getFinalAnimationBounds(outBounds);
+ return;
+ }
+ getBounds(outBounds);
+ }
+
+ /** Bounds of the stack with other system factors taken into consideration. */
+ void getDimBounds(Rect out) {
+ getBounds(out);
+ }
+
+ /**
+ * Updates the passed-in {@code inOutBounds} based on the current state of the
+ * pinned controller. This gets run *after* the override configuration is updated, so it's
+ * safe to rely on the controller's state in here (though eventually this dependence should
+ * be removed).
+ *
+ * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
+ * update pinned controller state.
+ *
+ * @param inOutBounds the bounds to update (both input and output).
+ * @return true if bounds were updated to some non-empty value.
+ */
+ boolean calculatePinnedBoundsForConfigChange(Rect inOutBounds) {
+ boolean animating = false;
+ if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
+ animating = true;
+ getFinalAnimationBounds(mTmpRect2);
+ } else {
+ mTmpRect2.set(inOutBounds);
+ }
+ boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
+ mTmpRect2, mTmpRect3);
+ if (updated) {
+ inOutBounds.set(mTmpRect3);
+
+ // The final boundary is updated while there is an existing boundary animation. Let's
+ // cancel this animation to prevent the obsolete animation overwritten updated bounds.
+ if (animating && !inOutBounds.equals(mBoundsAnimationTarget)) {
+ final DisplayContent displayContent = getDisplayContent();
+ displayContent.mBoundsAnimationController.getHandler().post(() ->
+ displayContent.mBoundsAnimationController.cancel(this));
+ }
+ // Once we've set the bounds based on the rotation of the old bounds in the new
+ // orientation, clear the animation target bounds since they are obsolete, and
+ // cancel any currently running animations
+ mBoundsAnimationTarget.setEmpty();
+ mBoundsAnimationSourceHintBounds.setEmpty();
+ mCancelCurrentBoundsAnimation = true;
+ }
+ return updated;
+ }
+
+ /**
+ * Updates the passed-in {@code inOutBounds} based on the current state of the
+ * docked controller. This gets run *after* the override configuration is updated, so it's
+ * safe to rely on the controller's state in here (though eventually this dependence should
+ * be removed).
+ *
+ * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
+ * update docked controller state.
+ *
+ * @param parentConfig the parent configuration for reference.
+ * @param inOutBounds the bounds to update (both input and output).
+ */
+ void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) {
+ final boolean primary =
+ getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds);
+ final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout;
+ snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds);
+ if (primary) {
+ final int newDockSide = getDockSide(parentConfig, inOutBounds);
+ // Update the dock create mode and clear the dock create bounds, these
+ // might change after a rotation and the original values will be invalid.
+ mWmService.setDockedStackCreateStateLocked(
+ (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
+ ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+ : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
+ null);
+ mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+ }
+ }
+
+ /**
+ * Some primary split screen sides are not allowed by the policy. This method queries the policy
+ * and moves the primary stack around if needed.
+ *
+ * @param parentConfig the configuration of the stack's parent.
+ * @param primary true if adjusting the primary docked stack, false for secondary.
+ * @param inOutBounds the bounds of the stack to adjust.
+ */
+ void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary,
+ Rect inOutBounds) {
+ final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds);
+ final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide);
+ final int primaryDockSide = primary ? dockSide : otherDockSide;
+ if (mDisplayContent.getDockedDividerController()
+ .canPrimaryStackDockTo(primaryDockSide,
+ parentConfig.windowConfiguration.getBounds(),
+ parentConfig.windowConfiguration.getRotation())) {
+ return;
+ }
+ final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+ switch (otherDockSide) {
+ case DOCKED_LEFT:
+ int movement = inOutBounds.left;
+ inOutBounds.left -= movement;
+ inOutBounds.right -= movement;
+ break;
+ case DOCKED_RIGHT:
+ movement = parentBounds.right - inOutBounds.right;
+ inOutBounds.left += movement;
+ inOutBounds.right += movement;
+ break;
+ case DOCKED_TOP:
+ movement = inOutBounds.top;
+ inOutBounds.top -= movement;
+ inOutBounds.bottom -= movement;
+ break;
+ case DOCKED_BOTTOM:
+ movement = parentBounds.bottom - inOutBounds.bottom;
+ inOutBounds.top += movement;
+ inOutBounds.bottom += movement;
+ break;
+ }
+ }
+
+ /**
+ * Snaps the bounds after rotation to the closest snap target for the docked stack.
+ */
+ void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout,
+ Rect outBounds) {
+
+ // Calculate the current position.
+ final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
+ final int dockSide = getDockSide(parentConfig, outBounds);
+ final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
+ dockSide, dividerSize);
+ final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
+ final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
+
+ // Snap the position to a target.
+ final int rotation = parentConfig.windowConfiguration.getRotation();
+ final int orientation = parentConfig.orientation;
+ mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
+ displayCutout, outBounds);
+ final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
+ mWmService.mContext.getResources(), displayWidth, displayHeight,
+ dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
+ getDockSide(), isMinimizedDockAndHomeStackResizable());
+ final DividerSnapAlgorithm.SnapTarget target =
+ algorithm.calculateNonDismissingSnapTarget(dividerPosition);
+
+ // Recalculate the bounds based on the position of the target.
+ DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
+ outBounds, displayWidth, displayHeight,
+ dividerSize);
+ }
+
+ /**
+ * Put a Task in this stack. Used for adding only.
+ * When task is added to top of the stack, the entire branch of the hierarchy (including stack
+ * and display) will be brought to top.
+ * @param task The task to add.
+ * @param position Target position to add the task to.
+ * @param showForAllUsers Whether to show the task regardless of the current user.
+ */
+ void addChild(Task task, int position, boolean showForAllUsers, boolean moveParents) {
+ // Add child task.
+ addChild(task, null);
+
+ // Move child to a proper position, as some restriction for position might apply.
+ position = positionChildAt(
+ position, task, moveParents /* includingParents */, showForAllUsers);
+ }
+
+ @Override
+ void addChild(Task task, int position) {
+ addChild(task, position, task.showForAllUsers(), false /* includingParents */);
+ }
+
+ void positionChildAtTop(Task child) {
+ if (child == null) {
+ // TODO: Fix the call-points that cause this to happen.
+ return;
+ }
+
+ positionChildAt(POSITION_TOP, child, true /* includingParents */);
+ child.updateTaskMovement(true);
+
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent.mAppTransition.isTransitionSet()) {
+ child.setSendingToBottom(false);
+ }
+ displayContent.layoutAndAssignWindowLayersIfNeeded();
+ }
+
+ private void positionChildAtBottom(Task child) {
+ // If there are other focusable stacks on the display, the z-order of the display should not
+ // be changed just because a task was placed at the bottom. E.g. if it is moving the topmost
+ // task to bottom, the next focusable stack on the same display should be focused.
+ final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
+ child.getStack(), true /* ignoreCurrent */);
+ positionChildAtBottom(child, nextFocusableStack == null /* includingParents */);
+ child.updateTaskMovement(true);
+ }
+
+ @VisibleForTesting
+ void positionChildAtBottom(Task child, boolean includingParents) {
+ if (child == null) {
+ // TODO: Fix the call-points that cause this to happen.
+ return;
+ }
+
+ positionChildAt(POSITION_BOTTOM, child, includingParents);
+
+ if (getDisplayContent().mAppTransition.isTransitionSet()) {
+ child.setSendingToBottom(true);
+ }
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+
+ @Override
+ void positionChildAt(int position, Task child, boolean includingParents) {
+ positionChildAt(position, child, includingParents, child.showForAllUsers());
+ }
+
+ /**
+ * Overridden version of {@link ActivityStack#positionChildAt(int, Task, boolean)}. Used in
+ * {@link ActivityStack#addChild(Task, int, boolean showForAllUsers, boolean)}, as it can
+ * receive showForAllUsers param from {@link ActivityRecord} instead of
+ * {@link Task#showForAllUsers()}.
+ */
+ private int positionChildAt(int position, Task child, boolean includingParents,
+ boolean showForAllUsers) {
+ final int targetPosition = findPositionForTask(child, position, showForAllUsers);
+ super.positionChildAt(targetPosition, child, includingParents);
+
+ // Log positioning.
+ if (DEBUG_TASK_MOVEMENT) {
+ Slog.d(TAG_WM, "positionTask: task=" + this + " position=" + position);
+ }
+
+ final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0;
+ EventLog.writeEvent(com.android.server.EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop,
+ targetPosition);
+
+ return targetPosition;
+ }
+
+ @Override
+ void onChildPositionChanged(WindowContainer child) {
+ if (!mChildren.contains(child)) {
+ return;
+ }
+
+ final Task task = (Task) child;
+ final boolean isTop = getTopChild() == task;
+ task.updateTaskMovement(isTop);
+ if (isTop) {
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent.mAppTransition.isTransitionSet()) {
+ task.setSendingToBottom(false);
+ }
+ displayContent.layoutAndAssignWindowLayersIfNeeded();
+ }
+ }
+
+ // TODO(display-unify): Remove after display unification.
+ protected void onParentChanged(ActivityDisplay newParent, ActivityDisplay oldParent) {
+ onParentChanged(
+ newParent != null ? newParent.mDisplayContent : null,
+ oldParent != null ? oldParent.mDisplayContent : null);
+ }
+
+ @Override
+ protected void onParentChanged(
+ ConfigurationContainer newParent, ConfigurationContainer oldParent) {
+ final ActivityDisplay display = newParent != null
+ ? ((WindowContainer) newParent).getDisplayContent().mActivityDisplay : null;
+ final ActivityDisplay oldDisplay = oldParent != null
+ ? ((WindowContainer) oldParent).getDisplayContent().mActivityDisplay : null;
+
+ mDisplayId = (display != null) ? display.mDisplayId : INVALID_DISPLAY;
+ mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
+
+ if (display != null) {
+ // Rotations are relative to the display. This means if there are 2 displays rotated
+ // differently (eg. 2 monitors with one landscape and one portrait), moving a stack
+ // from one to the other could look like a rotation change. To prevent this
+ // apparent rotation change (and corresponding bounds rotation), pretend like our
+ // current rotation is already the same as the new display.
+ // Note, if ActivityStack or related logic ever gets nested, this logic will need
+ // to move to onConfigurationChanged.
+ getConfiguration().windowConfiguration.setRotation(
+ display.getWindowConfiguration().getRotation());
+ }
+ super.onParentChanged(newParent, oldParent);
+ if (getParent() == null && mDisplayContent != null) {
+ EventLog.writeEvent(com.android.server.EventLogTags.WM_STACK_REMOVED, mStackId);
+
+ mDisplayContent = null;
+ mWmService.mWindowPlacerLocked.requestTraversal();
+ }
+ if (display != null && inSplitScreenPrimaryWindowingMode()) {
+ // If we created a docked stack we want to resize it so it resizes all other stacks
+ // in the system.
+ getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
+ mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
+ mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
+ mTmpRect2, null, null, PRESERVE_WINDOWS);
+ }
+ mRootActivityContainer.updateUIDsPresentOnDisplay();
+
+ // Resume next focusable stack after reparenting to another display if we aren't removing
+ // the prevous display.
+ if (oldDisplay != null && oldDisplay.isRemoving()) {
+ postReparent();
+ }
+ }
+
+ void reparent(DisplayContent newParent, boolean onTop) {
+ // Real parent of stack is within display object, so we have to delegate re-parenting there.
+ newParent.moveStackToDisplay(this, onTop);
+ }
+
+ // TODO: We should really have users as a window container in the hierarchy so that we don't
+ // have to do complicated things like we are doing in this method.
+ int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers) {
+ final boolean canShowTask =
+ showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
+
+ final int stackSize = mChildren.size();
+ int minPosition = 0;
+ int maxPosition = stackSize - 1;
+
+ if (canShowTask) {
+ minPosition = computeMinPosition(minPosition, stackSize);
+ } else {
+ maxPosition = computeMaxPosition(maxPosition);
+ }
+
+ // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
+ if (targetPosition == POSITION_BOTTOM && minPosition == 0) {
+ return POSITION_BOTTOM;
+ } else if (targetPosition == POSITION_TOP && maxPosition == (stackSize - 1)) {
+ return POSITION_TOP;
+ }
+ // Reset position based on minimum/maximum possible positions.
+ return Math.min(Math.max(targetPosition, minPosition), maxPosition);
+ }
+
+ /** Calculate the minimum possible position for a task that can be shown to the user.
+ * The minimum position will be above all other tasks that can't be shown.
+ * @param minPosition The minimum position the caller is suggesting.
+ * We will start adjusting up from here.
+ * @param size The size of the current task list.
+ */
+ private int computeMinPosition(int minPosition, int size) {
+ while (minPosition < size) {
+ final Task tmpTask = mChildren.get(minPosition);
+ final boolean canShowTmpTask =
+ tmpTask.showForAllUsers()
+ || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
+ if (canShowTmpTask) {
+ break;
+ }
+ minPosition++;
+ }
+ return minPosition;
+ }
+
+ /** Calculate the maximum possible position for a task that can't be shown to the user.
+ * The maximum position will be below all other tasks that can be shown.
+ * @param maxPosition The maximum position the caller is suggesting.
+ * We will start adjusting down from here.
+ */
+ private int computeMaxPosition(int maxPosition) {
+ while (maxPosition > 0) {
+ final Task tmpTask = mChildren.get(maxPosition);
+ final boolean canShowTmpTask =
+ tmpTask.showForAllUsers()
+ || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
+ if (!canShowTmpTask) {
+ break;
+ }
+ maxPosition--;
+ }
+ return maxPosition;
+ }
+
+ private void updateSurfaceBounds() {
+ updateSurfaceSize(getPendingTransaction());
+ updateSurfacePosition();
+ scheduleAnimation();
+ }
+
+ /**
+ * Calculate an amount by which to expand the stack bounds in each direction.
+ * Used to make room for shadows in the pinned windowing mode.
+ */
+ int getStackOutset() {
+ DisplayContent displayContent = getDisplayContent();
+ if (inPinnedWindowingMode() && displayContent != null) {
+ final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
+
+ // We multiply by two to match the client logic for converting view elevation
+ // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
+ return (int) Math.ceil(
+ mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, displayMetrics)
+ * 2);
+ }
+ return 0;
+ }
+
+ @Override
+ void getRelativeDisplayedPosition(Point outPos) {
+ super.getRelativeDisplayedPosition(outPos);
+ final int outset = getStackOutset();
+ outPos.x -= outset;
+ outPos.y -= outset;
+ }
+
+ private void updateSurfaceSize(SurfaceControl.Transaction transaction) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ final Rect stackBounds = getDisplayedBounds();
+ int width = stackBounds.width();
+ int height = stackBounds.height();
+
+ final int outset = getStackOutset();
+ width += 2 * outset;
+ height += 2 * outset;
+
+ if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
+ return;
+ }
+ transaction.setWindowCrop(mSurfaceControl, width, height);
+ mLastSurfaceSize.set(width, height);
+ }
+
+ @VisibleForTesting
+ Point getLastSurfaceSize() {
+ return mLastSurfaceSize;
+ }
+
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ super.onDisplayChanged(dc);
+ updateSurfaceBounds();
+ }
+
+ /**
+ * Determines the stack and task bounds of the other stack when in docked mode. The current task
+ * bounds is passed in but depending on the stack, the task and stack must match. Only in
+ * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds
+ * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds
+ * is calculated and is also used for its task bounds.
+ * If any of the out bounds are empty, it represents default bounds
+ *
+ * @param currentTempTaskBounds the current task bounds of the other stack
+ * @param outStackBounds the calculated stack bounds of the other stack
+ * @param outTempTaskBounds the calculated task bounds of the other stack
+ */
+ void getStackDockedModeBounds(Rect dockedBounds,
+ Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) {
+ final Configuration parentConfig = getParent().getConfiguration();
+ outTempTaskBounds.setEmpty();
+
+ if (dockedBounds == null || dockedBounds.isEmpty()) {
+ // Calculate the primary docked bounds.
+ final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
+ == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+ getStackDockedModeBounds(parentConfig,
+ true /* primary */, outStackBounds, dockedBounds,
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
+ return;
+ }
+ final int dockedSide = getDockSide(parentConfig, dockedBounds);
+
+ // When the home stack is resizable, should always have the same stack and task bounds
+ if (isActivityTypeHome()) {
+ final Task homeTask = findHomeTask();
+ if (homeTask == null || homeTask.isResizeable()) {
+ // Calculate the home stack bounds when in docked mode and the home stack is
+ // resizeable.
+ getDisplayContent().mDividerControllerLocked
+ .getHomeStackBoundsInDockedMode(parentConfig,
+ dockedSide, outStackBounds);
+ } else {
+ // Home stack isn't resizeable, so don't specify stack bounds.
+ outStackBounds.setEmpty();
+ }
+
+ outTempTaskBounds.set(outStackBounds);
+ return;
+ }
+
+ // When minimized state, the stack bounds for all non-home and docked stack bounds should
+ // match the passed task bounds
+ if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) {
+ outStackBounds.set(currentTempTaskBounds);
+ return;
+ }
+
+ if (dockedSide == DOCKED_INVALID) {
+ // Not sure how you got here...Only thing we can do is return current bounds.
+ Slog.e(TAG_WM, "Failed to get valid docked side for docked stack");
+ outStackBounds.set(getRawBounds());
+ return;
+ }
+
+ final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
+ getStackDockedModeBounds(parentConfig,
+ false /* primary */, outStackBounds, dockedBounds,
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
+ }
+
+ /**
+ * Outputs the bounds a stack should be given the presence of a docked stack on the display.
+ * @param parentConfig The parent configuration.
+ * @param primary {@code true} if getting the primary stack bounds.
+ * @param outBounds Output bounds that should be used for the stack.
+ * @param dockedBounds Bounds of the docked stack.
+ * @param dockDividerWidth We need to know the width of the divider make to the output bounds
+ * close to the side of the dock.
+ * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
+ */
+ private void getStackDockedModeBounds(Configuration parentConfig, boolean primary,
+ Rect outBounds, Rect dockedBounds, int dockDividerWidth,
+ boolean dockOnTopOrLeft) {
+ final Rect displayRect = parentConfig.windowConfiguration.getBounds();
+ final boolean splitHorizontally = displayRect.width() > displayRect.height();
+
+ outBounds.set(displayRect);
+ if (primary) {
+ if (mWmService.mDockedStackCreateBounds != null) {
+ outBounds.set(mWmService.mDockedStackCreateBounds);
+ return;
+ }
+
+ // The initial bounds of the docked stack when it is created about half the screen space
+ // and its bounds can be adjusted after that. The bounds of all other stacks are
+ // adjusted to occupy whatever screen space the docked stack isn't occupying.
+ final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
+ mDisplayContent.getDisplayPolicy().getStableInsetsLw(
+ parentConfig.windowConfiguration.getRotation(),
+ displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
+ final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
+ displayRect.width(),
+ displayRect.height(),
+ dockDividerWidth,
+ parentConfig.orientation == ORIENTATION_PORTRAIT,
+ mTmpRect2).getMiddleTarget().position;
+
+ if (dockOnTopOrLeft) {
+ if (splitHorizontally) {
+ outBounds.right = position;
+ } else {
+ outBounds.bottom = position;
+ }
+ } else {
+ if (splitHorizontally) {
+ outBounds.left = position + dockDividerWidth;
+ } else {
+ outBounds.top = position + dockDividerWidth;
+ }
+ }
+ return;
+ }
+
+ // Other stacks occupy whatever space is left by the docked stack.
+ if (!dockOnTopOrLeft) {
+ if (splitHorizontally) {
+ outBounds.right = dockedBounds.left - dockDividerWidth;
+ } else {
+ outBounds.bottom = dockedBounds.top - dockDividerWidth;
+ }
+ } else {
+ if (splitHorizontally) {
+ outBounds.left = dockedBounds.right + dockDividerWidth;
+ } else {
+ outBounds.top = dockedBounds.bottom + dockDividerWidth;
+ }
+ }
+ DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
+ }
+
+ void resetDockedStackToMiddle() {
+ if (!inSplitScreenPrimaryWindowingMode()) {
+ throw new IllegalStateException("Not a docked stack=" + this);
+ }
+
+ mWmService.mDockedStackCreateBounds = null;
+
+ final Rect bounds = new Rect();
+ final Rect tempBounds = new Rect();
+ getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
+ bounds, tempBounds);
+ mStackSupervisor.resizeDockedStackLocked(bounds, null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, null /* tempOtherTaskBounds */,
+ null /* tempOtherTaskInsetBounds */, false /* preserveWindows */,
+ false /* deferResume */);
+ }
+
+ @Override
+ void removeIfPossible() {
+ if (isAnimating(TRANSITION | CHILDREN)) {
+ mDeferRemoval = true;
+ return;
+ }
+ removeImmediately();
+ }
+
+ /**
+ * Adjusts the stack bounds if the IME is visible.
+ *
+ * @param imeWin The IME window.
+ * @param keepLastAmount Use {@code true} to keep the last adjusted amount from
+ * {@link DockedStackDividerController} for adjusting the stack bounds,
+ * Use {@code false} to reset adjusted amount as 0.
+ * @see #updateAdjustForIme(float, float, boolean)
+ */
+ void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) {
+ mImeWin = imeWin;
+ mImeGoingAway = false;
+ if (!mAdjustedForIme || keepLastAmount) {
+ mAdjustedForIme = true;
+ DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked;
+ final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f;
+ final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f;
+ updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */);
+ }
+ }
+
+ boolean isAdjustedForIme() {
+ return mAdjustedForIme;
+ }
+
+ boolean isAnimatingForIme() {
+ return mImeWin != null && mImeWin.isAnimatingLw();
+ }
+
+ /**
+ * Update the stack's bounds (crop or position) according to the IME window's
+ * current position. When IME window is animated, the bottom stack is animated
+ * together to track the IME window's current position, and the top stack is
+ * cropped as necessary.
+ *
+ * @return true if a traversal should be performed after the adjustment.
+ */
+ boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) {
+ if (adjustAmount != mAdjustImeAmount
+ || adjustDividerAmount != mAdjustDividerAmount || force) {
+ mAdjustImeAmount = adjustAmount;
+ mAdjustDividerAmount = adjustDividerAmount;
+ updateAdjustedBounds();
+ return isVisible();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Resets the adjustment after it got adjusted for the IME.
+ * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
+ * animations; otherwise, set flag and animates the window away together
+ * with IME window.
+ */
+ void resetAdjustedForIme(boolean adjustBoundsNow) {
+ if (adjustBoundsNow) {
+ mImeWin = null;
+ mImeGoingAway = false;
+ mAdjustImeAmount = 0f;
+ mAdjustDividerAmount = 0f;
+ if (!mAdjustedForIme) {
+ return;
+ }
+ mAdjustedForIme = false;
+ updateAdjustedBounds();
+ mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
+ } else {
+ mImeGoingAway |= mAdjustedForIme;
+ }
+ }
+
+ /**
+ * Sets the amount how much we currently minimize our stack.
+ *
+ * @param minimizeAmount The amount, between 0 and 1.
+ * @return Whether the amount has changed and a layout is needed.
+ */
+ boolean setAdjustedForMinimizedDock(float minimizeAmount) {
+ if (minimizeAmount != mMinimizeAmount) {
+ mMinimizeAmount = minimizeAmount;
+ updateAdjustedBounds();
+ return isVisible();
+ } else {
+ return false;
+ }
+ }
+
+ boolean shouldIgnoreInput() {
+ return isAdjustedForMinimizedDockedStack()
+ || (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
+ }
+
+ /**
+ * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
+ * to the list of to be drawn windows the service is waiting for.
+ */
+ void beginImeAdjustAnimation() {
+ for (int j = mChildren.size() - 1; j >= 0; j--) {
+ final Task task = mChildren.get(j);
+ if (task.hasContentToDisplay()) {
+ task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+ task.setWaitingForDrawnIfResizingChanged();
+ }
+ }
+ }
+
+ /**
+ * Resets the resizing state of all windows.
+ */
+ void endImeAdjustAnimation() {
+ for (int j = mChildren.size() - 1; j >= 0; j--) {
+ mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+ }
+ }
+
+ int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
+ return displayContentRect.top + (int)
+ ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+ }
+
+ private boolean adjustForIME(final WindowState imeWin) {
+ // To prevent task stack resize animation may flicking when playing app transition
+ // animation & IME window enter animation in parallel, we need to make sure app
+ // transition is done and then adjust task size for IME, skip the new adjusted frame when
+ // app transition is still running.
+ if (getDisplayContent().mAppTransition.isRunning()) {
+ return false;
+ }
+
+ final int dockedSide = getDockSide();
+ final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
+ if (imeWin == null || !dockedTopOrBottom) {
+ return false;
+ }
+
+ final Rect displayStableRect = mTmpRect;
+ final Rect contentBounds = mTmpRect2;
+
+ // Calculate the content bounds excluding the area occupied by IME
+ getDisplayContent().getStableRect(displayStableRect);
+ contentBounds.set(displayStableRect);
+ int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
+
+ imeTop += imeWin.getGivenContentInsetsLw().top;
+ if (contentBounds.bottom > imeTop) {
+ contentBounds.bottom = imeTop;
+ }
+
+ final int yOffset = displayStableRect.bottom - contentBounds.bottom;
+
+ final int dividerWidth =
+ getDisplayContent().mDividerControllerLocked.getContentWidth();
+ final int dividerWidthInactive =
+ getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
+
+ if (dockedSide == DOCKED_TOP) {
+ // If this stack is docked on top, we make it smaller so the bottom stack is not
+ // occluded by IME. We shift its bottom up by the height of the IME, but
+ // leaves at least 30% of the top stack visible.
+ final int minTopStackBottom =
+ getMinTopStackBottom(displayStableRect, getRawBounds().bottom);
+ final int bottom = Math.max(
+ getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive,
+ minTopStackBottom);
+ mTmpAdjustedBounds.set(getRawBounds());
+ mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount)
+ * getRawBounds().bottom);
+ mFullyAdjustedImeBounds.set(getRawBounds());
+ } else {
+ // When the stack is on bottom and has no focus, it's only adjusted for divider width.
+ final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
+
+ // When the stack is on bottom and has focus, it needs to be moved up so as to
+ // not occluded by IME, and at the same time adjusted for divider width.
+ // We try to move it up by the height of the IME window, but only to the extent
+ // that leaves at least 30% of the top stack visible.
+ // 'top' is where the top of bottom stack will move to in this case.
+ final int topBeforeImeAdjust =
+ getRawBounds().top - dividerWidth + dividerWidthInactive;
+ final int minTopStackBottom =
+ getMinTopStackBottom(displayStableRect,
+ getRawBounds().top - dividerWidth);
+ final int top = Math.max(
+ getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive);
+
+ mTmpAdjustedBounds.set(getRawBounds());
+ // Account for the adjustment for IME and divider width separately.
+ // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
+ // and dividerWidthDelta is due to divider width change only.
+ mTmpAdjustedBounds.top =
+ getRawBounds().top + (int) (mAdjustImeAmount * (top - topBeforeImeAdjust)
+ + mAdjustDividerAmount * dividerWidthDelta);
+ mFullyAdjustedImeBounds.set(getRawBounds());
+ mFullyAdjustedImeBounds.top = top;
+ mFullyAdjustedImeBounds.bottom = top + getRawBounds().height();
+ }
+ return true;
+ }
+
+ private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
+ final int dockSide = getDockSide();
+ if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
+ return false;
+ }
+
+ if (dockSide == DOCKED_TOP) {
+ mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+ int topInset = mTmpRect.top;
+ mTmpAdjustedBounds.set(getRawBounds());
+ mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
+ * getRawBounds().bottom);
+ } else if (dockSide == DOCKED_LEFT) {
+ mTmpAdjustedBounds.set(getRawBounds());
+ final int width = getRawBounds().width();
+ mTmpAdjustedBounds.right =
+ (int) (minimizeAmount * mDockedStackMinimizeThickness
+ + (1 - minimizeAmount) * getRawBounds().right);
+ mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
+ } else if (dockSide == DOCKED_RIGHT) {
+ mTmpAdjustedBounds.set(getRawBounds());
+ mTmpAdjustedBounds.left =
+ (int) (minimizeAmount * (getRawBounds().right - mDockedStackMinimizeThickness)
+ + (1 - minimizeAmount) * getRawBounds().left);
+ }
+ return true;
+ }
+
+ private boolean isMinimizedDockAndHomeStackResizable() {
+ return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
+ && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
+ }
+
+ /**
+ * @return the distance in pixels how much the stack gets minimized from it's original size
+ */
+ int getMinimizeDistance() {
+ final int dockSide = getDockSide();
+ if (dockSide == DOCKED_INVALID) {
+ return 0;
+ }
+
+ if (dockSide == DOCKED_TOP) {
+ mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+ int topInset = mTmpRect.top;
+ return getRawBounds().bottom - topInset;
+ } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
+ return getRawBounds().width() - mDockedStackMinimizeThickness;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ private void updateAdjustedBounds() {
+ boolean adjust = false;
+ if (mMinimizeAmount != 0f) {
+ adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
+ } else if (mAdjustedForIme) {
+ adjust = adjustForIME(mImeWin);
+ }
+ if (!adjust) {
+ mTmpAdjustedBounds.setEmpty();
+ }
+ setAdjustedBounds(mTmpAdjustedBounds);
+
+ final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
+ if (mAdjustedForIme && adjust && !isImeTarget) {
+ final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
+ * IME_ADJUST_DIM_AMOUNT;
+ mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
+ }
+ }
+
+ void applyAdjustForImeIfNeeded(Task task) {
+ if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) {
+ return;
+ }
+
+ final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds;
+ task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
+ mDisplayContent.setLayoutNeeded();
+ }
+
+
+ boolean isAdjustedForMinimizedDockedStack() {
+ return mMinimizeAmount != 0f;
+ }
+
+ /**
+ * @return {@code true} if we have a {@link Task} that is animating (currently only used for the
+ * recents animation); {@code false} otherwise.
+ */
+ boolean isTaskAnimating() {
+ for (int j = mChildren.size() - 1; j >= 0; j--) {
+ final Task task = mChildren.get(j);
+ if (task.isTaskAnimating()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ pw.println(prefix + "mStackId=" + mStackId);
+ pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
+ pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
+ if (mMinimizeAmount != 0f) {
+ pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
+ }
+ if (mAdjustedForIme) {
+ pw.println(prefix + "mAdjustedForIme=true");
+ pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
+ pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
+ }
+ if (!mAdjustedBounds.isEmpty()) {
+ pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
+ }
+ for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
+ mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
+ }
+ if (!mExitingActivities.isEmpty()) {
+ pw.println();
+ pw.println(" Exiting application tokens:");
+ for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
+ WindowToken token = mExitingActivities.get(i);
+ pw.print(" Exiting App #"); pw.print(i);
+ pw.print(' '); pw.print(token);
+ pw.println(':');
+ token.dump(pw, " ", dumpAll);
+ }
+ }
+ mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
+ }
+
+ @Override
+ boolean fillsParent() {
+ return matchParentBounds();
+ }
+
+ String getName() {
+ return toShortString();
+ }
+
+ public String toShortString() {
+ return "Stack=" + mStackId;
+ }
+
+ /**
+ * For docked workspace (or workspace that's side-by-side to the docked), provides
+ * information which side of the screen was the dock anchored.
+ */
+ int getDockSide() {
+ return getDockSide(mDisplayContent.getConfiguration(), getRawBounds());
+ }
+
+ int getDockSideForDisplay(DisplayContent dc) {
+ return getDockSide(dc, dc.getConfiguration(), getRawBounds());
+ }
+
+ int getDockSide(Configuration parentConfig, Rect bounds) {
+ if (mDisplayContent == null) {
+ return DOCKED_INVALID;
+ }
+ return getDockSide(mDisplayContent, parentConfig, bounds);
+ }
+
+ private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) {
+ return dc.getDockedDividerController().getDockSide(bounds,
+ parentConfig.windowConfiguration.getBounds(),
+ parentConfig.orientation, parentConfig.windowConfiguration.getRotation());
+ }
+
+ boolean hasTaskForUser(int userId) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final Task task = mChildren.get(i);
+ if (task.mUserId == userId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void findTaskForResizePoint(int x, int y, int delta,
+ DisplayContent.TaskForResizePointSearchResult results) {
+ if (!getWindowConfiguration().canResizeTask()) {
+ results.searchDone = true;
+ return;
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final Task task = mChildren.get(i);
+ if (task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ results.searchDone = true;
+ return;
+ }
+
+ // We need to use the task's dim bounds (which is derived from the visible bounds of
+ // its apps windows) for any touch-related tests. Can't use the task's original
+ // bounds because it might be adjusted to fit the content frame. One example is when
+ // the task is put to top-left quadrant, the actual visible area would not start at
+ // (0,0) after it's adjusted for the status bar.
+ task.getDimBounds(mTmpRect);
+ mTmpRect.inset(-delta, -delta);
+ if (mTmpRect.contains(x, y)) {
+ mTmpRect.inset(delta, delta);
+
+ results.searchDone = true;
+
+ if (!mTmpRect.contains(x, y)) {
+ results.taskForResize = task;
+ return;
+ }
+ // User touched inside the task. No need to look further,
+ // focus transfer will be handled in ACTION_UP.
+ return;
+ }
+ }
+ }
+
+ void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion,
+ Rect contentRect, Rect postExclude) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final Task task = mChildren.get(i);
+ ActivityRecord topVisibleActivity = task.getTopVisibleActivity();
+ if (topVisibleActivity == null || !topVisibleActivity.hasContentToDisplay()) {
+ continue;
+ }
+
+ /**
+ * Exclusion region is the region that TapDetector doesn't care about.
+ * Here we want to remove all non-focused tasks from the exclusion region.
+ * We also remove the outside touch area for resizing for all freeform
+ * tasks (including the focused).
+ *
+ * We save the focused task region once we find it, and add it back at the end.
+ *
+ * If the task is home stack and it is resizable in the minimized state, we want to
+ * exclude the docked stack from touch so we need the entire screen area and not just a
+ * small portion which the home stack currently is resized to.
+ */
+
+ if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) {
+ mDisplayContent.getBounds(mTmpRect);
+ } else {
+ task.getDimBounds(mTmpRect);
+ }
+
+ if (task == focusedTask) {
+ // Add the focused task rect back into the exclude region once we are done
+ // processing stacks.
+ postExclude.set(mTmpRect);
+ }
+
+ final boolean isFreeformed = task.inFreeformWindowingMode();
+ if (task != focusedTask || isFreeformed) {
+ if (isFreeformed) {
+ // If the task is freeformed, enlarge the area to account for outside
+ // touch area for resize.
+ mTmpRect.inset(-delta, -delta);
+ // Intersect with display content rect. If we have system decor (status bar/
+ // navigation bar), we want to exclude that from the tap detection.
+ // Otherwise, if the app is partially placed under some system button (eg.
+ // Recents, Home), pressing that button would cause a full series of
+ // unwanted transfer focus/resume/pause, before we could go home.
+ mTmpRect.intersect(contentRect);
+ }
+ touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+ }
+ }
+ }
+
+ public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
+ // Hold the lock since this is called from the BoundsAnimator running on the UiThread
+ synchronized (mWmService.mGlobalLock) {
+ if (mCancelCurrentBoundsAnimation) {
+ return false;
+ }
+ }
+
+ try {
+ mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
+ } catch (RemoteException e) {
+ // I don't believe you.
+ }
+ return true;
+ }
+
+ void onAllWindowsDrawn() {
+ if (!mBoundsAnimating && !mBoundsAnimatingRequested) {
+ return;
+ }
+
+ getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn();
+ }
+
+ @Override // AnimatesBounds
+ public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
+ @BoundsAnimationController.AnimationType int animationType) {
+ // Hold the lock since this is called from the BoundsAnimator running on the UiThread
+ synchronized (mWmService.mGlobalLock) {
+ if (!isAttached()) {
+ // Don't run the animation if the stack is already detached
+ return false;
+ }
+
+ if (animationType == BoundsAnimationController.BOUNDS) {
+ mBoundsAnimatingRequested = false;
+ mBoundsAnimating = true;
+ }
+ mAnimationType = animationType;
+
+ // If we are changing UI mode, as in the PiP to fullscreen
+ // transition, then we need to wait for the window to draw.
+ if (schedulePipModeChangedCallback) {
+ forAllWindows((w) -> {
+ w.mWinAnimator.resetDrawState();
+ }, false /* traverseTopToBottom */);
+ }
+ }
+
+ if (inPinnedWindowingMode()) {
+ try {
+ mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
+ } catch (RemoteException e) {
+ // I don't believe you...
+ }
+
+ if ((schedulePipModeChangedCallback || animationType == FADE_IN)) {
+ // We need to schedule the PiP mode change before the animation up. It is possible
+ // in this case for the animation down to not have been completed, so always
+ // force-schedule and update to the client to ensure that it is notified that it
+ // is no longer in picture-in-picture mode
+ updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
+ }
+ }
+ return true;
+ }
+
+ @Override // AnimatesBounds
+ public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
+ boolean moveToFullscreen) {
+ synchronized (mWmService.mGlobalLock) {
+ if (inPinnedWindowingMode()) {
+ // Update to the final bounds if requested. This is done here instead of in the
+ // bounds animator to allow us to coordinate this after we notify the PiP mode
+ // changed
+
+ if (schedulePipModeChangedCallback) {
+ // We need to schedule the PiP mode change after the animation down, so use the
+ // final bounds
+ updatePictureInPictureModeForPinnedStackAnimation(mBoundsAnimationTarget,
+ false /* forceUpdate */);
+ }
+
+ if (mAnimationType == BoundsAnimationController.FADE_IN) {
+ setPinnedStackAlpha(1f);
+ mWmService.mAtmService.notifyPinnedStackAnimationEnded();
+ return;
+ }
+
+ if (finalStackSize != null && !mCancelCurrentBoundsAnimation) {
+ setPinnedStackSize(finalStackSize, null);
+ } else {
+ // We have been canceled, so the final stack size is null, still run the
+ // animation-end logic
+ onPipAnimationEndResize();
+ }
+
+ mWmService.mAtmService.notifyPinnedStackAnimationEnded();
+ if (moveToFullscreen) {
+ ((ActivityStack) this).dismissPip();
+ }
+ } else {
+ // No PiP animation, just run the normal animation-end logic
+ onPipAnimationEndResize();
+ }
+ }
+ }
+
+ /**
+ * Sets the current picture-in-picture aspect ratio.
+ */
+ void setPictureInPictureAspectRatio(float aspectRatio) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
+ return;
+ }
+
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+
+ if (!inPinnedWindowingMode()) {
+ return;
+ }
+
+ final PinnedStackController pinnedStackController =
+ getDisplayContent().getPinnedStackController();
+
+ if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
+ return;
+ }
+
+ // Notify the pinned stack controller about aspect ratio change.
+ // This would result a callback delivered from SystemUI to WM to start animation,
+ // if the bounds are ought to be altered due to aspect ratio change.
+ pinnedStackController.setAspectRatio(
+ pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+ ? aspectRatio : -1f);
+ }
+
+ /**
+ * Sets the current picture-in-picture actions.
+ */
+ void setPictureInPictureActions(List<RemoteAction> actions) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
+ return;
+ }
+
+ if (!inPinnedWindowingMode()) {
+ return;
+ }
+
+ getDisplayContent().getPinnedStackController().setActions(actions);
+ }
+
+ /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
+ void onPipAnimationEndResize() {
+ mBoundsAnimating = false;
+ for (int i = 0; i < mChildren.size(); i++) {
+ final Task t = mChildren.get(i);
+ t.clearPreserveNonFloatingState();
+ }
+ mWmService.requestTraversal();
+ }
+
+ @Override
+ public boolean shouldDeferStartOnMoveToFullscreen() {
+ synchronized (mWmService.mGlobalLock) {
+ if (!isAttached()) {
+ // Unnecessary to pause the animation because the stack is detached.
+ return false;
+ }
+
+ // Workaround for the recents animation -- normally we need to wait for the new activity
+ // to show before starting the PiP animation, but because we start and show the home
+ // activity early for the recents animation prior to the PiP animation starting, there
+ // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
+ // stack is already visible and drawn.
+ final ActivityStack homeStack = mDisplayContent.getHomeStack();
+ if (homeStack == null) {
+ return true;
+ }
+ final Task homeTask = homeStack.getTopChild();
+ if (homeTask == null) {
+ return true;
+ }
+ final ActivityRecord homeApp = homeTask.getTopVisibleActivity();
+ if (!homeTask.isVisible() || homeApp == null) {
+ return true;
+ }
+ return !homeApp.allDrawn;
+ }
+ }
+
+ /**
+ * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
+ * bounds and we have a deferred PiP mode changed callback set with the animation.
+ */
+ public boolean deferScheduleMultiWindowModeChanged() {
+ if (inPinnedWindowingMode()) {
+ // For the pinned stack, the deferring of the multi-window mode changed is tied to the
+ // transition animation into picture-in-picture, and is called once the animation
+ // completes, or is interrupted in a way that would leave the stack in a non-fullscreen
+ // state.
+ // @see BoundsAnimationController
+ // @see BoundsAnimationControllerTests
+ return (mBoundsAnimatingRequested || mBoundsAnimating);
+ }
+ return false;
+ }
+
+ public boolean isForceScaled() {
+ return mBoundsAnimating;
+ }
+
+ public boolean isAnimatingBounds() {
+ return mBoundsAnimating;
+ }
+
+ public boolean lastAnimatingBoundsWasToFullscreen() {
+ return mBoundsAnimatingToFullscreen;
+ }
+
+ public boolean isAnimatingBoundsToFullscreen() {
+ return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen();
+ }
+
+ public boolean pinnedStackResizeDisallowed() {
+ if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
+ return true;
+ }
+ return false;
+ }
+
+ /** Returns true if a removal action is still being deferred. */
+ boolean checkCompleteDeferredRemoval() {
+ if (isAnimating(TRANSITION | CHILDREN)) {
+ return true;
+ }
+ if (mDeferRemoval) {
+ removeImmediately();
+ }
+
+ return super.checkCompleteDeferredRemoval();
+ }
+
+ @Override
+ int getOrientation() {
+ return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
+ }
+
+ private boolean canSpecifyOrientation() {
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_RECENTS
+ || activityType == ACTIVITY_TYPE_ASSISTANT;
+ }
+
+ @Override
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getDimBounds(mTmpDimBoundsRect);
+
+ // Bounds need to be relative, as the dim layer is a child.
+ mTmpDimBoundsRect.offsetTo(0, 0);
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
+
+ @Override
+ public boolean setPinnedStackAlpha(float alpha) {
+ // Hold the lock since this is called from the BoundsAnimator running on the UiThread
+ synchronized (mWmService.mGlobalLock) {
+ final SurfaceControl sc = getSurfaceControl();
+ if (sc == null || !sc.isValid()) {
+ // If the stack is already removed, don't bother updating any stack animation
+ return false;
+ }
+ getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
+ scheduleAnimation();
+ return !mCancelCurrentBoundsAnimation;
+ }
+ }
+
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayContent.getDisplayInfo();
+ }
+
+ void dim(float alpha) {
+ mDimmer.dimAbove(getPendingTransaction(), alpha);
+ scheduleAnimation();
+ }
+
+ void stopDimming() {
+ mDimmer.stopDim(getPendingTransaction());
+ scheduleAnimation();
+ }
+
+ AnimatingActivityRegistry getAnimatingActivityRegistry() {
+ return mAnimatingActivityRegistry;
+ }
+
+ @Override
+ void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
+ Rect outSurfaceInsets) {
+ final Task task = getTopChild();
+ if (task != null) {
+ task.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
+ } else {
+ super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
+ }
+ }
+
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final Task task = getTopChild();
+ return task != null ? task.createRemoteAnimationTarget(record) : null;
+ }
+
@Override
public String toString() {
return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
@@ -4792,10 +6488,10 @@ class ActivityStack extends TaskStack {
@WindowTraceLogLevel int logLevel) {
final long token = proto.start(fieldId);
writeToProtoInnerStackOnly(proto, STACK, logLevel);
- proto.write(ID, mStackId);
+ proto.write(com.android.server.am.ActivityStackProto.ID, mStackId);
for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
final Task task = getChildAt(taskNdx);
- task.writeToProto(proto, TASKS, logLevel);
+ task.writeToProto(proto, com.android.server.am.ActivityStackProto.TASKS, logLevel);
}
if (mResumedActivity != null) {
mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
@@ -4803,11 +6499,36 @@ class ActivityStack extends TaskStack {
proto.write(DISPLAY_ID, mDisplayId);
if (!matchParentBounds()) {
final Rect bounds = getRequestedOverrideBounds();
- bounds.writeToProto(proto, BOUNDS);
+ bounds.writeToProto(proto, com.android.server.am.ActivityStackProto.BOUNDS);
}
// TODO: Remove, no longer needed with windowingMode.
proto.write(FULLSCREEN, matchParentBounds());
proto.end(token);
}
+
+ // TODO(proto-merge): Remove once protos for ActivityStack and TaskStack are merged.
+ void writeToProtoInnerStackOnly(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ return;
+ }
+
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
+ proto.write(StackProto.ID, mStackId);
+ for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
+ mChildren.get(taskNdx).writeToProtoInnerTaskOnly(proto, StackProto.TASKS, logLevel);
+ }
+ proto.write(FILLS_PARENT, matchParentBounds());
+ getRawBounds().writeToProto(proto, StackProto.BOUNDS);
+ proto.write(DEFER_REMOVAL, mDeferRemoval);
+ proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
+ proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
+ proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
+ proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
+ mAdjustedBounds.writeToProto(proto, ADJUSTED_BOUNDS);
+ proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 4828a8d864e9..016654fcdf82 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -773,11 +773,12 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
- true /* isTop */) && r.allowMoveToFront()) {
- // We only set the visibility to true if the activity is not being launched in
- // background, and is allowed to be visible based on keyguard state. This avoids
- // setting this into motion in window manager that is later cancelled due to later
- // calls to ensure visible activities that set visibility back to false.
+ true /* isTop */)) {
+ // We only set the visibility to true if the activity is allowed to be visible
+ // based on
+ // keyguard state. This avoids setting this into motion in window manager that is
+ // later cancelled due to later calls to ensure visible activities that set
+ // visibility back to false.
r.setVisibility(true);
}
@@ -1708,15 +1709,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return;
}
- // It is possible for the bounds animation from the WM to call this but be delayed by
- // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
- // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
- // for the AMS lock to be freed. So check and make sure these bounds are still good.
- // TODO(stack-merge): Is this still relevant?
- if (stack.pinnedStackResizeDisallowed()) {
- return;
- }
-
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
mService.deferWindowLayout();
try {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3ef848cb0106..79c76b9ff8dc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3243,7 +3243,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private void sanitizeAndApplyConfigChange(ConfigurationContainer container,
WindowContainerTransaction.Change change) {
- if (!(container instanceof Task)) {
+ if (!(container instanceof Task || container instanceof ActivityStack)) {
throw new RuntimeException("Invalid token in task transaction");
}
// The "client"-facing API should prevent bad changes; however, just in case, sanitize
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index ff1b42377f5f..bef6af350269 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -688,16 +688,15 @@ public class AppTransitionController {
* compare z-order.
*
* @param apps The list of apps to search.
- * @param ignoreInvisible If set to true, ignores apps that are not
- * {@link ActivityRecord#isVisible}.
+ * @param ignoreHidden If set to true, ignores apps that are {@link ActivityRecord#isHidden}.
* @return The top {@link ActivityRecord}.
*/
- private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) {
+ private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreHidden) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord topApp = null;
for (int i = apps.size() - 1; i >= 0; i--) {
final ActivityRecord app = apps.valueAt(i);
- if (ignoreInvisible && !app.isVisible()) {
+ if (ignoreHidden && app.isHidden()) {
continue;
}
final int prefixOrderIndex = app.getPrefixOrderIndex();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6eb9dba254d8..b4dd55de2dba 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -556,9 +556,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Last systemUiVisibility we dispatched to windows.
private int mLastDispatchedSystemUiVisibility = 0;
- private final ArrayList<TaskStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
- private final ArrayList<TaskStack> mTmpNormalStacks = new ArrayList<>();
- private final ArrayList<TaskStack> mTmpHomeStacks = new ArrayList<>();
+ private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
+ private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
+ private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
/** Corner radius that windows should have in order to match the display. */
private final float mWindowCornerRadius;
@@ -652,12 +652,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
+ " config reported=" + w.isLastConfigReportedToClient());
final ActivityRecord activity = w.mActivityRecord;
if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility
- + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
- + " visibleRequested=" + (activity != null && activity.mVisibleRequested)
+ + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
+ + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
+ " parentHidden=" + w.isParentWindowHidden());
else Slog.v(TAG, " VIS: mViewVisibility=" + w.mViewVisibility
- + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
- + " visibleRequested=" + (activity != null && activity.mVisibleRequested)
+ + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
+ + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
+ " parentHidden=" + w.isParentWindowHidden());
}
@@ -1801,15 +1801,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
}
- TaskStack getHomeStack() {
+ ActivityStack getHomeStack() {
return mTaskStackContainers.getHomeStack();
}
/**
* @return The primary split-screen stack, but only if it is visible, and {@code null} otherwise.
*/
- TaskStack getSplitScreenPrimaryStack() {
- TaskStack stack = mTaskStackContainers.getSplitScreenPrimaryStack();
+ ActivityStack getSplitScreenPrimaryStack() {
+ ActivityStack stack = mTaskStackContainers.getSplitScreenPrimaryStack();
return (stack != null && stack.isVisible()) ? stack : null;
}
@@ -1821,11 +1821,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Like {@link #getSplitScreenPrimaryStack}, but also returns the stack if it's currently
* not visible.
*/
- TaskStack getSplitScreenPrimaryStackIgnoringVisibility() {
+ ActivityStack getSplitScreenPrimaryStackIgnoringVisibility() {
return mTaskStackContainers.getSplitScreenPrimaryStack();
}
- TaskStack getPinnedStack() {
+ ActivityStack getPinnedStack() {
return mTaskStackContainers.getPinnedStack();
}
@@ -1837,7 +1837,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Returns the topmost stack on the display that is compatible with the input windowing mode.
* Null is no compatible stack on the display.
*/
- TaskStack getTopStackInWindowingMode(int windowingMode) {
+ ActivityStack getTopStackInWindowingMode(int windowingMode) {
return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
}
@@ -1845,17 +1845,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Returns the topmost stack on the display that is compatible with the input windowing mode and
* activity type. Null is no compatible stack on the display.
*/
- TaskStack getStack(int windowingMode, int activityType) {
+ ActivityStack getStack(int windowingMode, int activityType) {
return mTaskStackContainers.getStack(windowingMode, activityType);
}
@VisibleForTesting
- WindowList<TaskStack> getStacks() {
+ WindowList<ActivityStack> getStacks() {
return mTaskStackContainers.mChildren;
}
@VisibleForTesting
- TaskStack getTopStack() {
+ ActivityStack getTopStack() {
return mTaskStackContainers.getTopStack();
}
@@ -1863,7 +1863,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mTaskStackContainers.getVisibleTasks();
}
- void onStackWindowingModeChanged(TaskStack stack) {
+ void onStackWindowingModeChanged(ActivityStack stack) {
mTaskStackContainers.onStackWindowingModeChanged(stack);
}
@@ -2207,17 +2207,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
out.set(mDisplayFrames.mStable);
}
- void setStackOnDisplay(TaskStack stack, int position) {
+ void setStackOnDisplay(ActivityStack stack, int position) {
if (DEBUG_STACK) Slog.d(TAG_WM, "Set stack=" + stack + " on displayId=" + mDisplayId);
mTaskStackContainers.addChild(stack, position);
}
- void moveStackToDisplay(TaskStack stack, boolean onTop) {
+ void moveStackToDisplay(ActivityStack stack, boolean onTop) {
stack.reparent(mTaskStackContainers, onTop ? POSITION_TOP: POSITION_BOTTOM);
}
// TODO(display-unify): No longer needed then.
- void removeStackFromDisplay(TaskStack stack) {
+ void removeStackFromDisplay(ActivityStack stack) {
mTaskStackContainers.removeChild(stack);
}
@@ -2250,7 +2250,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
getParent().positionChildAt(position, this, includingParents);
}
- void positionStackAt(int position, TaskStack child, boolean includingParents) {
+ void positionStackAt(int position, ActivityStack child, boolean includingParents) {
mTaskStackContainers.positionChildAt(position, child, includingParents);
layoutAndAssignWindowLayersIfNeeded();
}
@@ -2284,7 +2284,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
mTmpTaskForResizePointSearchResult.reset();
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
if (!stack.getWindowConfiguration().canResizeTask()) {
return null;
}
@@ -2307,7 +2307,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTmpRect2.setEmpty();
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0;
--stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
mDisplayFrames.mContent, mTmpRect2);
}
@@ -2437,7 +2437,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
boolean updated = false;
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(i);
if (stack == null || !stack.isAdjustedForIme()) {
continue;
}
@@ -2466,7 +2466,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
boolean clearImeAdjustAnimation() {
boolean changed = false;
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(i);
if (stack != null && stack.isAdjustedForIme()) {
stack.resetAdjustedForIme(true /* adjustBoundsNow */);
changed = true;
@@ -2477,7 +2477,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void beginImeAdjustAnimation() {
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(i);
if (stack.isVisible() && stack.isAdjustedForIme()) {
stack.beginImeAdjustAnimation();
}
@@ -2488,10 +2488,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final WindowState imeWin = mInputMethodWindow;
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
&& !mDividerControllerLocked.isImeHideRequested();
- final TaskStack dockedStack = getSplitScreenPrimaryStack();
+ final ActivityStack dockedStack = getSplitScreenPrimaryStack();
final boolean dockVisible = dockedStack != null;
final Task topDockedTask = dockVisible ? dockedStack.getTopChild() : null;
- final TaskStack imeTargetStack = mWmService.getImeFocusStackLocked();
+ final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
final int imeDockSide = (dockVisible && imeTargetStack != null) ?
imeTargetStack.getDockSide() : DOCKED_INVALID;
final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
@@ -2515,7 +2515,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(i);
final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
&& stack.inSplitScreenWindowingMode()) {
@@ -2528,7 +2528,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
} else {
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(i);
stack.resetAdjustedForIme(!dockVisible);
}
mDividerControllerLocked.setAdjustedForIme(
@@ -2539,7 +2539,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void prepareFreezingTaskBounds() {
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
stack.prepareFreezingTaskBounds();
}
}
@@ -2620,7 +2620,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
proto.write(ID, mDisplayId);
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
stack.writeToProtoInnerStackOnly(proto, STACKS, logLevel);
}
mDividerControllerLocked.writeToProto(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
@@ -2735,7 +2735,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
stack.dump(pw, prefix + " ", dumpAll);
}
@@ -2765,15 +2765,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.println();
// Dump stack references
- final TaskStack homeStack = getHomeStack();
+ final ActivityStack homeStack = getHomeStack();
if (homeStack != null) {
pw.println(prefix + "homeStack=" + homeStack.getName());
}
- final TaskStack pinnedStack = getPinnedStack();
+ final ActivityStack pinnedStack = getPinnedStack();
if (pinnedStack != null) {
pw.println(prefix + "pinnedStack=" + pinnedStack.getName());
}
- final TaskStack splitScreenPrimaryStack = getSplitScreenPrimaryStack();
+ final ActivityStack splitScreenPrimaryStack = getSplitScreenPrimaryStack();
if (splitScreenPrimaryStack != null) {
pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName());
}
@@ -2806,7 +2806,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** Returns true if the stack in the windowing mode is visible. */
boolean isStackVisible(int windowingMode) {
- final TaskStack stack = getTopStackInWindowingMode(windowingMode);
+ final ActivityStack stack = getTopStackInWindowingMode(windowingMode);
return stack != null && stack.isVisible();
}
@@ -3061,7 +3061,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
wsa.destroySurface();
mWmService.mForceRemoves.add(w);
mTmpWindow = w;
- } else if (w.mActivityRecord != null && !w.mActivityRecord.isClientVisible()) {
+ } else if (w.mActivityRecord != null && w.mActivityRecord.isClientHidden()) {
Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
+ w + " surface=" + wsa.mSurfaceController
+ " token=" + w.mActivityRecord);
@@ -3911,7 +3911,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Window container class that contains all containers on this display relating to Apps.
* I.e Activities.
*/
- private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
+ private final class TaskStackContainers extends DisplayChildWindowContainer<ActivityStack> {
/**
* A control placed at the appropriate level for transitions to occur.
*/
@@ -3936,9 +3936,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
- private TaskStack mHomeStack = null;
- private TaskStack mPinnedStack = null;
- private TaskStack mSplitScreenPrimaryStack = null;
+ private ActivityStack mHomeStack = null;
+ private ActivityStack mPinnedStack = null;
+ private ActivityStack mSplitScreenPrimaryStack = null;
TaskStackContainers(WindowManagerService service) {
super(service);
@@ -3954,7 +3954,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Returns the topmost stack on the display that is compatible with the input windowing mode
* and activity type. Null is no compatible stack on the display.
*/
- TaskStack getStack(int windowingMode, int activityType) {
+ ActivityStack getStack(int windowingMode, int activityType) {
if (activityType == ACTIVITY_TYPE_HOME) {
return mHomeStack;
}
@@ -3964,7 +3964,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mSplitScreenPrimaryStack;
}
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ final ActivityStack stack = mTaskStackContainers.getChildAt(i);
if (activityType == ACTIVITY_TYPE_UNDEFINED
&& windowingMode == stack.getWindowingMode()) {
// Passing in undefined type means we want to match the topmost stack with the
@@ -3979,23 +3979,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
@VisibleForTesting
- TaskStack getTopStack() {
+ ActivityStack getTopStack() {
return mTaskStackContainers.getChildCount() > 0
? mTaskStackContainers.getChildAt(mTaskStackContainers.getChildCount() - 1) : null;
}
- TaskStack getHomeStack() {
+ ActivityStack getHomeStack() {
if (mHomeStack == null && mDisplayId == DEFAULT_DISPLAY) {
Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
}
return mHomeStack;
}
- TaskStack getPinnedStack() {
+ ActivityStack getPinnedStack() {
return mPinnedStack;
}
- TaskStack getSplitScreenPrimaryStack() {
+ ActivityStack getSplitScreenPrimaryStack() {
return mSplitScreenPrimaryStack;
}
@@ -4009,7 +4009,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return visibleTasks;
}
- void onStackWindowingModeChanged(TaskStack stack) {
+ void onStackWindowingModeChanged(ActivityStack stack) {
removeStackReferenceIfNeeded(stack);
addStackReferenceIfNeeded(stack);
if (stack == mPinnedStack && getTopStack() != stack) {
@@ -4018,7 +4018,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- private void addStackReferenceIfNeeded(TaskStack stack) {
+ private void addStackReferenceIfNeeded(ActivityStack stack) {
if (stack.isActivityTypeHome()) {
if (mHomeStack != null) {
throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
@@ -4046,7 +4046,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- private void removeStackReferenceIfNeeded(TaskStack stack) {
+ private void removeStackReferenceIfNeeded(ActivityStack stack) {
if (stack == mHomeStack) {
mHomeStack = null;
} else if (stack == mPinnedStack) {
@@ -4061,14 +4061,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
@Override
- void addChild(TaskStack stack, int position) {
+ void addChild(ActivityStack stack, int position) {
addStackReferenceIfNeeded(stack);
position = findPositionForStack(position, stack, true /* adding */);
super.addChild(stack, position);
if (mActivityDisplay != null) {
- // TODO(stack-merge): Remove cast.
- mActivityDisplay.addChild((ActivityStack) stack, position, true /*fromDc*/);
+ mActivityDisplay.addChild(stack, position, true /*fromDc*/);
}
// The reparenting case is handled in WindowContainer.
@@ -4079,11 +4078,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
@Override
- protected void removeChild(TaskStack stack) {
+ protected void removeChild(ActivityStack stack) {
super.removeChild(stack);
if (mActivityDisplay != null) {
- // TODO(stack-merge): Remove cast.
- mActivityDisplay.onChildRemoved((ActivityStack) stack);
+ mActivityDisplay.onChildRemoved(stack);
}
removeStackReferenceIfNeeded(stack);
}
@@ -4095,7 +4093,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
@Override
- void positionChildAt(int position, TaskStack child, boolean includingParents) {
+ void positionChildAt(int position, ActivityStack child, boolean includingParents) {
if (child.getWindowConfiguration().isAlwaysOnTop()
&& position != POSITION_TOP) {
// This stack is always-on-top, override the default behavior.
@@ -4134,7 +4132,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* @param adding Flag indicates whether we're adding a new stack or positioning an existing.
* @return The proper position for the stack.
*/
- private int findPositionForStack(int requestedPosition, TaskStack stack, boolean adding) {
+ private int findPositionForStack(int requestedPosition, ActivityStack stack,
+ boolean adding) {
if (stack.inPinnedWindowingMode()) {
return POSITION_TOP;
}
@@ -4316,7 +4315,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
assignStackOrdering(t);
for (int i = 0; i < mChildren.size(); i++) {
- final TaskStack s = mChildren.get(i);
+ final ActivityStack s = mChildren.get(i);
s.assignChildLayers(t);
}
}
@@ -4329,7 +4328,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTmpHomeStacks.clear();
mTmpNormalStacks.clear();
for (int i = 0; i < mChildren.size(); ++i) {
- final TaskStack s = mChildren.get(i);
+ final ActivityStack s = mChildren.get(i);
if (s.isAlwaysOnTop()) {
mTmpAlwaysOnTopStacks.add(s);
} else if (s.isActivityTypeHome()) {
@@ -4349,7 +4348,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
int layerForSplitScreenDividerAnchor = layer++;
int layerForAnimationLayer = layer++;
for (int i = 0; i < mTmpNormalStacks.size(); i++) {
- final TaskStack s = mTmpNormalStacks.get(i);
+ final ActivityStack s = mTmpNormalStacks.get(i);
s.assignLayer(t, layer++);
if (s.inSplitScreenWindowingMode()) {
// The split screen divider anchor is located above the split screen window.
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b454922ae7cd..07d5094a8d4c 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -140,7 +140,7 @@ public class DockedStackDividerController {
float mLastDividerProgress;
private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
private boolean mImeHideRequested;
- private TaskStack mDimmedStack;
+ private ActivityStack mDimmedStack;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -255,7 +255,7 @@ public class DockedStackDividerController {
}
boolean isHomeStackResizable() {
- final TaskStack homeStack = mDisplayContent.getHomeStack();
+ final ActivityStack homeStack = mDisplayContent.getHomeStack();
if (homeStack == null) {
return false;
}
@@ -371,7 +371,7 @@ public class DockedStackDividerController {
if (mWindow == null) {
return;
}
- TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
// If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
final boolean visible = stack != null;
@@ -415,7 +415,7 @@ public class DockedStackDividerController {
}
void positionDockedStackedDivider(Rect frame) {
- TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
if (stack == null) {
// Unfortunately we might end up with still having a divider, even though the underlying
// stack was already removed. This is because we are on AM thread and the removal of the
@@ -523,7 +523,8 @@ public class DockedStackDividerController {
// If a primary stack was just created, it will not have access to display content at
// this point so pass it from here to get a valid dock side.
- final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ final ActivityStack stack =
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
mOriginalDockedSide = stack.getDockSideForDisplay(mDisplayContent);
return;
}
@@ -558,7 +559,7 @@ public class DockedStackDividerController {
boolean isHomeStackResizable) {
long animDuration = 0;
if (animate) {
- final TaskStack stack =
+ final ActivityStack stack =
mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
final long transitionDuration = isAnimationMaximizing()
? mDisplayContent.mAppTransition.getLastClipRevealTransitionDuration()
@@ -629,10 +630,10 @@ public class DockedStackDividerController {
*/
void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
// TODO: Maybe only allow split-screen windowing modes?
- final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
+ final ActivityStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode)
: null;
- final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
+ final ActivityStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
// Ensure an old dim that was shown for the docked stack divider is removed so we don't end
@@ -703,7 +704,7 @@ public class DockedStackDividerController {
if (mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() == null) {
return;
}
- final TaskStack homeStack = mDisplayContent.getHomeStack();
+ final ActivityStack homeStack = mDisplayContent.getHomeStack();
if (homeStack == null) {
return;
}
@@ -717,7 +718,7 @@ public class DockedStackDividerController {
if (mMinimizedDock && mService.mKeyguardOrAodShowingOnDefaultDisplay) {
return;
}
- final TaskStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
+ final ActivityStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
final boolean minimizedForRecentsAnimation = recentsAnim != null &&
@@ -872,7 +873,7 @@ public class DockedStackDividerController {
}
private boolean setMinimizedDockedStack(boolean minimized) {
- final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ final ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
}
@@ -922,7 +923,7 @@ public class DockedStackDividerController {
}
private boolean animateForMinimizedDockedStack(long now) {
- final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ final ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
@@ -956,7 +957,7 @@ public class DockedStackDividerController {
/**
* Gets the amount how much to minimize a stack depending on the interpolated fraction t.
*/
- private float getMinimizeAmount(TaskStack stack, float t) {
+ private float getMinimizeAmount(ActivityStack stack, float t) {
final float naturalAmount = getInterpolatedAnimationValue(t);
if (isAnimationMaximizing()) {
return adjustMaximizeAmount(stack, t, naturalAmount);
@@ -971,7 +972,7 @@ public class DockedStackDividerController {
* transition so we don't create a visible "hole", but only if both the clip reveal and the
* docked stack divider start from about the same portion on the screen.
*/
- private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
+ private float adjustMaximizeAmount(ActivityStack stack, float t, float naturalAmount) {
if (mMaximizeMeetFraction == 1f) {
return naturalAmount;
}
@@ -987,7 +988,7 @@ public class DockedStackDividerController {
* Retrieves the animation fraction at which the docked stack has to meet the clip reveal
* edge. See {@link #adjustMaximizeAmount}.
*/
- private float getClipRevealMeetFraction(TaskStack stack) {
+ private float getClipRevealMeetFraction(ActivityStack stack) {
if (!isAnimationMaximizing() || stack == null ||
!mDisplayContent.mAppTransition.hadClipRevealAnimation()) {
return 1f;
diff --git a/services/core/java/com/android/server/wm/DragResizeMode.java b/services/core/java/com/android/server/wm/DragResizeMode.java
index c0bf1e89abfb..71beb5032914 100644
--- a/services/core/java/com/android/server/wm/DragResizeMode.java
+++ b/services/core/java/com/android/server/wm/DragResizeMode.java
@@ -35,7 +35,7 @@ class DragResizeMode {
*/
static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
- static boolean isModeAllowedForStack(TaskStack stack, int mode) {
+ static boolean isModeAllowedForStack(ActivityStack stack, int mode) {
switch (mode) {
case DRAG_RESIZE_MODE_FREEFORM:
return stack.getWindowingMode() == WINDOWING_MODE_FREEFORM;
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 6cb1e763b812..a5b1fda1d73c 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -140,7 +140,7 @@ class PinnedStackController {
public void startAnimation(Rect destinationBounds, Rect sourceRectHint,
int animationDuration) {
synchronized (mService.mGlobalLock) {
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
pinnedStack.animateResizePinnedStack(destinationBounds,
sourceRectHint, animationDuration, true /* fromFullscreen */);
}
@@ -468,7 +468,7 @@ class PinnedStackController {
}
try {
final Rect animatingBounds = new Rect();
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
if (pinnedStack != null) {
pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index fc74d00e82db..caf95de75357 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -107,7 +107,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetStack);
if (targetActivity != null) {
- if (targetActivity.mVisibleRequested || targetActivity.isTopRunningActivity()) {
+ if (targetActivity.visible || targetActivity.isTopRunningActivity()) {
// The activity is ready.
return;
}
@@ -189,7 +189,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
// Send launch hint if we are actually launching the target. If it's already visible
// (shouldn't happen in general) we don't need to send it.
- if (targetActivity == null || !targetActivity.mVisibleRequested) {
+ if (targetActivity == null || !targetActivity.visible) {
mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
true /* forceSend */, targetActivity);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 7a3e43b127f4..282144e50510 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -366,7 +366,7 @@ public class RecentsAnimationController implements DeathRecipient {
// Make leashes for each of the visible/target tasks and add it to the recents animation to
// be started
final ArrayList<Task> visibleTasks = mDisplayContent.getVisibleTasks();
- final TaskStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED,
+ final ActivityStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED,
targetActivityType);
if (targetStack != null) {
for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
@@ -410,7 +410,8 @@ public class RecentsAnimationController implements DeathRecipient {
}
// Save the minimized home height
- final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ final ActivityStack dockedStack =
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode(
mDisplayContent.getConfiguration(),
dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 5a21016e54dc..06cce520d0bf 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1250,6 +1250,7 @@ class RootActivityContainer extends ConfigurationContainer
stack.getBounds(info.bounds);
info.displayId = displayId;
info.stackId = stack.mStackId;
+ info.stackToken = stack.mRemoteToken;
info.userId = stack.mCurrentUser;
info.visible = stack.shouldBeVisible(null);
// A stack might be not attached to a display.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 63b11ffe158d..565f95e3681b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -412,10 +412,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- TaskStack getStack(int windowingMode, int activityType) {
+ ActivityStack getStack(int windowingMode, int activityType) {
for (int i = mChildren.size() - 1; i >= 0; i--) {
final DisplayContent dc = mChildren.get(i);
- final TaskStack stack = dc.getStack(windowingMode, activityType);
+ final ActivityStack stack = dc.getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 520c26e65fee..b229a1d0a8e7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -60,7 +60,9 @@ import static com.android.server.EventLogTags.WM_TASK_CREATED;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
import static com.android.server.am.TaskRecordProto.ACTIVITIES;
import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
+import static com.android.server.am.TaskRecordProto.BOUNDS;
import static com.android.server.am.TaskRecordProto.FULLSCREEN;
+import static com.android.server.am.TaskRecordProto.ID;
import static com.android.server.am.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
import static com.android.server.am.TaskRecordProto.MIN_HEIGHT;
import static com.android.server.am.TaskRecordProto.MIN_WIDTH;
@@ -87,8 +89,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
+import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.ID;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
@@ -928,7 +932,6 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
@Override
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- // TODO(stack-merge): Remove casts after object merge.
final ActivityStack oldStack = ((ActivityStack) oldParent);
final ActivityStack newStack = ((ActivityStack) newParent);
@@ -2198,7 +2201,7 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = getChildAt(activityNdx);
- if (r.mVisibleRequested) {
+ if (r.visible) {
r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
}
}
@@ -2231,8 +2234,8 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
return getTaskStack() != null ? getTaskStack().getDisplayContent() : null;
}
- TaskStack getTaskStack() {
- return (TaskStack) getParent();
+ ActivityStack getTaskStack() {
+ return (ActivityStack) getParent();
}
int getAdjustedAddPosition(ActivityRecord r, int suggestedPosition) {
@@ -2298,13 +2301,12 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
}
// TODO: Consolidate this with Task.reparent()
- void reparent(TaskStack stack, int position, boolean moveParents, String reason) {
+ void reparent(ActivityStack stack, int position, boolean moveParents, String reason) {
if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
+ " from stack=" + getTaskStack());
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask");
- // TODO(stack-merge): Remove cast.
- final ActivityStack prevStack = (ActivityStack) getTaskStack();
+ final ActivityStack prevStack = getTaskStack();
final boolean wasTopFocusedStack =
mAtmService.mRootActivityContainer.isTopDisplayFocusedStack(prevStack);
final ActivityDisplay prevStackDisplay = prevStack.getDisplay();
@@ -2520,7 +2522,7 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
for (int i = mChildren.size() - 1; i >= 0; i--) {
final ActivityRecord token = mChildren.get(i);
// skip hidden (or about to hide) apps
- if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) {
+ if (token.mIsExiting || token.isClientHidden() || token.hiddenRequested) {
continue;
}
final WindowState win = token.findMainWindow();
@@ -2738,9 +2740,10 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta
ActivityRecord getTopVisibleActivity() {
for (int i = mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mChildren.get(i);
- if (!activity.mIsExiting && activity.isClientVisible() && activity.mVisibleRequested) {
- return activity;
+ final ActivityRecord token = mChildren.get(i);
+ // skip hidden (or about to hide) apps
+ if (!token.mIsExiting && !token.isClientHidden() && !token.hiddenRequested) {
+ return token;
}
}
return null;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index f7b802da8860..a867a5d41e24 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -133,14 +133,15 @@ class TaskPositioner implements IBinder.DeathRecipient {
@Override
public void onInputEvent(InputEvent event) {
- if (!(event instanceof MotionEvent)
- || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
- return;
- }
- final MotionEvent motionEvent = (MotionEvent) event;
boolean handled = false;
-
try {
+ // All returns need to be in the try block to make sure the finishInputEvent is
+ // called correctly.
+ if (!(event instanceof MotionEvent)
+ || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+ return;
+ }
+ final MotionEvent motionEvent = (MotionEvent) event;
if (mDragEnded) {
// The drag has ended but the clean-up message has not been processed by
// window manager. Drop events that occur after this until window manager
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
deleted file mode 100644
index ec627c881709..000000000000
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ /dev/null
@@ -1,1816 +0,0 @@
-/*
- * Copyright (C) 2013 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.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
-import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
-import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
-import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
-import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
-import static com.android.server.wm.StackProto.ANIMATING_BOUNDS;
-import static com.android.server.wm.StackProto.BOUNDS;
-import static com.android.server.wm.StackProto.DEFER_REMOVAL;
-import static com.android.server.wm.StackProto.FILLS_PARENT;
-import static com.android.server.wm.StackProto.ID;
-import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
-import static com.android.server.wm.StackProto.TASKS;
-import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.app.RemoteAction;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.RemoteException;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.server.EventLogTags;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-public class TaskStack extends WindowContainer<Task> implements
- BoundsAnimationTarget, ConfigurationContainerListener {
- /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
- * restrict IME adjustment so that a min portion of top stack remains visible.*/
- private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
-
- /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
- private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
-
- /** Unique identifier */
- final int mStackId;
-
- /** For comparison with DisplayContent bounds. */
- private Rect mTmpRect = new Rect();
- private Rect mTmpRect2 = new Rect();
- private Rect mTmpRect3 = new Rect();
-
- /** For Pinned stack controlling. */
- private Rect mTmpToBounds = new Rect();
-
- /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
- private final Rect mAdjustedBounds = new Rect();
-
- /**
- * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
- * represent the state when the animation has ended.
- */
- private final Rect mFullyAdjustedImeBounds = new Rect();
-
- /** ActivityRecords that are exiting, but still on screen for animations. */
- final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>();
-
- /** Detach this stack from its display when animation completes. */
- // TODO: maybe tie this to WindowContainer#removeChild some how...
- private boolean mDeferRemoval;
-
- private final Rect mTmpAdjustedBounds = new Rect();
- private boolean mAdjustedForIme;
- private boolean mImeGoingAway;
- private WindowState mImeWin;
- private float mMinimizeAmount;
- private float mAdjustImeAmount;
- private float mAdjustDividerAmount;
- private final int mDockedStackMinimizeThickness;
-
- // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
- // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
- // would otherwise apply while resizing, while resizing in the bounds animating mode.
- private boolean mBoundsAnimating = false;
- // Set when an animation has been requested but has not yet started from the UI thread. This is
- // cleared when the animation actually starts.
- private boolean mBoundsAnimatingRequested = false;
- private boolean mBoundsAnimatingToFullscreen = false;
- private boolean mCancelCurrentBoundsAnimation = false;
- private Rect mBoundsAnimationTarget = new Rect();
- private Rect mBoundsAnimationSourceHintBounds = new Rect();
- private @BoundsAnimationController.AnimationType int mAnimationType;
-
- Rect mPreAnimationBounds = new Rect();
-
- private Dimmer mDimmer = new Dimmer(this);
-
- /**
- * For {@link #prepareSurfaces}.
- */
- private final Rect mTmpDimBoundsRect = new Rect();
- private final Point mLastSurfaceSize = new Point();
-
- private final AnimatingActivityRegistry mAnimatingActivityRegistry =
- new AnimatingActivityRegistry();
-
- TaskStack(WindowManagerService service, int stackId) {
- super(service);
- mStackId = stackId;
- mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_minimize_thickness);
- EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
- }
-
- Task findHomeTask() {
- if (!isActivityTypeHome() || mChildren.isEmpty()) {
- return null;
- }
- return mChildren.get(mChildren.size() - 1);
- }
-
- void prepareFreezingTaskBounds() {
- for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = mChildren.get(taskNdx);
- task.prepareFreezingBounds();
- }
- }
-
- /**
- * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
- * the normal task bounds.
- *
- * @param bounds The adjusted bounds.
- */
- private void setAdjustedBounds(Rect bounds) {
- if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
- return;
- }
-
- mAdjustedBounds.set(bounds);
- final boolean adjusted = !mAdjustedBounds.isEmpty();
- Rect insetBounds = null;
- if (adjusted && isAdjustedForMinimizedDockedStack()) {
- insetBounds = getRawBounds();
- } else if (adjusted && mAdjustedForIme) {
- if (mImeGoingAway) {
- insetBounds = getRawBounds();
- } else {
- insetBounds = mFullyAdjustedImeBounds;
- }
- }
- alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : getRawBounds(), insetBounds);
- mDisplayContent.setLayoutNeeded();
-
- updateSurfaceBounds();
- }
-
- private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
- if (matchParentBounds()) {
- return;
- }
-
- final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
-
- // Update bounds of containing tasks.
- for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = mChildren.get(taskNdx);
- task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
- }
- }
-
- @Override
- public int setBounds(Rect bounds) {
- return setBounds(getRequestedOverrideBounds(), bounds);
- }
-
- private int setBounds(Rect existing, Rect bounds) {
- if (equivalentBounds(existing, bounds)) {
- return BOUNDS_CHANGE_NONE;
- }
-
- final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
-
- updateAdjustedBounds();
-
- updateSurfaceBounds();
- return result;
- }
-
- /** Bounds of the stack without adjusting for other factors in the system like visibility
- * of docked stack.
- * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a
- * it takes into consideration other system factors. */
- void getRawBounds(Rect out) {
- out.set(getRawBounds());
- }
-
- private Rect getRawBounds() {
- return super.getBounds();
- }
-
- @Override
- public void getBounds(Rect bounds) {
- bounds.set(getBounds());
- }
-
- @Override
- public Rect getBounds() {
- // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
- // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
- // stack is visible since it is already what we want to represent to the rest of the
- // system.
- if (!mAdjustedBounds.isEmpty()) {
- return mAdjustedBounds;
- } else {
- return super.getBounds();
- }
- }
-
- /**
- * Sets the bounds animation target bounds ahead of an animation. This can't currently be done
- * in onAnimationStart() since that is started on the UiThread.
- */
- private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
- boolean toFullscreen) {
- if (mAnimationType == BoundsAnimationController.BOUNDS) {
- mBoundsAnimatingRequested = true;
- }
- mBoundsAnimatingToFullscreen = toFullscreen;
- if (destBounds != null) {
- mBoundsAnimationTarget.set(destBounds);
- } else {
- mBoundsAnimationTarget.setEmpty();
- }
- if (sourceHintBounds != null) {
- mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
- } else if (!mBoundsAnimating) {
- // If the bounds are already animating, we don't want to reset the source hint. This is
- // because the source hint is sent when starting the animation from the client that
- // requested to enter pip. Other requests can adjust the pip bounds during an animation,
- // but could accidentally reset the source hint bounds.
- mBoundsAnimationSourceHintBounds.setEmpty();
- }
-
- mPreAnimationBounds.set(getRawBounds());
- }
-
- /**
- * @return the final bounds for the bounds animation.
- */
- void getFinalAnimationBounds(Rect outBounds) {
- outBounds.set(mBoundsAnimationTarget);
- }
-
- /**
- * @return the final source bounds for the bounds animation.
- */
- void getFinalAnimationSourceHintBounds(Rect outBounds) {
- outBounds.set(mBoundsAnimationSourceHintBounds);
- }
-
- /**
- * @return the final animation bounds if the task stack is currently being animated, or the
- * current stack bounds otherwise.
- */
- void getAnimationOrCurrentBounds(Rect outBounds) {
- if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
- getFinalAnimationBounds(outBounds);
- return;
- }
- getBounds(outBounds);
- }
-
- /** Bounds of the stack with other system factors taken into consideration. */
- void getDimBounds(Rect out) {
- getBounds(out);
- }
-
- /**
- * Updates the passed-in {@code inOutBounds} based on the current state of the
- * pinned controller. This gets run *after* the override configuration is updated, so it's
- * safe to rely on the controller's state in here (though eventually this dependence should
- * be removed).
- *
- * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
- * update pinned controller state.
- *
- * @param inOutBounds the bounds to update (both input and output).
- * @return true if bounds were updated to some non-empty value.
- */
- boolean calculatePinnedBoundsForConfigChange(Rect inOutBounds) {
- boolean animating = false;
- if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
- animating = true;
- getFinalAnimationBounds(mTmpRect2);
- } else {
- mTmpRect2.set(inOutBounds);
- }
- boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
- mTmpRect2, mTmpRect3);
- if (updated) {
- inOutBounds.set(mTmpRect3);
-
- // The final boundary is updated while there is an existing boundary animation. Let's
- // cancel this animation to prevent the obsolete animation overwritten updated bounds.
- if (animating && !inOutBounds.equals(mBoundsAnimationTarget)) {
- final DisplayContent displayContent = getDisplayContent();
- displayContent.mBoundsAnimationController.getHandler().post(() ->
- displayContent.mBoundsAnimationController.cancel(this));
- }
- // Once we've set the bounds based on the rotation of the old bounds in the new
- // orientation, clear the animation target bounds since they are obsolete, and
- // cancel any currently running animations
- mBoundsAnimationTarget.setEmpty();
- mBoundsAnimationSourceHintBounds.setEmpty();
- mCancelCurrentBoundsAnimation = true;
- }
- return updated;
- }
-
- /**
- * Updates the passed-in {@code inOutBounds} based on the current state of the
- * docked controller. This gets run *after* the override configuration is updated, so it's
- * safe to rely on the controller's state in here (though eventually this dependence should
- * be removed).
- *
- * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
- * update docked controller state.
- *
- * @param parentConfig the parent configuration for reference.
- * @param inOutBounds the bounds to update (both input and output).
- */
- void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) {
- final boolean primary =
- getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds);
- final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout;
- snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds);
- if (primary) {
- final int newDockSide = getDockSide(parentConfig, inOutBounds);
- // Update the dock create mode and clear the dock create bounds, these
- // might change after a rotation and the original values will be invalid.
- mWmService.setDockedStackCreateStateLocked(
- (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
- null);
- mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
- }
- }
-
- /**
- * Some primary split screen sides are not allowed by the policy. This method queries the policy
- * and moves the primary stack around if needed.
- *
- * @param parentConfig the configuration of the stack's parent.
- * @param primary true if adjusting the primary docked stack, false for secondary.
- * @param inOutBounds the bounds of the stack to adjust.
- */
- void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary,
- Rect inOutBounds) {
- final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds);
- final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide);
- final int primaryDockSide = primary ? dockSide : otherDockSide;
- if (mDisplayContent.getDockedDividerController()
- .canPrimaryStackDockTo(primaryDockSide,
- parentConfig.windowConfiguration.getBounds(),
- parentConfig.windowConfiguration.getRotation())) {
- return;
- }
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- switch (otherDockSide) {
- case DOCKED_LEFT:
- int movement = inOutBounds.left;
- inOutBounds.left -= movement;
- inOutBounds.right -= movement;
- break;
- case DOCKED_RIGHT:
- movement = parentBounds.right - inOutBounds.right;
- inOutBounds.left += movement;
- inOutBounds.right += movement;
- break;
- case DOCKED_TOP:
- movement = inOutBounds.top;
- inOutBounds.top -= movement;
- inOutBounds.bottom -= movement;
- break;
- case DOCKED_BOTTOM:
- movement = parentBounds.bottom - inOutBounds.bottom;
- inOutBounds.top += movement;
- inOutBounds.bottom += movement;
- break;
- }
- }
-
- /**
- * Snaps the bounds after rotation to the closest snap target for the docked stack.
- */
- void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout,
- Rect outBounds) {
-
- // Calculate the current position.
- final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
- final int dockSide = getDockSide(parentConfig, outBounds);
- final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
- dockSide, dividerSize);
- final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
- final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
-
- // Snap the position to a target.
- final int rotation = parentConfig.windowConfiguration.getRotation();
- final int orientation = parentConfig.orientation;
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
- displayCutout, outBounds);
- final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
- mWmService.mContext.getResources(), displayWidth, displayHeight,
- dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
- getDockSide(), isMinimizedDockAndHomeStackResizable());
- final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
-
- // Recalculate the bounds based on the position of the target.
- DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
- outBounds, displayWidth, displayHeight,
- dividerSize);
- }
-
- /**
- * Put a Task in this stack. Used for adding only.
- * When task is added to top of the stack, the entire branch of the hierarchy (including stack
- * and display) will be brought to top.
- * @param task The task to add.
- * @param position Target position to add the task to.
- * @param showForAllUsers Whether to show the task regardless of the current user.
- */
- void addChild(Task task, int position, boolean showForAllUsers, boolean moveParents) {
- // Add child task.
- addChild(task, null);
-
- // Move child to a proper position, as some restriction for position might apply.
- position = positionChildAt(
- position, task, moveParents /* includingParents */, showForAllUsers);
- // TODO: Feels like this should go in TaskRecord#onParentChanged
- final boolean toTop = position >= getChildCount();
- task.updateTaskMovement(toTop);
- }
-
- @Override
- void addChild(Task task, int position) {
- addChild(task, position, task.showForAllUsers(), false /* includingParents */);
- }
-
- void positionChildAt(Task child, int position) {
- if (DEBUG_STACK) {
- Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
- }
- if (child == null) {
- if (DEBUG_STACK) {
- Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
- }
- return;
- }
- positionChildAt(position, child, false /* includingParents */);
- child.updateTaskMovement(true);
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
-
- void positionChildAtTop(Task child, boolean includingParents) {
- if (child == null) {
- // TODO: Fix the call-points that cause this to happen.
- return;
- }
-
- positionChildAt(POSITION_TOP, child, includingParents);
- child.updateTaskMovement(true);
-
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent.mAppTransition.isTransitionSet()) {
- child.setSendingToBottom(false);
- }
- displayContent.layoutAndAssignWindowLayersIfNeeded();
- }
-
- void positionChildAtBottom(Task child, boolean includingParents) {
- if (child == null) {
- // TODO: Fix the call-points that cause this to happen.
- return;
- }
-
- positionChildAt(POSITION_BOTTOM, child, includingParents);
-
- if (getDisplayContent().mAppTransition.isTransitionSet()) {
- child.setSendingToBottom(true);
- }
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
-
- @Override
- void positionChildAt(int position, Task child, boolean includingParents) {
- positionChildAt(position, child, includingParents, child.showForAllUsers());
- }
-
- /**
- * Overridden version of {@link TaskStack#positionChildAt(int, Task, boolean)}. Used in
- * {@link TaskStack#addChild(Task, int, boolean showForAllUsers, boolean)}, as it can receive
- * showForAllUsers param from {@link ActivityRecord} instead of {@link Task#showForAllUsers()}.
- */
- private int positionChildAt(int position, Task child, boolean includingParents,
- boolean showForAllUsers) {
- final int targetPosition = findPositionForTask(child, position, showForAllUsers);
- super.positionChildAt(targetPosition, child, includingParents);
-
- // Log positioning.
- if (DEBUG_TASK_MOVEMENT)
- Slog.d(TAG_WM, "positionTask: task=" + this + " position=" + position);
-
- final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0;
- EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition);
-
- return targetPosition;
- }
-
- @Override
- void onChildPositionChanged(WindowContainer child) {
- if (mChildren.contains(child)) {
- ((Task) child).updateTaskMovement(getTopChild() == child);
- }
- }
-
- void reparent(DisplayContent newParent, boolean onTop) {
- // Real parent of stack is within display object, so we have to delegate re-parenting there.
- newParent.moveStackToDisplay(this, onTop);
- }
-
- // TODO: We should really have users as a window container in the hierarchy so that we don't
- // have to do complicated things like we are doing in this method.
- int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers) {
- final boolean canShowTask =
- showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
-
- final int stackSize = mChildren.size();
- int minPosition = 0;
- int maxPosition = stackSize - 1;
-
- if (canShowTask) {
- minPosition = computeMinPosition(minPosition, stackSize);
- } else {
- maxPosition = computeMaxPosition(maxPosition);
- }
-
- // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
- if (targetPosition == POSITION_BOTTOM && minPosition == 0) {
- return POSITION_BOTTOM;
- } else if (targetPosition == POSITION_TOP && maxPosition == (stackSize - 1)) {
- return POSITION_TOP;
- }
- // Reset position based on minimum/maximum possible positions.
- return Math.min(Math.max(targetPosition, minPosition), maxPosition);
- }
-
- /** Calculate the minimum possible position for a task that can be shown to the user.
- * The minimum position will be above all other tasks that can't be shown.
- * @param minPosition The minimum position the caller is suggesting.
- * We will start adjusting up from here.
- * @param size The size of the current task list.
- */
- private int computeMinPosition(int minPosition, int size) {
- while (minPosition < size) {
- final Task tmpTask = mChildren.get(minPosition);
- final boolean canShowTmpTask =
- tmpTask.showForAllUsers()
- || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
- if (canShowTmpTask) {
- break;
- }
- minPosition++;
- }
- return minPosition;
- }
-
- /** Calculate the maximum possible position for a task that can't be shown to the user.
- * The maximum position will be below all other tasks that can be shown.
- * @param maxPosition The maximum position the caller is suggesting.
- * We will start adjusting down from here.
- */
- private int computeMaxPosition(int maxPosition) {
- while (maxPosition > 0) {
- final Task tmpTask = mChildren.get(maxPosition);
- final boolean canShowTmpTask =
- tmpTask.showForAllUsers()
- || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
- if (!canShowTmpTask) {
- break;
- }
- maxPosition--;
- }
- return maxPosition;
- }
-
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- final int prevWindowingMode = getWindowingMode();
- super.onConfigurationChanged(newParentConfig);
-
- // Only need to update surface size here since the super method will handle updating
- // surface position.
- updateSurfaceSize(getPendingTransaction());
- final int windowingMode = getWindowingMode();
-
- if (mDisplayContent == null) {
- return;
- }
-
- if (prevWindowingMode != windowingMode) {
- mDisplayContent.onStackWindowingModeChanged(this);
-
- if (inSplitScreenSecondaryWindowingMode()) {
- // When the stack is resized due to entering split screen secondary, offset the
- // windows to compensate for the new stack position.
- forAllWindows(w -> {
- w.mWinAnimator.setOffsetPositionForStackResize(true);
- }, true);
- }
- }
- }
-
- private void updateSurfaceBounds() {
- updateSurfaceSize(getPendingTransaction());
- updateSurfacePosition();
- scheduleAnimation();
- }
-
- /**
- * Calculate an amount by which to expand the stack bounds in each direction.
- * Used to make room for shadows in the pinned windowing mode.
- */
- int getStackOutset() {
- DisplayContent displayContent = getDisplayContent();
- if (inPinnedWindowingMode() && displayContent != null) {
- final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
-
- // We multiply by two to match the client logic for converting view elevation
- // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
- return (int)Math.ceil(mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
- displayMetrics) * 2);
- }
- return 0;
- }
-
- @Override
- void getRelativeDisplayedPosition(Point outPos) {
- super.getRelativeDisplayedPosition(outPos);
- final int outset = getStackOutset();
- outPos.x -= outset;
- outPos.y -= outset;
- }
-
- private void updateSurfaceSize(SurfaceControl.Transaction transaction) {
- if (mSurfaceControl == null) {
- return;
- }
-
- final Rect stackBounds = getDisplayedBounds();
- int width = stackBounds.width();
- int height = stackBounds.height();
-
- final int outset = getStackOutset();
- width += 2*outset;
- height += 2*outset;
-
- if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
- return;
- }
- transaction.setWindowCrop(mSurfaceControl, width, height);
- mLastSurfaceSize.set(width, height);
- }
-
- @VisibleForTesting
- Point getLastSurfaceSize() {
- return mLastSurfaceSize;
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- super.onDisplayChanged(dc);
- updateSurfaceBounds();
- }
-
- /**
- * Determines the stack and task bounds of the other stack when in docked mode. The current task
- * bounds is passed in but depending on the stack, the task and stack must match. Only in
- * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds
- * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds
- * is calculated and is also used for its task bounds.
- * If any of the out bounds are empty, it represents default bounds
- *
- * @param currentTempTaskBounds the current task bounds of the other stack
- * @param outStackBounds the calculated stack bounds of the other stack
- * @param outTempTaskBounds the calculated task bounds of the other stack
- */
- void getStackDockedModeBounds(Rect dockedBounds,
- Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) {
- final Configuration parentConfig = getParent().getConfiguration();
- outTempTaskBounds.setEmpty();
-
- if (dockedBounds == null || dockedBounds.isEmpty()) {
- // Calculate the primary docked bounds.
- final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
- == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- getStackDockedModeBounds(parentConfig,
- true /* primary */, outStackBounds, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- return;
- }
- final int dockedSide = getDockSide(parentConfig, dockedBounds);
-
- // When the home stack is resizable, should always have the same stack and task bounds
- if (isActivityTypeHome()) {
- final Task homeTask = findHomeTask();
- if (homeTask == null || homeTask.isResizeable()) {
- // Calculate the home stack bounds when in docked mode and the home stack is
- // resizeable.
- getDisplayContent().mDividerControllerLocked
- .getHomeStackBoundsInDockedMode(parentConfig,
- dockedSide, outStackBounds);
- } else {
- // Home stack isn't resizeable, so don't specify stack bounds.
- outStackBounds.setEmpty();
- }
-
- outTempTaskBounds.set(outStackBounds);
- return;
- }
-
- // When minimized state, the stack bounds for all non-home and docked stack bounds should
- // match the passed task bounds
- if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) {
- outStackBounds.set(currentTempTaskBounds);
- return;
- }
-
- if (dockedSide == DOCKED_INVALID) {
- // Not sure how you got here...Only thing we can do is return current bounds.
- Slog.e(TAG_WM, "Failed to get valid docked side for docked stack");
- outStackBounds.set(getRawBounds());
- return;
- }
-
- final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
- getStackDockedModeBounds(parentConfig,
- false /* primary */, outStackBounds, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- }
-
- /**
- * Outputs the bounds a stack should be given the presence of a docked stack on the display.
- * @param parentConfig The parent configuration.
- * @param primary {@code true} if getting the primary stack bounds.
- * @param outBounds Output bounds that should be used for the stack.
- * @param dockedBounds Bounds of the docked stack.
- * @param dockDividerWidth We need to know the width of the divider make to the output bounds
- * close to the side of the dock.
- * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
- */
- private void getStackDockedModeBounds(Configuration parentConfig, boolean primary,
- Rect outBounds, Rect dockedBounds, int dockDividerWidth,
- boolean dockOnTopOrLeft) {
- final Rect displayRect = parentConfig.windowConfiguration.getBounds();
- final boolean splitHorizontally = displayRect.width() > displayRect.height();
-
- outBounds.set(displayRect);
- if (primary) {
- if (mWmService.mDockedStackCreateBounds != null) {
- outBounds.set(mWmService.mDockedStackCreateBounds);
- return;
- }
-
- // The initial bounds of the docked stack when it is created about half the screen space
- // and its bounds can be adjusted after that. The bounds of all other stacks are
- // adjusted to occupy whatever screen space the docked stack isn't occupying.
- final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(
- parentConfig.windowConfiguration.getRotation(),
- displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
- final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
- displayRect.width(),
- displayRect.height(),
- dockDividerWidth,
- parentConfig.orientation == ORIENTATION_PORTRAIT,
- mTmpRect2).getMiddleTarget().position;
-
- if (dockOnTopOrLeft) {
- if (splitHorizontally) {
- outBounds.right = position;
- } else {
- outBounds.bottom = position;
- }
- } else {
- if (splitHorizontally) {
- outBounds.left = position + dockDividerWidth;
- } else {
- outBounds.top = position + dockDividerWidth;
- }
- }
- return;
- }
-
- // Other stacks occupy whatever space is left by the docked stack.
- if (!dockOnTopOrLeft) {
- if (splitHorizontally) {
- outBounds.right = dockedBounds.left - dockDividerWidth;
- } else {
- outBounds.bottom = dockedBounds.top - dockDividerWidth;
- }
- } else {
- if (splitHorizontally) {
- outBounds.left = dockedBounds.right + dockDividerWidth;
- } else {
- outBounds.top = dockedBounds.bottom + dockDividerWidth;
- }
- }
- DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
- }
-
- void resetDockedStackToMiddle() {
- if (!inSplitScreenPrimaryWindowingMode()) {
- throw new IllegalStateException("Not a docked stack=" + this);
- }
-
- mWmService.mDockedStackCreateBounds = null;
-
- final Rect bounds = new Rect();
- final Rect tempBounds = new Rect();
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- bounds, tempBounds);
- // TODO(stack-merge): remove cast.
- ((ActivityStack) this).mStackSupervisor.resizeDockedStackLocked(bounds,
- null /* tempTaskBounds */,
- null /* tempTaskInsetBounds */, null /* tempOtherTaskBounds */,
- null /* tempOtherTaskInsetBounds */, false /* preserveWindows */,
- false /* deferResume */);
- }
-
- @Override
- void removeIfPossible() {
- if (isAnimating(TRANSITION | CHILDREN)) {
- mDeferRemoval = true;
- return;
- }
- removeImmediately();
- }
-
- @Override
- // TODO(stack-merge): This is mostly taking care of the case where the stask is removing from
- // the display, so we should probably consolidate it there instead.
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- super.onParentChanged(newParent, oldParent);
-
- if (getParent() != null || mDisplayContent == null) {
- return;
- }
-
- EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
-
- mDisplayContent = null;
- mWmService.mWindowPlacerLocked.requestTraversal();
- }
-
- /**
- * Adjusts the stack bounds if the IME is visible.
- *
- * @param imeWin The IME window.
- * @param keepLastAmount Use {@code true} to keep the last adjusted amount from
- * {@link DockedStackDividerController} for adjusting the stack bounds,
- * Use {@code false} to reset adjusted amount as 0.
- * @see #updateAdjustForIme(float, float, boolean)
- */
- void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) {
- mImeWin = imeWin;
- mImeGoingAway = false;
- if (!mAdjustedForIme || keepLastAmount) {
- mAdjustedForIme = true;
- DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked;
- final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f;
- final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f;
- updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */);
- }
- }
-
- boolean isAdjustedForIme() {
- return mAdjustedForIme;
- }
-
- boolean isAnimatingForIme() {
- return mImeWin != null && mImeWin.isAnimatingLw();
- }
-
- /**
- * Update the stack's bounds (crop or position) according to the IME window's
- * current position. When IME window is animated, the bottom stack is animated
- * together to track the IME window's current position, and the top stack is
- * cropped as necessary.
- *
- * @return true if a traversal should be performed after the adjustment.
- */
- boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) {
- if (adjustAmount != mAdjustImeAmount
- || adjustDividerAmount != mAdjustDividerAmount || force) {
- mAdjustImeAmount = adjustAmount;
- mAdjustDividerAmount = adjustDividerAmount;
- updateAdjustedBounds();
- return isVisible();
- } else {
- return false;
- }
- }
-
- /**
- * Resets the adjustment after it got adjusted for the IME.
- * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
- * animations; otherwise, set flag and animates the window away together
- * with IME window.
- */
- void resetAdjustedForIme(boolean adjustBoundsNow) {
- if (adjustBoundsNow) {
- mImeWin = null;
- mImeGoingAway = false;
- mAdjustImeAmount = 0f;
- mAdjustDividerAmount = 0f;
- if (!mAdjustedForIme) {
- return;
- }
- mAdjustedForIme = false;
- updateAdjustedBounds();
- mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
- } else {
- mImeGoingAway |= mAdjustedForIme;
- }
- }
-
- /**
- * Sets the amount how much we currently minimize our stack.
- *
- * @param minimizeAmount The amount, between 0 and 1.
- * @return Whether the amount has changed and a layout is needed.
- */
- boolean setAdjustedForMinimizedDock(float minimizeAmount) {
- if (minimizeAmount != mMinimizeAmount) {
- mMinimizeAmount = minimizeAmount;
- updateAdjustedBounds();
- return isVisible();
- } else {
- return false;
- }
- }
-
- boolean shouldIgnoreInput() {
- return isAdjustedForMinimizedDockedStack() ||
- (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
- }
-
- /**
- * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
- * to the list of to be drawn windows the service is waiting for.
- */
- void beginImeAdjustAnimation() {
- for (int j = mChildren.size() - 1; j >= 0; j--) {
- final Task task = mChildren.get(j);
- if (task.hasContentToDisplay()) {
- task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- task.setWaitingForDrawnIfResizingChanged();
- }
- }
- }
-
- /**
- * Resets the resizing state of all windows.
- */
- void endImeAdjustAnimation() {
- for (int j = mChildren.size() - 1; j >= 0; j--) {
- mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }
- }
-
- int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
- return displayContentRect.top + (int)
- ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
- }
-
- private boolean adjustForIME(final WindowState imeWin) {
- // To prevent task stack resize animation may flicking when playing app transition
- // animation & IME window enter animation in parallel, we need to make sure app
- // transition is done and then adjust task size for IME, skip the new adjusted frame when
- // app transition is still running.
- if (getDisplayContent().mAppTransition.isRunning()) {
- return false;
- }
-
- final int dockedSide = getDockSide();
- final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
- if (imeWin == null || !dockedTopOrBottom) {
- return false;
- }
-
- final Rect displayStableRect = mTmpRect;
- final Rect contentBounds = mTmpRect2;
-
- // Calculate the content bounds excluding the area occupied by IME
- getDisplayContent().getStableRect(displayStableRect);
- contentBounds.set(displayStableRect);
- int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
-
- imeTop += imeWin.getGivenContentInsetsLw().top;
- if (contentBounds.bottom > imeTop) {
- contentBounds.bottom = imeTop;
- }
-
- final int yOffset = displayStableRect.bottom - contentBounds.bottom;
-
- final int dividerWidth =
- getDisplayContent().mDividerControllerLocked.getContentWidth();
- final int dividerWidthInactive =
- getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
-
- if (dockedSide == DOCKED_TOP) {
- // If this stack is docked on top, we make it smaller so the bottom stack is not
- // occluded by IME. We shift its bottom up by the height of the IME, but
- // leaves at least 30% of the top stack visible.
- final int minTopStackBottom =
- getMinTopStackBottom(displayStableRect, getRawBounds().bottom);
- final int bottom = Math.max(
- getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive,
- minTopStackBottom);
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount)
- * getRawBounds().bottom);
- mFullyAdjustedImeBounds.set(getRawBounds());
- } else {
- // When the stack is on bottom and has no focus, it's only adjusted for divider width.
- final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
-
- // When the stack is on bottom and has focus, it needs to be moved up so as to
- // not occluded by IME, and at the same time adjusted for divider width.
- // We try to move it up by the height of the IME window, but only to the extent
- // that leaves at least 30% of the top stack visible.
- // 'top' is where the top of bottom stack will move to in this case.
- final int topBeforeImeAdjust =
- getRawBounds().top - dividerWidth + dividerWidthInactive;
- final int minTopStackBottom =
- getMinTopStackBottom(displayStableRect,
- getRawBounds().top - dividerWidth);
- final int top = Math.max(
- getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive);
-
- mTmpAdjustedBounds.set(getRawBounds());
- // Account for the adjustment for IME and divider width separately.
- // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
- // and dividerWidthDelta is due to divider width change only.
- mTmpAdjustedBounds.top = getRawBounds().top +
- (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) +
- mAdjustDividerAmount * dividerWidthDelta);
- mFullyAdjustedImeBounds.set(getRawBounds());
- mFullyAdjustedImeBounds.top = top;
- mFullyAdjustedImeBounds.bottom = top + getRawBounds().height();
- }
- return true;
- }
-
- private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
- final int dockSide = getDockSide();
- if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
- return false;
- }
-
- if (dockSide == DOCKED_TOP) {
- mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
- int topInset = mTmpRect.top;
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
- * getRawBounds().bottom);
- } else if (dockSide == DOCKED_LEFT) {
- mTmpAdjustedBounds.set(getRawBounds());
- final int width = getRawBounds().width();
- mTmpAdjustedBounds.right =
- (int) (minimizeAmount * mDockedStackMinimizeThickness
- + (1 - minimizeAmount) * getRawBounds().right);
- mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
- } else if (dockSide == DOCKED_RIGHT) {
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.left = (int) (minimizeAmount *
- (getRawBounds().right - mDockedStackMinimizeThickness)
- + (1 - minimizeAmount) * getRawBounds().left);
- }
- return true;
- }
-
- private boolean isMinimizedDockAndHomeStackResizable() {
- return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
- && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
- }
-
- /**
- * @return the distance in pixels how much the stack gets minimized from it's original size
- */
- int getMinimizeDistance() {
- final int dockSide = getDockSide();
- if (dockSide == DOCKED_INVALID) {
- return 0;
- }
-
- if (dockSide == DOCKED_TOP) {
- mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
- int topInset = mTmpRect.top;
- return getRawBounds().bottom - topInset;
- } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
- return getRawBounds().width() - mDockedStackMinimizeThickness;
- } else {
- return 0;
- }
- }
-
- /**
- * Updates the adjustment depending on it's current state.
- */
- private void updateAdjustedBounds() {
- boolean adjust = false;
- if (mMinimizeAmount != 0f) {
- adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
- } else if (mAdjustedForIme) {
- adjust = adjustForIME(mImeWin);
- }
- if (!adjust) {
- mTmpAdjustedBounds.setEmpty();
- }
- setAdjustedBounds(mTmpAdjustedBounds);
-
- final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
- if (mAdjustedForIme && adjust && !isImeTarget) {
- final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
- * IME_ADJUST_DIM_AMOUNT;
- mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
- }
- }
-
- void applyAdjustForImeIfNeeded(Task task) {
- if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) {
- return;
- }
-
- final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds;
- task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
- mDisplayContent.setLayoutNeeded();
- }
-
-
- boolean isAdjustedForMinimizedDockedStack() {
- return mMinimizeAmount != 0f;
- }
-
- /**
- * @return {@code true} if we have a {@link Task} that is animating (currently only used for the
- * recents animation); {@code false} otherwise.
- */
- boolean isTaskAnimating() {
- for (int j = mChildren.size() - 1; j >= 0; j--) {
- final Task task = mChildren.get(j);
- if (task.isTaskAnimating()) {
- return true;
- }
- }
- return false;
- }
-
- // TODO(proto-merge): Remove once protos for ActivityStack and TaskStack are merged.
- void writeToProtoInnerStackOnly(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
- return;
- }
-
- final long token = proto.start(fieldId);
- super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
- proto.write(ID, mStackId);
- for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
- mChildren.get(taskNdx).writeToProtoInnerTaskOnly(proto, TASKS, logLevel);
- }
- proto.write(FILLS_PARENT, matchParentBounds());
- getRawBounds().writeToProto(proto, BOUNDS);
- proto.write(DEFER_REMOVAL, mDeferRemoval);
- proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
- proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
- proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
- proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
- mAdjustedBounds.writeToProto(proto, ADJUSTED_BOUNDS);
- proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
- proto.end(token);
- }
-
- @Override
- void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- pw.println(prefix + "mStackId=" + mStackId);
- pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
- pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
- if (mMinimizeAmount != 0f) {
- pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
- }
- if (mAdjustedForIme) {
- pw.println(prefix + "mAdjustedForIme=true");
- pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
- pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
- }
- if (!mAdjustedBounds.isEmpty()) {
- pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
- }
- for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
- mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
- }
- if (!mExitingActivities.isEmpty()) {
- pw.println();
- pw.println(" Exiting application tokens:");
- for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
- WindowToken token = mExitingActivities.get(i);
- pw.print(" Exiting App #"); pw.print(i);
- pw.print(' '); pw.print(token);
- pw.println(':');
- token.dump(pw, " ", dumpAll);
- }
- }
- mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
- }
-
- @Override
- boolean fillsParent() {
- return matchParentBounds();
- }
-
- String getName() {
- return toShortString();
- }
-
- public String toShortString() {
- return "Stack=" + mStackId;
- }
-
- /**
- * For docked workspace (or workspace that's side-by-side to the docked), provides
- * information which side of the screen was the dock anchored.
- */
- int getDockSide() {
- return getDockSide(mDisplayContent.getConfiguration(), getRawBounds());
- }
-
- int getDockSideForDisplay(DisplayContent dc) {
- return getDockSide(dc, dc.getConfiguration(), getRawBounds());
- }
-
- int getDockSide(Configuration parentConfig, Rect bounds) {
- if (mDisplayContent == null) {
- return DOCKED_INVALID;
- }
- return getDockSide(mDisplayContent, parentConfig, bounds);
- }
-
- private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) {
- return dc.getDockedDividerController().getDockSide(bounds,
- parentConfig.windowConfiguration.getBounds(),
- parentConfig.orientation, parentConfig.windowConfiguration.getRotation());
- }
-
- boolean hasTaskForUser(int userId) {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final Task task = mChildren.get(i);
- if (task.mUserId == userId) {
- return true;
- }
- }
- return false;
- }
-
- void findTaskForResizePoint(int x, int y, int delta,
- DisplayContent.TaskForResizePointSearchResult results) {
- if (!getWindowConfiguration().canResizeTask()) {
- results.searchDone = true;
- return;
- }
-
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final Task task = mChildren.get(i);
- if (task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
- results.searchDone = true;
- return;
- }
-
- // We need to use the task's dim bounds (which is derived from the visible bounds of
- // its apps windows) for any touch-related tests. Can't use the task's original
- // bounds because it might be adjusted to fit the content frame. One example is when
- // the task is put to top-left quadrant, the actual visible area would not start at
- // (0,0) after it's adjusted for the status bar.
- task.getDimBounds(mTmpRect);
- mTmpRect.inset(-delta, -delta);
- if (mTmpRect.contains(x, y)) {
- mTmpRect.inset(delta, delta);
-
- results.searchDone = true;
-
- if (!mTmpRect.contains(x, y)) {
- results.taskForResize = task;
- return;
- }
- // User touched inside the task. No need to look further,
- // focus transfer will be handled in ACTION_UP.
- return;
- }
- }
- }
-
- void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion,
- Rect contentRect, Rect postExclude) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final Task task = mChildren.get(i);
- ActivityRecord topVisibleActivity = task.getTopVisibleActivity();
- if (topVisibleActivity == null || !topVisibleActivity.hasContentToDisplay()) {
- continue;
- }
-
- /**
- * Exclusion region is the region that TapDetector doesn't care about.
- * Here we want to remove all non-focused tasks from the exclusion region.
- * We also remove the outside touch area for resizing for all freeform
- * tasks (including the focused).
- *
- * We save the focused task region once we find it, and add it back at the end.
- *
- * If the task is home stack and it is resizable in the minimized state, we want to
- * exclude the docked stack from touch so we need the entire screen area and not just a
- * small portion which the home stack currently is resized to.
- */
-
- if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) {
- mDisplayContent.getBounds(mTmpRect);
- } else {
- task.getDimBounds(mTmpRect);
- }
-
- if (task == focusedTask) {
- // Add the focused task rect back into the exclude region once we are done
- // processing stacks.
- postExclude.set(mTmpRect);
- }
-
- final boolean isFreeformed = task.inFreeformWindowingMode();
- if (task != focusedTask || isFreeformed) {
- if (isFreeformed) {
- // If the task is freeformed, enlarge the area to account for outside
- // touch area for resize.
- mTmpRect.inset(-delta, -delta);
- // Intersect with display content rect. If we have system decor (status bar/
- // navigation bar), we want to exclude that from the tap detection.
- // Otherwise, if the app is partially placed under some system button (eg.
- // Recents, Home), pressing that button would cause a full series of
- // unwanted transfer focus/resume/pause, before we could go home.
- mTmpRect.intersect(contentRect);
- }
- touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
- }
- }
- }
-
- public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
- // Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mWmService.mGlobalLock) {
- if (mCancelCurrentBoundsAnimation) {
- return false;
- }
- }
-
- try {
- mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
- } catch (RemoteException e) {
- // I don't believe you.
- }
- return true;
- }
-
- void onAllWindowsDrawn() {
- if (!mBoundsAnimating && !mBoundsAnimatingRequested) {
- return;
- }
-
- getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn();
- }
-
- @Override // AnimatesBounds
- public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
- @BoundsAnimationController.AnimationType int animationType) {
- // Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mWmService.mGlobalLock) {
- if (!isAttached()) {
- // Don't run the animation if the stack is already detached
- return false;
- }
-
- if (animationType == BoundsAnimationController.BOUNDS) {
- mBoundsAnimatingRequested = false;
- mBoundsAnimating = true;
- }
- mAnimationType = animationType;
-
- // If we are changing UI mode, as in the PiP to fullscreen
- // transition, then we need to wait for the window to draw.
- if (schedulePipModeChangedCallback) {
- forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); },
- false /* traverseTopToBottom */);
- }
- }
-
- if (inPinnedWindowingMode()) {
- try {
- mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
- } catch (RemoteException e) {
- // I don't believe you...
- }
-
- if ((schedulePipModeChangedCallback || animationType == FADE_IN)) {
- // We need to schedule the PiP mode change before the animation up. It is possible
- // in this case for the animation down to not have been completed, so always
- // force-schedule and update to the client to ensure that it is notified that it
- // is no longer in picture-in-picture mode
- // TODO(stack-merge): Remove cast.
- ((ActivityStack) this).updatePictureInPictureModeForPinnedStackAnimation(null,
- forceUpdate);
- }
- }
- return true;
- }
-
- @Override // AnimatesBounds
- public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
- boolean moveToFullscreen) {
- synchronized (mWmService.mGlobalLock) {
- if (inPinnedWindowingMode()) {
- // Update to the final bounds if requested. This is done here instead of in the
- // bounds animator to allow us to coordinate this after we notify the PiP mode changed
-
- if (schedulePipModeChangedCallback) {
- // We need to schedule the PiP mode change after the animation down, so use the
- // final bounds
- // TODO(stack-merge): Remove cast.
- ((ActivityStack) this).updatePictureInPictureModeForPinnedStackAnimation(
- mBoundsAnimationTarget, false /* forceUpdate */);
- }
-
- if (mAnimationType == BoundsAnimationController.FADE_IN) {
- setPinnedStackAlpha(1f);
- mWmService.mAtmService.notifyPinnedStackAnimationEnded();
- return;
- }
-
- if (finalStackSize != null && !mCancelCurrentBoundsAnimation) {
- setPinnedStackSize(finalStackSize, null);
- } else {
- // We have been canceled, so the final stack size is null, still run the
- // animation-end logic
- onPipAnimationEndResize();
- }
-
- mWmService.mAtmService.notifyPinnedStackAnimationEnded();
- if (moveToFullscreen) {
- ((ActivityStack) this).dismissPip();
- }
- } else {
- // No PiP animation, just run the normal animation-end logic
- onPipAnimationEndResize();
- }
- }
- }
-
- /**
- * Animates the pinned stack.
- */
- void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
- int animationDuration, boolean fromFullscreen) {
- if (!inPinnedWindowingMode()) {
- return;
- }
- // Get the from-bounds
- final Rect fromBounds = new Rect();
- getBounds(fromBounds);
-
- // Get non-null fullscreen to-bounds for animating if the bounds are null
- @SchedulePipModeChangedState int schedulePipModeChangedState =
- NO_PIP_MODE_CHANGED_CALLBACKS;
- final boolean toFullscreen = toBounds == null;
- if (toFullscreen) {
- if (fromFullscreen) {
- throw new IllegalArgumentException("Should not defer scheduling PiP mode"
- + " change on animation to fullscreen.");
- }
- schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
-
- mWmService.getStackBounds(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
- if (!mTmpToBounds.isEmpty()) {
- // If there is a fullscreen bounds, use that
- toBounds = new Rect(mTmpToBounds);
- } else {
- // Otherwise, use the display bounds
- toBounds = new Rect();
- getDisplayContent().getBounds(toBounds);
- }
- } else if (fromFullscreen) {
- schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
- }
-
- setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
-
- final Rect finalToBounds = toBounds;
- final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
- schedulePipModeChangedState;
- final DisplayContent displayContent = getDisplayContent();
- @BoundsAnimationController.AnimationType int intendedAnimationType =
- displayContent.mBoundsAnimationController.getAnimationType();
- if (intendedAnimationType == FADE_IN) {
- if (fromFullscreen) {
- setPinnedStackAlpha(0f);
- }
- if (toBounds.width() == fromBounds.width()
- && toBounds.height() == fromBounds.height()) {
- intendedAnimationType = BoundsAnimationController.BOUNDS;
- } else if (!fromFullscreen && !toBounds.equals(fromBounds)) {
- // intendedAnimationType may have been reset at the end of RecentsAnimation,
- // force it to BOUNDS type if we know for certain we're animating to
- // a different bounds, especially for expand and collapse of PiP window.
- intendedAnimationType = BoundsAnimationController.BOUNDS;
- }
- }
-
- final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
- mCancelCurrentBoundsAnimation = false;
- displayContent.mBoundsAnimationController.getHandler().post(() -> {
- displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
- finalToBounds, animationDuration, finalSchedulePipModeChangedState,
- fromFullscreen, toFullscreen, animationType);
- });
- }
-
- /**
- * Sets the current picture-in-picture aspect ratio.
- */
- void setPictureInPictureAspectRatio(float aspectRatio) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return;
- }
-
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent == null) {
- return;
- }
-
- if (!inPinnedWindowingMode()) {
- return;
- }
-
- final PinnedStackController pinnedStackController =
- getDisplayContent().getPinnedStackController();
-
- if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
- return;
- }
-
- // Notify the pinned stack controller about aspect ratio change.
- // This would result a callback delivered from SystemUI to WM to start animation,
- // if the bounds are ought to be altered due to aspect ratio change.
- pinnedStackController.setAspectRatio(
- pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
- ? aspectRatio : -1f);
- }
-
- /**
- * Sets the current picture-in-picture actions.
- */
- void setPictureInPictureActions(List<RemoteAction> actions) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return;
- }
-
- if (!inPinnedWindowingMode()) {
- return;
- }
-
- getDisplayContent().getPinnedStackController().setActions(actions);
- }
-
- /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
- void onPipAnimationEndResize() {
- mBoundsAnimating = false;
- for (int i = 0; i < mChildren.size(); i++) {
- final Task t = mChildren.get(i);
- t.clearPreserveNonFloatingState();
- }
- mWmService.requestTraversal();
- }
-
- @Override
- public boolean shouldDeferStartOnMoveToFullscreen() {
- synchronized (mWmService.mGlobalLock) {
- if (!isAttached()) {
- // Unnecessary to pause the animation because the stack is detached.
- return false;
- }
-
- // Workaround for the recents animation -- normally we need to wait for the new activity
- // to show before starting the PiP animation, but because we start and show the home
- // activity early for the recents animation prior to the PiP animation starting, there
- // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
- // stack is already visible and drawn.
- final TaskStack homeStack = mDisplayContent.getHomeStack();
- if (homeStack == null) {
- return true;
- }
- final Task homeTask = homeStack.getTopChild();
- if (homeTask == null) {
- return true;
- }
- final ActivityRecord homeApp = homeTask.getTopVisibleActivity();
- if (!homeTask.isVisible() || homeApp == null) {
- return true;
- }
- return !homeApp.allDrawn;
- }
- }
-
- /**
- * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
- * bounds and we have a deferred PiP mode changed callback set with the animation.
- */
- public boolean deferScheduleMultiWindowModeChanged() {
- if (inPinnedWindowingMode()) {
- // For the pinned stack, the deferring of the multi-window mode changed is tied to the
- // transition animation into picture-in-picture, and is called once the animation
- // completes, or is interrupted in a way that would leave the stack in a non-fullscreen
- // state.
- // @see BoundsAnimationController
- // @see BoundsAnimationControllerTests
- return (mBoundsAnimatingRequested || mBoundsAnimating);
- }
- return false;
- }
-
- public boolean isForceScaled() {
- return mBoundsAnimating;
- }
-
- public boolean isAnimatingBounds() {
- return mBoundsAnimating;
- }
-
- public boolean lastAnimatingBoundsWasToFullscreen() {
- return mBoundsAnimatingToFullscreen;
- }
-
- public boolean isAnimatingBoundsToFullscreen() {
- return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen();
- }
-
- public boolean pinnedStackResizeDisallowed() {
- if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
- return true;
- }
- return false;
- }
-
- /** Returns true if a removal action is still being deferred. */
- boolean checkCompleteDeferredRemoval() {
- if (isAnimating(TRANSITION | CHILDREN)) {
- return true;
- }
- if (mDeferRemoval) {
- removeImmediately();
- }
-
- return super.checkCompleteDeferredRemoval();
- }
-
- @Override
- int getOrientation() {
- return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
- }
-
- private boolean canSpecifyOrientation() {
- final int windowingMode = getWindowingMode();
- final int activityType = getActivityType();
- return windowingMode == WINDOWING_MODE_FULLSCREEN
- || activityType == ACTIVITY_TYPE_HOME
- || activityType == ACTIVITY_TYPE_RECENTS
- || activityType == ACTIVITY_TYPE_ASSISTANT;
- }
-
- @Override
- Dimmer getDimmer() {
- return mDimmer;
- }
-
- @Override
- void prepareSurfaces() {
- mDimmer.resetDimStates();
- super.prepareSurfaces();
- getDimBounds(mTmpDimBoundsRect);
-
- // Bounds need to be relative, as the dim layer is a child.
- mTmpDimBoundsRect.offsetTo(0, 0);
- if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
- scheduleAnimation();
- }
- }
-
- @Override
- public boolean setPinnedStackAlpha(float alpha) {
- // Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mWmService.mGlobalLock) {
- final SurfaceControl sc = getSurfaceControl();
- if (sc == null || !sc.isValid()) {
- // If the stack is already removed, don't bother updating any stack animation
- return false;
- }
- getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
- scheduleAnimation();
- return !mCancelCurrentBoundsAnimation;
- }
- }
-
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- void dim(float alpha) {
- mDimmer.dimAbove(getPendingTransaction(), alpha);
- scheduleAnimation();
- }
-
- void stopDimming() {
- mDimmer.stopDim(getPendingTransaction());
- scheduleAnimation();
- }
-
- AnimatingActivityRegistry getAnimatingActivityRegistry() {
- return mAnimatingActivityRegistry;
- }
-
- @Override
- void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
- Rect outSurfaceInsets) {
- final Task task = getTopChild();
- if (task != null) {
- task.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
- } else {
- super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
- }
- }
-
- @Override
- RemoteAnimationTarget createRemoteAnimationTarget(
- RemoteAnimationController.RemoteAnimationRecord record) {
- final Task task = getTopChild();
- return task != null ? task.createRemoteAnimationTarget(record) : null;
- }
-}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6ff4b2e504f1..3632284fdeb6 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -120,7 +120,7 @@ class WallpaperController {
}
mFindResults.resetTopWallpaper = true;
- if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
+ if (w.mActivityRecord != null && w.mActivityRecord.isHidden()
&& !w.mActivityRecord.isAnimating(TRANSITION)) {
// If this window's app token is hidden and not animating, it is of no interest to us.
@@ -278,11 +278,9 @@ class WallpaperController {
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
token.hideWallpaperToken(wasDeferred, "hideWallpapers");
- if (DEBUG_WALLPAPER_LIGHT && token.isVisible()) {
- Slog.d(TAG, "Hiding wallpaper " + token
- + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
- + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, " "));
- }
+ if (DEBUG_WALLPAPER_LIGHT && !token.isHidden()) Slog.d(TAG, "Hiding wallpaper " + token
+ + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
+ + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, " "));
}
}
@@ -534,9 +532,9 @@ class WallpaperController {
}
final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null
- && !wallpaperTarget.mActivityRecord.mVisibleRequested;
+ && wallpaperTarget.mActivityRecord.hiddenRequested;
final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null
- && !prevWallpaperTarget.mActivityRecord.mVisibleRequested;
+ && prevWallpaperTarget.mActivityRecord.hiddenRequested;
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
+ prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index d23bf978cbab..528cece9a78b 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -56,6 +56,7 @@ class WallpaperWindowToken extends WindowToken {
final WindowState wallpaper = mChildren.get(j);
wallpaper.hideWallpaperWindow(wasDeferred, reason);
}
+ setHidden(true);
}
void sendWindowWallpaperCommand(
@@ -87,7 +88,9 @@ class WallpaperWindowToken extends WindowToken {
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
- if (isVisible() != visible) {
+ if (isHidden() == visible) {
+ setHidden(!visible);
+
// Need to do a layout to ensure the wallpaper now has the correct size.
mDisplayContent.setLayoutNeeded();
}
@@ -115,9 +118,10 @@ class WallpaperWindowToken extends WindowToken {
void updateWallpaperWindows(boolean visible) {
- if (isVisible() != visible) {
+ if (isHidden() == visible) {
if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
- "Wallpaper token " + token + " visible=" + visible);
+ "Wallpaper token " + token + " hidden=" + !visible);
+ setHidden(!visible);
// Need to do a layout to ensure the wallpaper now has the correct size.
mDisplayContent.setLayoutNeeded();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6fed2cbf03be..3e78d5ef0f65 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -92,16 +92,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
- /** Animation layer that happens above all animating {@link TaskStack}s. */
+ /** Animation layer that happens above all animating {@link ActivityStack}s. */
static final int ANIMATION_LAYER_STANDARD = 0;
- /** Animation layer that happens above all {@link TaskStack}s. */
+ /** Animation layer that happens above all {@link ActivityStack}s. */
static final int ANIMATION_LAYER_BOOSTED = 1;
/**
* Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
* activities and all activities that are being controlled by the recents animation. This
- * layer is generally below all {@link TaskStack}s.
+ * layer is generally below all {@link ActivityStack}s.
*/
static final int ANIMATION_LAYER_HOME = 2;
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 70fc19470f83..84fcfbd43d32 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -57,7 +57,7 @@ public class WindowFrames {
public final Rect mParentFrame = new Rect();
/**
- * The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
+ * The entire screen area of the {@link ActivityStack} this window is in. Usually equal to the
* screen area of the device.
*
* TODO(b/111611553): The name is unclear and most likely should be swapped with
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6e4f1ee29a64..519cc2165cca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1702,7 +1702,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
- if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
+ if (win.mActivityRecord == null || !win.mActivityRecord.isClientHidden()) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
@@ -1934,7 +1934,7 @@ public class WindowManagerService extends IWindowManager.Stub
// re-factor.
activity.firstWindowDrawn = false;
activity.clearAllDrawn();
- final TaskStack stack = activity.getStack();
+ final ActivityStack stack = activity.getStack();
if (stack != null) {
stack.mExitingActivities.remove(activity);
}
@@ -2235,7 +2235,7 @@ public class WindowManagerService extends IWindowManager.Stub
// associated appToken is not hidden.
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
- || win.mActivityRecord.isClientVisible());
+ || !win.mActivityRecord.isClientHidden());
// If we are not currently running the exit animation, we need to see about starting
// one.
@@ -2763,7 +2763,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void getStackBounds(int windowingMode, int activityType, Rect bounds) {
synchronized (mGlobalLock) {
- final TaskStack stack = mRoot.getStack(windowingMode, activityType);
+ final ActivityStack stack = mRoot.getStack(windowingMode, activityType);
if (stack != null) {
stack.getBounds(bounds);
return;
@@ -3217,7 +3217,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Notify whether the docked stack exists for the current user
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final TaskStack stack =
+ final ActivityStack stack =
displayContent.getSplitScreenPrimaryStackIgnoringVisibility();
displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
stack != null && stack.hasTaskForUser(newUserId));
@@ -4368,7 +4368,7 @@ public class WindowManagerService extends IWindowManager.Stub
return mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
- TaskStack getImeFocusStackLocked() {
+ ActivityStack getImeFocusStackLocked() {
// Don't use mCurrentFocus.getStack() because it returns home stack for system windows.
// Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
// and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved
@@ -6388,7 +6388,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public int getDockedStackSide() {
synchronized (mGlobalLock) {
- final TaskStack dockedStack = getDefaultDisplayContentLocked()
+ final ActivityStack dockedStack = getDefaultDisplayContentLocked()
.getSplitScreenPrimaryStackIgnoringVisibility();
return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
}
@@ -7642,7 +7642,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- final TaskStack stack = task.getTaskStack();
+ final ActivityStack stack = task.getTaskStack();
// We ignore home stack since we don't want home stack to move to front when touched.
// Specifically, in freeform we don't want tapping on home to cause the freeform apps to go
// behind home. See b/117376413
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 8e955cf3a8bb..22183667832e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -20,19 +20,27 @@ import static android.os.Build.IS_USER;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
import android.util.DisplayMetrics;
+import android.util.Pair;
import android.view.Display;
import android.view.IWindowManager;
import android.view.Surface;
+import android.view.ViewDebug;
+import com.android.internal.os.ByteTransferPipe;
import com.android.server.protolog.ProtoLogImpl;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
/**
* ShellCommands for WindowManagerService.
@@ -81,6 +89,8 @@ public class WindowManagerShellCommand extends ShellCommand {
return runSetDisplayUserRotation(pw);
case "set-fix-to-user-rotation":
return runSetFixToUserRotation(pw);
+ case "dump-visible-window-views":
+ return runDumpVisibleWindowViews(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -341,6 +351,50 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runDumpVisibleWindowViews(PrintWriter pw) {
+ ParcelFileDescriptor outFile = openFileForSystem(getNextArgRequired(), "w");
+ try (ZipOutputStream out = new ZipOutputStream(
+ new ParcelFileDescriptor.AutoCloseOutputStream(outFile))) {
+ ArrayList<Pair<String, ByteTransferPipe>> requestList = new ArrayList<>();
+ synchronized (mInternal.mGlobalLock) {
+ // Request dump from all windows parallelly before writing to disk.
+ mInternal.mRoot.forAllWindows(w -> {
+ if (w.isVisible()) {
+ ByteTransferPipe pipe = null;
+ try {
+ pipe = new ByteTransferPipe();
+ w.mClient.executeCommand(ViewDebug.REMOTE_COMMAND_DUMP_ENCODED, null,
+ pipe.getWriteFd());
+ requestList.add(Pair.create(w.getName(), pipe));
+ } catch (IOException | RemoteException e) {
+ // Skip this window
+ pw.println("Error for window " + w.getName() + " : " + e.getMessage());
+ if (pipe != null) {
+ pipe.kill();
+ }
+ }
+ }
+ }, false /* traverseTopToBottom */);
+ }
+ for (Pair<String, ByteTransferPipe> entry : requestList) {
+ byte[] data;
+ try {
+ data = entry.second.get();
+ } catch (IOException e) {
+ // Ignore this window
+ pw.println(
+ "Error for window " + entry.first + " : " + e.getMessage());
+ continue;
+ }
+ out.putNextEntry(new ZipEntry(entry.first));
+ out.write(data);
+ }
+ } catch (IOException e) {
+ pw.println("Error fetching dump " + e.getMessage());
+ }
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -360,6 +414,8 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
pw.println(" Set user rotation mode and user rotation.");
+ pw.println(" dump-visible-window-views out-file");
+ pw.println(" Dumps the encoded view hierarchies of visible windows");
pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]");
pw.println(" Enable or disable rotating display for app requested orientation.");
if (!IS_USER) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2e188b7cc86d..d63fbc217e54 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -533,7 +533,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
synchronized (mAtm.mGlobalLockWithoutBoost) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
- if (r.mVisibleRequested) {
+ if (r.visible) {
return true;
}
}
@@ -555,7 +555,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
continue;
}
ActivityRecord topActivity = task.getTopActivity();
- if (topActivity != null && topActivity.mVisibleRequested) {
+ if (topActivity != null && topActivity.visible) {
return true;
}
}
@@ -589,7 +589,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// - no longer visible OR
// - not focusable (in PiP mode for instance)
if (topDisplay == null
- || !mPreQTopResumedActivity.mVisibleRequested
+ || !mPreQTopResumedActivity.visible
|| !mPreQTopResumedActivity.isFocusable()) {
canUpdate = true;
}
@@ -739,7 +739,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
// Don't consider any activities that are currently not in a state where they
// can be destroyed.
- if (r.mVisibleRequested || !r.stopped || !r.hasSavedState()
+ if (r.visible || !r.stopped || !r.hasSavedState()
|| r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
continue;
@@ -793,7 +793,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
continue;
}
}
- if (r.mVisibleRequested) {
+ if (r.visible) {
final Task task = r.getTask();
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b8168319dd29..f98e30748847 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1006,7 +1006,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
- final TaskStack stack = getStack();
+ final ActivityStack stack = getStack();
if (inPinnedWindowingMode() && stack != null
&& stack.lastAnimatingBoundsWasToFullscreen()) {
// PIP edge case: When going from pinned to fullscreen, we apply a
@@ -1404,7 +1404,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
- TaskStack getStack() {
+ ActivityStack getStack() {
Task task = getTask();
if (task != null) {
if (task.getTaskStack() != null) {
@@ -1427,7 +1427,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
bounds.setEmpty();
mTmpRect.setEmpty();
if (intersectWithStackBounds) {
- final TaskStack stack = task.getTaskStack();
+ final ActivityStack stack = task.getTaskStack();
if (stack != null) {
stack.getDimBounds(mTmpRect);
} else {
@@ -1539,7 +1539,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
// TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
boolean isWinVisibleLw() {
- return (mActivityRecord == null || mActivityRecord.mVisibleRequested
+ return (mActivityRecord == null || !mActivityRecord.hiddenRequested
|| mActivityRecord.isAnimating(TRANSITION)) && isVisible();
}
@@ -1548,7 +1548,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* not the pending requested hidden state.
*/
boolean isVisibleNow() {
- return (mToken.isVisible() || mAttrs.type == TYPE_APPLICATION_STARTING)
+ return (!mToken.isHidden() || mAttrs.type == TYPE_APPLICATION_STARTING)
&& isVisible();
}
@@ -1570,7 +1570,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final ActivityRecord atoken = mActivityRecord;
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& isVisibleByPolicy() && !isParentWindowHidden()
- && (atoken == null || atoken.mVisibleRequested)
+ && (atoken == null || !atoken.hiddenRequested)
&& !mAnimatingExit && !mDestroying;
}
@@ -1585,7 +1585,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
final ActivityRecord atoken = mActivityRecord;
if (atoken != null) {
- return ((!isParentWindowHidden() && atoken.mVisibleRequested)
+ return ((!isParentWindowHidden() && !atoken.hiddenRequested)
|| isAnimating(TRANSITION | PARENTS));
}
return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS);
@@ -1621,7 +1621,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
final boolean parentAndClientVisible = !isParentWindowHidden()
- && mViewVisibility == View.VISIBLE && mToken.isVisible();
+ && mViewVisibility == View.VISIBLE && !mToken.isHidden();
return mHasSurface && isVisibleByPolicy() && !mDestroying
&& (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
}
@@ -1640,7 +1640,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} else {
final Task task = getTask();
final boolean canFromTask = task != null && task.canAffectSystemUiFlags();
- return canFromTask && mActivityRecord.isVisible();
+ return canFromTask && !mActivityRecord.isHidden();
}
}
@@ -1652,7 +1652,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
public boolean isDisplayedLw() {
final ActivityRecord atoken = mActivityRecord;
return isDrawnLw() && isVisibleByPolicy()
- && ((!isParentWindowHidden() && (atoken == null || atoken.mVisibleRequested))
+ && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
|| isAnimating(TRANSITION | PARENTS));
}
@@ -1669,8 +1669,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final ActivityRecord atoken = mActivityRecord;
return mViewVisibility == View.GONE
|| !mRelayoutCalled
- || (atoken == null && !mToken.isVisible())
- || (atoken != null && !atoken.mVisibleRequested)
+ || (atoken == null && mToken.isHidden())
+ || (atoken != null && atoken.hiddenRequested)
|| isParentWindowGoneForLayout()
|| (mAnimatingExit && !isAnimatingLw())
|| mDestroying;
@@ -2135,7 +2135,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
- final TaskStack stack = getStack();
+ final ActivityStack stack = getStack();
if (stack != null && stack.shouldIgnoreInput()) {
// Ignore when the stack shouldn't receive input event.
// (i.e. the minimized stack in split screen mode.)
@@ -2162,8 +2162,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " parentHidden=" + isParentWindowHidden()
+ " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
if (mActivityRecord != null) {
- Slog.i(TAG_WM, " mActivityRecord.visibleRequested="
- + mActivityRecord.mVisibleRequested);
+ Slog.i(TAG_WM, " mActivityRecord.hiddenRequested=" + mActivityRecord.hiddenRequested);
}
}
}
@@ -2540,7 +2539,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// just in case they have the divider at an unstable position. Better
// also reset drag resizing state, because the owner can't do it
// anymore.
- final TaskStack stack =
+ final ActivityStack stack =
dc.getSplitScreenPrimaryStackIgnoringVisibility();
if (stack != null) {
stack.resetDockedStackToMiddle();
@@ -2574,7 +2573,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* interacts with it.
*/
private boolean shouldKeepVisibleDeadAppWindow() {
- if (!isWinVisibleLw() || mActivityRecord == null || !mActivityRecord.isClientVisible()) {
+ if (!isWinVisibleLw() || mActivityRecord == null || mActivityRecord.isClientHidden()) {
// Not a visible app window or the app isn't dead.
return false;
}
@@ -2611,14 +2610,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return showBecauseOfActivity || showBecauseOfWindow;
}
- /** @return {@code false} if this window desires touch events. */
+ /** @return false if this window desires touch events. */
boolean cantReceiveTouchInput() {
if (mActivityRecord == null || mActivityRecord.getTask() == null) {
return false;
}
return mActivityRecord.getTask().getTaskStack().shouldIgnoreInput()
- || !mActivityRecord.mVisibleRequested
+ || mActivityRecord.hiddenRequested
|| isAnimatingToRecents();
}
@@ -2886,13 +2885,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void sendAppVisibilityToClients() {
super.sendAppVisibilityToClients();
- final boolean clientVisible = mActivityRecord.isClientVisible();
- if (mAttrs.type == TYPE_APPLICATION_STARTING && !clientVisible) {
+ final boolean clientHidden = mActivityRecord.isClientHidden();
+ if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
// Don't hide the starting window.
return;
}
- if (!clientVisible) {
+ if (clientHidden) {
// Once we are notifying the client that it's visibility has changed, we need to prevent
// it from destroying child surfaces until the animation has finished. We do this by
// detaching any surface control the client added from the client.
@@ -2906,8 +2905,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
try {
if (DEBUG_VISIBILITY) Slog.v(TAG,
- "Setting visibility of " + this + ": " + clientVisible);
- mClient.dispatchAppVisibility(clientVisible);
+ "Setting visibility of " + this + ": " + (!clientHidden));
+ mClient.dispatchAppVisibility(!clientHidden);
} catch (RemoteException e) {
}
}
@@ -3137,7 +3136,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- final TaskStack stack = task.getTaskStack();
+ final ActivityStack stack = task.getTaskStack();
if (stack == null) {
return;
}
@@ -3151,7 +3150,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- final TaskStack stack = task.getTaskStack();
+ final ActivityStack stack = task.getTaskStack();
if (stack == null) {
return;
}
@@ -3400,7 +3399,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
private int getStackId() {
- final TaskStack stack = getStack();
+ final ActivityStack stack = getStack();
if (stack == null) {
return INVALID_STACK_ID;
}
@@ -3631,7 +3630,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- final TaskStack stack = getStack();
+ final ActivityStack stack = getStack();
pw.print(prefix + "mDisplayId=" + getDisplayId());
if (stack != null) {
pw.print(" stackId=" + stack.mStackId);
@@ -4147,9 +4146,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING)
+ " during animation: policyVis=" + isVisibleByPolicy()
+ " parentHidden=" + isParentWindowHidden()
- + " tok.visibleRequested="
- + (mActivityRecord != null && mActivityRecord.mVisibleRequested)
- + " tok.visible=" + (mActivityRecord != null && mActivityRecord.isVisible())
+ + " tok.hiddenRequested="
+ + (mActivityRecord != null && mActivityRecord.hiddenRequested)
+ + " tok.hidden=" + (mActivityRecord != null && mActivityRecord.isHidden())
+ " animating=" + isAnimating(TRANSITION | PARENTS)
+ " tok animating="
+ (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION))
@@ -4556,7 +4555,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " pv=" + isVisibleByPolicy()
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
- + " th=" + (mActivityRecord != null && mActivityRecord.mVisibleRequested)
+ + " th=" + (mActivityRecord != null ? mActivityRecord.hiddenRequested : false)
+ " a=" + isAnimating(TRANSITION | PARENTS));
}
}
@@ -5081,7 +5080,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
- TaskStack stack = getStack();
+ ActivityStack stack = getStack();
// If we have stack outsets, that means the top-left
// will be outset, and we need to inset ourselves
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a853828317c9..94aff7bd86a7 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1016,7 +1016,7 @@ class WindowStateAnimator {
mSurfaceController.deferTransactionUntil(mSurfaceController.mSurfaceControl,
mWin.getFrameNumber());
} else {
- final TaskStack stack = mWin.getStack();
+ final ActivityStack stack = mWin.getStack();
mTmpPos.x = 0;
mTmpPos.y = 0;
if (stack != null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 6480a15a4220..88a1458a783f 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -29,6 +29,7 @@ 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.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowTokenProto.HASH_CODE;
+import static com.android.server.wm.WindowTokenProto.HIDDEN;
import static com.android.server.wm.WindowTokenProto.PAUSED;
import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
import static com.android.server.wm.WindowTokenProto.WINDOWS;
@@ -71,6 +72,9 @@ class WindowToken extends WindowContainer<WindowState> {
// Is key dispatching paused for this token?
boolean paused = false;
+ // Should this token's windows be hidden?
+ private boolean mHidden;
+
// Temporary for finding which tokens no longer have visible windows.
boolean hasVisible;
@@ -124,6 +128,16 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
+ void setHidden(boolean hidden) {
+ if (hidden != mHidden) {
+ mHidden = hidden;
+ }
+ }
+
+ boolean isHidden() {
+ return mHidden;
+ }
+
void removeAllWindowsIfPossible() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState win = mChildren.get(i);
@@ -142,7 +156,7 @@ class WindowToken extends WindowContainer<WindowState> {
// This token is exiting, so allow it to be removed when it no longer contains any windows.
mPersistOnEmpty = false;
- if (!isVisible()) {
+ if (mHidden) {
return;
}
@@ -155,10 +169,7 @@ class WindowToken extends WindowContainer<WindowState> {
changed |= win.onSetAppExiting();
}
- final ActivityRecord app = asActivityRecord();
- if (app != null) {
- app.setVisible(false);
- }
+ setHidden(true);
if (changed) {
mWmService.mWindowPlacerLocked.performSurfacePlacement();
@@ -275,6 +286,7 @@ class WindowToken extends WindowContainer<WindowState> {
final WindowState w = mChildren.get(i);
w.writeToProto(proto, WINDOWS, logLevel);
}
+ proto.write(HIDDEN, mHidden);
proto.write(WAITING_TO_SHOW, waitingToShow);
proto.write(PAUSED, paused);
proto.end(token);
@@ -284,7 +296,8 @@ class WindowToken extends WindowContainer<WindowState> {
super.dump(pw, prefix, dumpAll);
pw.print(prefix); pw.print("windows="); pw.println(mChildren);
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
- pw.print(" hasVisible="); pw.println(hasVisible);
+ pw.print(" hidden="); pw.print(mHidden);
+ pw.print(" hasVisible="); pw.println(hasVisible);
if (waitingToShow || sendingToBottom) {
pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
pw.print(" sendingToBottom="); pw.print(sendingToBottom);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9dac03f633dd..99f484e2d90f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1945,7 +1945,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
TelephonyManager getTelephonyManager() {
- return TelephonyManager.from(mContext);
+ return mContext.getSystemService(TelephonyManager.class);
}
TrustManager getTrustManager() {
@@ -9724,13 +9724,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- int userInfoFlags = 0;
- if (ephemeral) {
- userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
- }
- if (demo) {
- userInfoFlags |= UserInfo.FLAG_DEMO;
- }
+ int userInfoFlags = ephemeral ? UserInfo.FLAG_EPHEMERAL : 0;
+ String userType = demo ? UserManager.USER_TYPE_FULL_DEMO
+ : UserManager.USER_TYPE_FULL_SECONDARY;
String[] disallowedPackages = null;
if (!leaveAllSystemAppsEnabled) {
disallowedPackages = mOverlayPackagesProvider.getNonRequiredApps(admin,
@@ -9738,7 +9734,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
new String[0]);
}
UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
- userInfoFlags, disallowedPackages);
+ userType, userInfoFlags, disallowedPackages);
if (userInfo != null) {
user = userInfo.getUserHandle();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 9c9730501a78..45de451e5bea 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -33,6 +33,8 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
+import static com.android.server.DeviceIdleController.MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR;
+import static com.android.server.DeviceIdleController.MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR;
import static com.android.server.DeviceIdleController.STATE_ACTIVE;
import static com.android.server.DeviceIdleController.STATE_IDLE;
import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -180,7 +182,9 @@ public class DeviceIdleControllerTest {
mHandler = controller.new MyHandler(getContext().getMainLooper());
spyOn(mHandler);
doNothing().when(mHandler).handleMessage(argThat((message) ->
- message.what != MSG_REPORT_STATIONARY_STATUS));
+ message.what != MSG_REPORT_STATIONARY_STATUS
+ && message.what != MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR
+ && message.what != MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR));
doAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
@@ -189,7 +193,9 @@ public class DeviceIdleControllerTest {
return true;
}
}).when(mHandler).sendMessageDelayed(
- argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS),
+ argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS
+ || message.what == MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR
+ || message.what == MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR),
anyLong());
}
@@ -1734,13 +1740,11 @@ public class DeviceIdleControllerTest {
}
//TODO(b/123045185): Mocked Handler of DeviceIdleController to make message loop
//workable in this test class
- mDeviceIdleController.updatePreIdleFactor();
float expectedfactor = mDeviceIdleController.getPreIdleTimeoutByMode(mode);
float curfactor = mDeviceIdleController.getPreIdleTimeoutFactor();
assertEquals("Pre idle time factor of mode [" + mode + "].",
expectedfactor, curfactor, delta);
mDeviceIdleController.resetPreIdleTimeoutMode();
- mDeviceIdleController.updatePreIdleFactor();
checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_INACTIVE);
checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_IDLE_PENDING);
@@ -2088,14 +2092,11 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK, ret);
}
if (ret == mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK) {
- mDeviceIdleController.updatePreIdleFactor();
long newAlarm = mDeviceIdleController.getNextAlarmTime();
long newDelay = (long) ((alarm - now) * factor);
assertTrue("setPreIdleTimeoutFactor: " + factor,
Math.abs(newDelay - (newAlarm - now)) < errorTolerance);
mDeviceIdleController.resetPreIdleTimeoutMode();
- mDeviceIdleController.updatePreIdleFactor();
- mDeviceIdleController.maybeDoImmediateMaintenance();
newAlarm = mDeviceIdleController.getNextAlarmTime();
assertTrue("resetPreIdleTimeoutMode from: " + factor,
Math.abs(newAlarm - alarm) < errorTolerance);
@@ -2106,19 +2107,14 @@ public class DeviceIdleControllerTest {
assertTrue("setPreIdleTimeoutFactor: " + factor + " before step to idle",
Math.abs(newDelay - (newAlarm - now)) < errorTolerance);
mDeviceIdleController.resetPreIdleTimeoutMode();
- mDeviceIdleController.updatePreIdleFactor();
- mDeviceIdleController.maybeDoImmediateMaintenance();
}
} else {
mDeviceIdleController.setPreIdleTimeoutFactor(factor);
- mDeviceIdleController.updatePreIdleFactor();
long newAlarm = mDeviceIdleController.getNextAlarmTime();
assertTrue("setPreIdleTimeoutFactor: " + factor
+ " shounld not change next alarm" ,
(newAlarm == alarm));
mDeviceIdleController.resetPreIdleTimeoutMode();
- mDeviceIdleController.updatePreIdleFactor();
- mDeviceIdleController.maybeDoImmediateMaintenance();
}
}
@@ -2138,18 +2134,15 @@ public class DeviceIdleControllerTest {
long alarm = mDeviceIdleController.getNextAlarmTime();
mDeviceIdleController.setIdleStartTimeForTest(
now - (long) (mConstants.IDLE_TIMEOUT * 0.6));
- mDeviceIdleController.maybeDoImmediateMaintenance();
long newAlarm = mDeviceIdleController.getNextAlarmTime();
assertTrue("maintenance not reschedule IDLE_TIMEOUT * 0.6",
newAlarm == alarm);
mDeviceIdleController.setIdleStartTimeForTest(
now - (long) (mConstants.IDLE_TIMEOUT * 1.2));
- mDeviceIdleController.maybeDoImmediateMaintenance();
newAlarm = mDeviceIdleController.getNextAlarmTime();
assertTrue("maintenance not reschedule IDLE_TIMEOUT * 1.2",
(newAlarm - now) < minuteInMillis);
mDeviceIdleController.resetPreIdleTimeoutMode();
- mDeviceIdleController.updatePreIdleFactor();
}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 71f7d2c27c86..f2e118d493f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -56,6 +56,7 @@ import com.android.server.AppStateTracker;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
+import com.android.server.usage.AppStandbyInternal;
import org.junit.After;
import org.junit.Before;
@@ -100,6 +101,8 @@ public class JobSchedulerServiceTest {
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
doReturn(mActivityMangerInternal)
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mock(AppStandbyInternal.class))
+ .when(() -> LocalServices.getService(AppStandbyInternal.class));
doReturn(mock(UsageStatsManagerInternal.class))
.when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
// Called in BackgroundJobsController constructor.
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 5bd08c03d233..bc0b184cd359 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -78,6 +78,7 @@ import com.android.server.job.JobServiceContext;
import com.android.server.job.JobStore;
import com.android.server.job.controllers.QuotaController.ExecutionStats;
import com.android.server.job.controllers.QuotaController.TimingSession;
+import com.android.server.usage.AppStandbyInternal;
import org.junit.After;
import org.junit.Before;
@@ -150,6 +151,8 @@ public class QuotaControllerTest {
when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
doReturn(mActivityMangerInternal)
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mock(AppStandbyInternal.class))
+ .when(() -> LocalServices.getService(AppStandbyInternal.class));
doReturn(mock(BatteryManagerInternal.class))
.when(() -> LocalServices.getService(BatteryManagerInternal.class));
doReturn(mUsageStatsManager)
diff --git a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
index 0605d9e18069..50437b4d5f3e 100644
--- a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
@@ -36,7 +36,7 @@ public class DynamicSystemServiceTest extends AndroidTestCase {
public void test1() {
assertTrue("dynamic_system service available", mService != null);
try {
- mService.startInstallation("userdata", 8L << 30, false);
+ mService.startInstallation();
fail("DynamicSystemService did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 819091c378b8..f6fb6e23ccfb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -20,20 +20,17 @@ package com.android.server.pm;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.os.Build;
import android.os.Process;
-import android.permission.IPermissionManager;
import android.util.ArrayMap;
import org.junit.Before;
@@ -43,20 +40,16 @@ import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Map;
-
@RunWith(JUnit4.class)
public class AppsFilterTest {
private static final int DUMMY_CALLING_UID = 10345;
-
- @Mock
- IPermissionManager mPermissionManagerMock;
+ private static final int DUMMY_TARGET_UID = 10556;
@Mock
AppsFilter.FeatureConfig mFeatureConfigMock;
- private Map<String, PackageParser.Package> mExisting = new ArrayMap<>();
+ private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
private static PackageBuilder pkg(String packageName) {
return new PackageBuilder(packageName)
@@ -72,9 +65,10 @@ public class AppsFilterTest {
}
private static PackageBuilder pkg(String packageName, IntentFilter... filters) {
+ final ActivityInfo activityInfo = new ActivityInfo();
final PackageBuilder packageBuilder = pkg(packageName).addActivity(
pkg -> new PackageParser.ParseComponentArgs(pkg, new String[1], 0, 0, 0, 0, 0, 0,
- new String[]{packageName}, 0, 0, 0), new ActivityInfo());
+ new String[]{packageName}, 0, 0, 0), activityInfo);
for (IntentFilter filter : filters) {
packageBuilder.addActivityIntentInfo(0 /* index */, activity -> {
final PackageParser.ActivityIntentInfo info =
@@ -83,7 +77,7 @@ public class AppsFilterTest {
filter.actionsIterator().forEachRemaining(info::addAction);
}
if (filter.countCategories() > 0) {
- filter.actionsIterator().forEachRemaining(info::addAction);
+ filter.actionsIterator().forEachRemaining(info::addCategory);
}
if (filter.countDataAuthorities() > 0) {
filter.authoritiesIterator().forEachRemaining(info::addDataAuthority);
@@ -91,6 +85,7 @@ public class AppsFilterTest {
if (filter.countDataSchemes() > 0) {
filter.schemesIterator().forEachRemaining(info::addDataScheme);
}
+ activityInfo.exported = true;
return info;
});
}
@@ -102,9 +97,6 @@ public class AppsFilterTest {
mExisting = new ArrayMap<>();
MockitoAnnotations.initMocks(this);
- when(mPermissionManagerMock
- .checkPermission(anyString(), anyString(), anyInt()))
- .thenReturn(PackageManager.PERMISSION_DENIED);
when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
.thenReturn(true);
@@ -113,7 +105,7 @@ public class AppsFilterTest {
@Test
public void testSystemReadyPropogates() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
appsFilter.onSystemReady();
verify(mFeatureConfigMock).onSystemReady();
}
@@ -121,12 +113,12 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_FilterMatches() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package", new IntentFilter("TEST_ACTION"))).build();
+ pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION"))).build();
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -134,12 +126,12 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_NoMatchingAction_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package")).build();
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION"))).build();
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -147,13 +139,17 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID);
+ PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package",
+ new Intent("TEST_ACTION"))
+ .setApplicationInfoTargetSdkVersion(Build.VERSION_CODES.P),
+ DUMMY_CALLING_UID);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
- PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package",
- new Intent("TEST_ACTION")).setApplicationInfoTargetSdkVersion(
- Build.VERSION_CODES.P)).build();
+ when(mFeatureConfigMock.packageIsEnabled(calling.pkg)).thenReturn(false);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -161,12 +157,12 @@ public class AppsFilterTest {
@Test
public void testNoQueries_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -174,14 +170,12 @@ public class AppsFilterTest {
@Test
public void testForceQueryable_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
- PackageSetting target =
- simulateAddPackage(appsFilter, pkg("com.some.package").setForceQueryable(true))
- .build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -189,14 +183,13 @@ public class AppsFilterTest {
@Test
public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{"com.some.package"}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
- .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
- .build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID,
+ setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -204,12 +197,12 @@ public class AppsFilterTest {
@Test
public void testForceQueryableByDevice_NonSystemCaller_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{"com.some.package"}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -218,14 +211,14 @@ public class AppsFilterTest {
@Test
public void testSystemQueryable_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, true /* system force queryable */);
+ new AppsFilter(mFeatureConfigMock, new String[]{},
+ true /* system force queryable */);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
- .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
- .build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID,
+ setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -233,12 +226,12 @@ public class AppsFilterTest {
@Test
public void testQueriesPackage_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", "com.some.package")).build();
+ pkg("com.some.other.package", "com.some.package"), DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -248,12 +241,12 @@ public class AppsFilterTest {
when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
.thenReturn(false);
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
- PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
+ PackageSetting target = simulateAddPackage(
+ appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID);
+ PackageSetting calling = simulateAddPackage(
+ appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_UID);
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@@ -261,10 +254,10 @@ public class AppsFilterTest {
@Test
public void testSystemUid_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID);
assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
assertFalse(appsFilter.shouldFilterApplication(
@@ -274,10 +267,10 @@ public class AppsFilterTest {
@Test
public void testNonSystemUid_NoCallingSetting_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_UID);
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, null, target, 0));
}
@@ -285,8 +278,7 @@ public class AppsFilterTest {
@Test
public void testNoTargetPackage_filters() {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false);
PackageSetting target = new PackageSettingBuilder()
.setName("com.some.package")
@@ -295,22 +287,36 @@ public class AppsFilterTest {
.setPVersionCode(1L)
.build();
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION"))).build();
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
- private PackageSettingBuilder simulateAddPackage(AppsFilter filter,
- PackageBuilder newPkgBuilder) {
+ private interface WithSettingBuilder {
+ PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
+ }
+
+ private PackageSetting simulateAddPackage(AppsFilter filter,
+ PackageBuilder newPkgBuilder, int appId) {
+ return simulateAddPackage(filter, newPkgBuilder, appId, null);
+ }
+
+ private PackageSetting simulateAddPackage(AppsFilter filter,
+ PackageBuilder newPkgBuilder, int appId, @Nullable WithSettingBuilder action) {
PackageParser.Package newPkg = newPkgBuilder.build();
- filter.addPackage(newPkg, mExisting);
- mExisting.put(newPkg.packageName, newPkg);
- return new PackageSettingBuilder()
+
+ final PackageSettingBuilder settingBuilder = new PackageSettingBuilder()
.setPackage(newPkg)
+ .setAppId(appId)
.setName(newPkg.packageName)
.setCodePath("/")
.setResourcePath("/")
.setPVersionCode(1L);
+ final PackageSetting setting =
+ (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
+ filter.addPackage(setting, mExisting);
+ mExisting.put(newPkg.packageName, setting);
+ return setting;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 06c6314ab907..8d476f6e9318 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -43,6 +43,7 @@ class PackageSettingBuilder {
private String mVolumeUuid;
private SparseArray<PackageUserState> mUserStates = new SparseArray<>();
private PackageParser.Package mPkg;
+ private int mAppId;
public PackageSettingBuilder setPackage(PackageParser.Package pkg) {
this.mPkg = pkg;
@@ -54,6 +55,11 @@ class PackageSettingBuilder {
return this;
}
+ public PackageSettingBuilder setAppId(int appId) {
+ this.mAppId = appId;
+ return this;
+ }
+
public PackageSettingBuilder setRealName(String realName) {
this.mRealName = realName;
return this;
@@ -152,6 +158,7 @@ class PackageSettingBuilder {
mChildPackageNames, mSharedUserId, mUsesStaticLibraries,
mUsesStaticLibrariesVersions);
packageSetting.pkg = mPkg;
+ packageSetting.appId = mAppId;
packageSetting.volumeUuid = this.mVolumeUuid;
for (int i = 0; i < mUserStates.size(); i++) {
packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i));
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
index d6f7e37fcb39..7916bd37060e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
@@ -71,7 +71,7 @@ public class UserLifecycleStressTest {
throws IOException, RemoteException, InterruptedException {
for (int i = 0; i < NUM_ITERATIONS_STOP_USER; i++) {
final UserInfo userInfo = mUserManager.createProfileForUser("TestUser",
- UserInfo.FLAG_MANAGED_PROFILE, mActivityManager.getCurrentUser());
+ UserManager.USER_TYPE_PROFILE_MANAGED, 0, mActivityManager.getCurrentUser());
assertNotNull(userInfo);
try {
assertTrue(
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index 8dd896713834..e375aef3b7f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -16,15 +16,15 @@
package com.android.server.pm;
+import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.app.ApplicationPackageManager;
import android.content.pm.UserInfo;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManagerInternal;
-import android.util.IconDrawableFactory;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -71,6 +71,7 @@ public class UserManagerServiceCreateProfileTest {
removeUsers();
}
+ /** Tests UMS.getProfileIds() when no specific userType is specified. */
@Test
public void testGetProfiles() {
// Pretend we have a secondary user with a profile.
@@ -93,38 +94,73 @@ public class UserManagerServiceCreateProfileTest {
|| users.get(1).id == profile.id);
}
+ /** Tests UMS.getProfileIds() when a specific userType is specified. */
+ @Test
+ public void testGetProfileIds_specifyType() {
+ // Pretend we have a secondary user with a profile.
+ UserInfo secondaryUser = addUser();
+ UserInfo profile = addProfile(secondaryUser);
+
+ // TODO: When there are multiple profiles types, ensure correct output for mixed types.
+ final String userType1 = USER_TYPE_PROFILE_MANAGED;
+
+ // System user should still have no userType1 profile so getProfileIds should be empty.
+ int[] users = mUserManagerService.getProfileIds(UserHandle.USER_SYSTEM, userType1, false);
+ assertEquals("System user should have no managed profiles", 0, users.length);
+
+ // Secondary user should have one userType1 profile, so return just that.
+ users = mUserManagerService.getProfileIds(secondaryUser.id, userType1, false);
+ assertEquals("Wrong number of profiles", 1, users.length);
+ assertEquals("Wrong profile id", profile.id, users[0]);
+
+ // The profile itself is a userType1 profile, so it should return just itself.
+ users = mUserManagerService.getProfileIds(profile.id, userType1, false);
+ assertEquals("Wrong number of profiles", 1, users.length);
+ assertEquals("Wrong profile id", profile.id, users[0]);
+ }
+
@Test
public void testProfileBadge() {
// First profile for system user should get badge 0
assertEquals("First profile isn't given badge index 0", 0,
- mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM));
+ mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+ USER_TYPE_PROFILE_MANAGED));
// Pretend we have a secondary user.
UserInfo secondaryUser = addUser();
// Check first profile badge for secondary user is also 0.
assertEquals("First profile for secondary user isn't given badge index 0", 0,
- mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id));
+ mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
+ USER_TYPE_PROFILE_MANAGED));
// Shouldn't impact the badge for profile in system user
assertEquals("First profile isn't given badge index 0 with secondary user", 0,
- mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM));
+ mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+ USER_TYPE_PROFILE_MANAGED));
// Pretend a secondary user has a profile.
addProfile(secondaryUser);
// Shouldn't have impacted the badge for the system user
assertEquals("First profile isn't given badge index 0 in secondary user", 0,
- mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM));
+ mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+ USER_TYPE_PROFILE_MANAGED));
}
@Test
public void testProfileBadgeUnique() {
List<UserInfo> users = mUserManagerService.getUsers(/* excludeDying */ false);
UserInfo system = users.get(0);
+ int max = mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED);
+ if (max < 0) {
+ // Indicates no max. Instead of infinite, we'll just do 10.
+ max = 10;
+ }
// Badges should get allocated 0 -> max
- for (int i = 0; i < UserManagerService.getMaxManagedProfiles(); ++i) {
- int nextBadge = mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM);
+ for (int i = 0; i < max; ++i) {
+ int nextBadge = mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+ USER_TYPE_PROFILE_MANAGED);
assertEquals("Wrong badge allocated", i, nextBadge);
UserInfo profile = addProfile(system);
profile.profileBadge = nextBadge;
@@ -140,30 +176,23 @@ public class UserManagerServiceCreateProfileTest {
mUserManagerService.addRemovingUserIdLocked(profile.id);
// We should reuse the badge from the profile being removed.
assertEquals("Badge index not reused while removing a user", 0,
- mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id));
+ mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
+ USER_TYPE_PROFILE_MANAGED));
// Edge case of reuse that only applies if we ever support 3 managed profiles
// We should prioritise using lower badge indexes
- if (UserManagerService.getMaxManagedProfiles() > 2) {
+ int max = mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED);
+ if (max < 0 || max > 2) {
UserInfo profileBadgeOne = addProfile(secondaryUser);
profileBadgeOne.profileBadge = 1;
// 0 and 2 are free, we should reuse 0 rather than 2.
assertEquals("Lower index not used", 0,
- mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id));
+ mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
+ USER_TYPE_PROFILE_MANAGED));
}
}
@Test
- public void testNumberOfBadges() {
- assertTrue("Max profiles greater than number of badges",
- UserManagerService.MAX_MANAGED_PROFILES
- <= IconDrawableFactory.CORP_BADGE_COLORS.length);
- assertEquals("Num colors doesn't match number of badge labels",
- IconDrawableFactory.CORP_BADGE_COLORS.length,
- ApplicationPackageManager.CORP_BADGE_LABEL_RES_ID.length);
- }
-
- @Test
public void testCanAddMoreManagedProfiles_removeProfile() {
// if device is low-ram or doesn't support managed profiles for some other reason, just
// skip the test
@@ -171,6 +200,10 @@ public class UserManagerServiceCreateProfileTest {
false /* disallow remove */)) {
return;
}
+ if (mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED) < 0) {
+ // Indicates no limit, so we cannot run this test;
+ return;
+ }
// GIVEN we've reached the limit of managed profiles possible on the system user
while (mUserManagerService.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
@@ -192,6 +225,10 @@ public class UserManagerServiceCreateProfileTest {
false /* disallow remove */)) {
return;
}
+ if (mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED) < 0) {
+ // Indicates no limit, so we cannot run this test;
+ return;
+ }
// GIVEN we've reached the limit of managed profiles possible on the system user
// GIVEN that the profiles are not enabled yet
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 6d5b994a63bb..1e760ccba9f3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -16,11 +16,29 @@
package com.android.server.pm;
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+import static android.os.UserManager.USER_TYPE_FULL_DEMO;
+import static android.os.UserManager.USER_TYPE_FULL_GUEST;
+import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
+import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
+import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.annotation.UserIdInt;
import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.os.Looper;
import android.os.Parcel;
import android.os.UserHandle;
@@ -121,8 +139,61 @@ public class UserManagerServiceUserInfoTest {
assertEquals("A Name", mUserManagerService.getUserInfo(TEST_ID).name);
}
+ /** Test UMS.getUserTypeForUser(). */
+ @Test
+ public void testGetUserTypeForUser() throws Exception {
+ final String typeSys = mUserManagerService.getUserTypeForUser(UserHandle.USER_SYSTEM);
+ assertTrue("System user was of invalid type " + typeSys,
+ typeSys.equals(USER_TYPE_SYSTEM_HEADLESS) || typeSys.equals(USER_TYPE_FULL_SYSTEM));
+
+ final int testId = 100;
+ final String typeName = "A type";
+ UserInfo userInfo = createUser(testId, 0, typeName);
+ mUserManagerService.putUserInfo(userInfo);
+ assertEquals(typeName, mUserManagerService.getUserTypeForUser(testId));
+ }
+
+ /** Tests upgradeIfNecessaryLP (but without locking) for upgrading from version 8 to 9+. */
+ @Test
+ public void testUpgradeIfNecessaryLP_9() {
+ final int versionToTest = 9;
+
+ mUserManagerService.putUserInfo(createUser(100, FLAG_MANAGED_PROFILE, null));
+ mUserManagerService.putUserInfo(createUser(101,
+ FLAG_GUEST | FLAG_EPHEMERAL | FLAG_FULL, null));
+ mUserManagerService.putUserInfo(createUser(102, FLAG_RESTRICTED | FLAG_FULL, null));
+ mUserManagerService.putUserInfo(createUser(103, FLAG_FULL, null));
+ mUserManagerService.putUserInfo(createUser(104, FLAG_SYSTEM, null));
+ mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null));
+ mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null));
+
+ mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1);
+
+ assertEquals(USER_TYPE_PROFILE_MANAGED, mUserManagerService.getUserTypeForUser(100));
+ assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0);
+
+ assertEquals(USER_TYPE_FULL_GUEST, mUserManagerService.getUserTypeForUser(101));
+
+ assertEquals(USER_TYPE_FULL_RESTRICTED, mUserManagerService.getUserTypeForUser(102));
+ assertTrue((mUserManagerService.getUserInfo(102).flags & FLAG_PROFILE) == 0);
+
+ assertEquals(USER_TYPE_FULL_SECONDARY, mUserManagerService.getUserTypeForUser(103));
+ assertTrue((mUserManagerService.getUserInfo(103).flags & FLAG_PROFILE) == 0);
+
+ assertEquals(USER_TYPE_SYSTEM_HEADLESS, mUserManagerService.getUserTypeForUser(104));
+
+ assertEquals(USER_TYPE_FULL_SYSTEM, mUserManagerService.getUserTypeForUser(105));
+
+ assertEquals(USER_TYPE_FULL_DEMO, mUserManagerService.getUserTypeForUser(106));
+ }
+
+ /** Creates a UserInfo with the given flags and userType. */
+ private UserInfo createUser(@UserIdInt int userId, @UserInfoFlag int flags, String userType) {
+ return new UserInfo(userId, "A Name", "A path", flags, userType);
+ }
+
private UserInfo createUser() {
- UserInfo user = new UserInfo(/*id*/ 21, "A Name", "A path", /*flags*/ 0x0ff0ff);
+ UserInfo user = new UserInfo(/*id*/ 21, "A Name", "A path", /*flags*/ 0x0ff0ff, "A type");
user.serialNumber = 5;
user.creationTime = 4L << 32;
user.lastLoggedInTime = 5L << 32;
@@ -141,6 +212,7 @@ public class UserManagerServiceUserInfoTest {
assertEquals("Name not preserved", one.name, two.name);
assertEquals("Icon path not preserved", one.iconPath, two.iconPath);
assertEquals("Flags not preserved", one.flags, two.flags);
+ assertEquals("UserType not preserved", one.userType, two.userType);
assertEquals("profile group not preserved", one.profileGroupId,
two.profileGroupId);
assertEquals("restricted profile parent not preseved", one.restrictedProfileParentId,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
new file mode 100644
index 000000000000..7aadd87efb08
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
+
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.UserManager;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}.
+ *
+ * <p>Run with: atest UserManagerServiceUserTypeTest
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class UserManagerServiceUserTypeTest {
+
+ @Test
+ public void testUserTypeBuilder_createUserType() {
+ UserTypeDetails type = new UserTypeDetails.Builder()
+ .setName("a.name")
+ .setEnabled(true)
+ .setMaxAllowed(21)
+ .setBaseType(FLAG_FULL)
+ .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
+ .setBadgeLabels(23, 24, 25)
+ .setBadgeColors(26, 27)
+ .setIconBadge(28)
+ .setBadgePlain(29)
+ .setBadgeNoBackground(30)
+ .setLabel(31)
+ .setMaxAllowedPerParent(32)
+ .setDefaultRestrictions(new ArrayList<>(Arrays.asList("r1", "r2")))
+ .createUserTypeDetails();
+
+ assertEquals("a.name", type.getName());
+ assertTrue(type.isEnabled());
+ assertEquals(21, type.getMaxAllowed());
+ assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+ assertEquals(28, type.getIconBadge());
+ assertEquals(29, type.getBadgePlain());
+ assertEquals(30, type.getBadgeNoBackground());
+ assertEquals(31, type.getLabel());
+ assertEquals(32, type.getMaxAllowedPerParent());
+ assertEquals(new ArrayList<>(Arrays.asList("r1", "r2")), type.getDefaultRestrictions());
+
+
+ assertEquals(23, type.getBadgeLabel(0));
+ assertEquals(24, type.getBadgeLabel(1));
+ assertEquals(25, type.getBadgeLabel(2));
+ assertEquals(25, type.getBadgeLabel(3));
+ assertEquals(25, type.getBadgeLabel(4));
+ assertEquals(Resources.ID_NULL, type.getBadgeLabel(-1));
+
+ assertEquals(26, type.getBadgeColor(0));
+ assertEquals(27, type.getBadgeColor(1));
+ assertEquals(27, type.getBadgeColor(2));
+ assertEquals(27, type.getBadgeColor(3));
+ assertEquals(Resources.ID_NULL, type.getBadgeColor(-100));
+
+ assertTrue(type.hasBadge());
+ }
+
+ @Test
+ public void testUserTypeBuilder_defaults() {
+ UserTypeDetails type = new UserTypeDetails.Builder()
+ .setName("name") // Required (no default allowed)
+ .setBaseType(FLAG_FULL) // Required (no default allowed)
+ .createUserTypeDetails();
+
+ assertTrue(type.isEnabled());
+ assertEquals(UserTypeDetails.UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowed());
+ assertEquals(UserTypeDetails.UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowedPerParent());
+ assertEquals(FLAG_FULL, type.getDefaultUserInfoFlags());
+ assertEquals(Resources.ID_NULL, type.getIconBadge());
+ assertEquals(Resources.ID_NULL, type.getBadgePlain());
+ assertEquals(Resources.ID_NULL, type.getBadgeNoBackground());
+ assertEquals(Resources.ID_NULL, type.getBadgeLabel(0));
+ assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
+ assertEquals(Resources.ID_NULL, type.getLabel());
+ assertTrue(type.getDefaultRestrictions().isEmpty());
+
+ assertFalse(type.hasBadge());
+ }
+
+ @Test
+ public void testUserTypeBuilder_nameIsRequired() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new UserTypeDetails.Builder()
+ .setMaxAllowed(21)
+ .setBaseType(FLAG_FULL)
+ .createUserTypeDetails());
+ }
+
+ @Test
+ public void testUserTypeBuilder_baseTypeIsRequired() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new UserTypeDetails.Builder()
+ .setName("name")
+ .createUserTypeDetails());
+ }
+
+ @Test
+ public void testUserTypeBuilder_colorIsRequiredIfBadged() {
+ assertThrows(IllegalArgumentException.class,
+ () -> getMinimalBuilder()
+ .setIconBadge(1)
+ .setBadgeLabels(2)
+ .createUserTypeDetails());
+ }
+
+ @Test
+ public void testUserTypeBuilder_badgeLabelIsRequiredIfBadged() {
+ assertThrows(IllegalArgumentException.class,
+ () -> getMinimalBuilder()
+ .setIconBadge(1)
+ .setBadgeColors(2)
+ .createUserTypeDetails());
+ }
+
+ @Test
+ public void testCheckUserTypeConsistency() {
+ assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST));
+ assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST | FLAG_EPHEMERAL));
+ assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE));
+
+ assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_DEMO | FLAG_RESTRICTED));
+ assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_SYSTEM));
+ assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_FULL));
+ }
+
+ @Test
+ public void testGetDefaultUserType() {
+ // Simple example.
+ assertEquals(UserManager.USER_TYPE_FULL_RESTRICTED,
+ UserInfo.getDefaultUserType(FLAG_RESTRICTED));
+
+ // Type plus a non-type flag.
+ assertEquals(UserManager.USER_TYPE_FULL_GUEST,
+ UserInfo.getDefaultUserType(FLAG_GUEST | FLAG_EPHEMERAL));
+
+ // Two types, which is illegal.
+ assertThrows(IllegalArgumentException.class,
+ () -> UserInfo.getDefaultUserType(FLAG_MANAGED_PROFILE | FLAG_GUEST));
+
+ // No type, which defaults to {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+ assertEquals(UserManager.USER_TYPE_FULL_SECONDARY,
+ UserInfo.getDefaultUserType(FLAG_EPHEMERAL));
+ }
+
+ /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */
+ private UserTypeDetails.Builder getMinimalBuilder() {
+ return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index e9edba58a3dd..d07192768182 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -23,6 +24,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -196,6 +198,62 @@ public class UserManagerTest extends AndroidTestCase {
}
}
+ /** Tests creating a FULL user via specifying userType. */
+ @MediumTest
+ public void testCreateUserViaTypes() throws Exception {
+ createUserWithTypeAndCheckFlags(UserManager.USER_TYPE_FULL_GUEST,
+ UserInfo.FLAG_GUEST | UserInfo.FLAG_FULL);
+
+ createUserWithTypeAndCheckFlags(UserManager.USER_TYPE_FULL_DEMO,
+ UserInfo.FLAG_DEMO | UserInfo.FLAG_FULL);
+
+ createUserWithTypeAndCheckFlags(UserManager.USER_TYPE_FULL_SECONDARY,
+ UserInfo.FLAG_FULL);
+ }
+
+ /** Tests creating a FULL user via specifying user flags. */
+ @MediumTest
+ public void testCreateUserViaFlags() throws Exception {
+ createUserWithFlagsAndCheckType(UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST,
+ UserInfo.FLAG_FULL);
+
+ createUserWithFlagsAndCheckType(0, UserManager.USER_TYPE_FULL_SECONDARY,
+ UserInfo.FLAG_FULL);
+
+ createUserWithFlagsAndCheckType(UserInfo.FLAG_FULL, UserManager.USER_TYPE_FULL_SECONDARY,
+ 0);
+
+ createUserWithFlagsAndCheckType(UserInfo.FLAG_DEMO, UserManager.USER_TYPE_FULL_DEMO,
+ UserInfo.FLAG_FULL);
+ }
+
+ /** Creates a user of the given user type and checks that the result has the requiredFlags. */
+ private void createUserWithTypeAndCheckFlags(String userType,
+ @UserIdInt int requiredFlags) {
+ final UserInfo userInfo = createUser("Name", userType, 0);
+ assertEquals("Wrong user type", userType, userInfo.userType);
+ assertEquals(
+ "Flags " + userInfo.flags + " did not contain expected " + requiredFlags,
+ requiredFlags, userInfo.flags & requiredFlags);
+ removeUser(userInfo.id);
+ }
+
+ /**
+ * Creates a user of the given flags and checks that the result is of the expectedUserType type
+ * and that it has the expected flags (including both flags and any additionalRequiredFlags).
+ */
+ private void createUserWithFlagsAndCheckType(@UserIdInt int flags, String expectedUserType,
+ @UserIdInt int additionalRequiredFlags) {
+ final UserInfo userInfo = createUser("Name", flags);
+ assertEquals("Wrong user type", expectedUserType, userInfo.userType);
+ additionalRequiredFlags |= flags;
+ assertEquals(
+ "Flags " + userInfo.flags + " did not contain expected " + additionalRequiredFlags,
+ additionalRequiredFlags, userInfo.flags & additionalRequiredFlags);
+ removeUser(userInfo.id);
+ }
+
+
@MediumTest
public void testAddGuest() throws Exception {
UserInfo userInfo1 = createUser("Guest 1", UserInfo.FLAG_GUEST);
@@ -234,7 +292,7 @@ public class UserManagerTest extends AndroidTestCase {
final int primaryUserId = mUserManager.getPrimaryUser().id;
UserInfo userInfo = createProfileForUser("Profile",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
assertNotNull(userInfo);
assertNull(mUserManager.getProfileParent(primaryUserId));
UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
@@ -244,17 +302,61 @@ public class UserManagerTest extends AndroidTestCase {
assertNull(mUserManager.getProfileParent(primaryUserId));
}
+ /** Test that UserManager returns the correct badge information for a managed profile. */
+ @MediumTest
+ public void testProfileTypeInformation() throws Exception {
+ final UserTypeDetails userTypeDetails =
+ UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_PROFILE_MANAGED);
+ assertNotNull("No " + UserManager.USER_TYPE_PROFILE_MANAGED + " type on device",
+ userTypeDetails);
+ assertEquals(UserManager.USER_TYPE_PROFILE_MANAGED, userTypeDetails.getName());
+
+ final int primaryUserId = mUserManager.getPrimaryUser().id;
+ UserInfo userInfo = createProfileForUser("Managed",
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
+ assertNotNull(userInfo);
+ final int userId = userInfo.id;
+ final UserHandle userHandle = new UserHandle(userId);
+
+ assertEquals(userTypeDetails.hasBadge(),
+ mUserManager.hasBadge(userId));
+ assertEquals(userTypeDetails.getIconBadge(),
+ mUserManager.getUserIconBadgeResId(userId));
+ assertEquals(userTypeDetails.getBadgePlain(),
+ mUserManager.getUserBadgeResId(userId));
+ assertEquals(userTypeDetails.getBadgeNoBackground(),
+ mUserManager.getUserBadgeNoBackgroundResId(userId));
+ assertEquals(userTypeDetails.isProfile(),
+ mUserManager.isProfile(userId));
+ assertEquals(userTypeDetails.getName(),
+ mUserManager.getUserTypeForUser(userHandle));
+
+ final int badgeIndex = userInfo.profileBadge;
+ assertEquals(
+ Resources.getSystem().getColor(userTypeDetails.getBadgeColor(badgeIndex), null),
+ mUserManager.getUserBadgeColor(userId));
+ assertEquals(
+ Resources.getSystem().getString(userTypeDetails.getBadgeLabel(badgeIndex), "Test"),
+ mUserManager.getBadgedLabelForUser("Test", userHandle));
+ }
+
// Make sure only one managed profile can be created
@MediumTest
public void testAddManagedProfile() throws Exception {
final int primaryUserId = mUserManager.getPrimaryUser().id;
UserInfo userInfo1 = createProfileForUser("Managed 1",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
UserInfo userInfo2 = createProfileForUser("Managed 2",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
assertNotNull(userInfo1);
assertNull(userInfo2);
+
+ assertEquals(userInfo1.userType, UserManager.USER_TYPE_PROFILE_MANAGED);
+ int requiredFlags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_PROFILE;
+ assertEquals("Wrong flags " + userInfo1.flags, requiredFlags,
+ userInfo1.flags & requiredFlags);
+
// Verify that current user is not a managed profile
assertFalse(mUserManager.isManagedProfile());
}
@@ -264,7 +366,7 @@ public class UserManagerTest extends AndroidTestCase {
public void testAddManagedProfile_withDisallowedPackages() throws Exception {
final int primaryUserId = mUserManager.getPrimaryUser().id;
UserInfo userInfo1 = createProfileForUser("Managed1",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
// Verify that the packagesToVerify are installed by default.
for (String pkg : PACKAGES) {
assertTrue("Package should be installed in managed profile: " + pkg,
@@ -273,7 +375,7 @@ public class UserManagerTest extends AndroidTestCase {
removeUser(userInfo1.id);
UserInfo userInfo2 = createProfileForUser("Managed2",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId, PACKAGES);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId, PACKAGES);
// Verify that the packagesToVerify are not installed by default.
for (String pkg : PACKAGES) {
assertFalse("Package should not be installed in managed profile when disallowed: "
@@ -287,7 +389,7 @@ public class UserManagerTest extends AndroidTestCase {
public void testAddManagedProfile_disallowedPackagesInstalledLater() throws Exception {
final int primaryUserId = mUserManager.getPrimaryUser().id;
UserInfo userInfo = createProfileForUser("Managed",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId, PACKAGES);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId, PACKAGES);
// Verify that the packagesToVerify are not installed by default.
for (String pkg : PACKAGES) {
assertFalse("Package should not be installed in managed profile when disallowed: "
@@ -326,7 +428,7 @@ public class UserManagerTest extends AndroidTestCase {
primaryUserHandle);
try {
UserInfo userInfo = createProfileForUser("Managed",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
assertNull(userInfo);
} finally {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
@@ -343,7 +445,7 @@ public class UserManagerTest extends AndroidTestCase {
primaryUserHandle);
try {
UserInfo userInfo = createProfileEvenWhenDisallowedForUser("Managed",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
assertNotNull(userInfo);
} finally {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
@@ -359,7 +461,7 @@ public class UserManagerTest extends AndroidTestCase {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
try {
UserInfo userInfo = createProfileForUser("Managed",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
assertNotNull(userInfo);
} finally {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
@@ -396,7 +498,7 @@ public class UserManagerTest extends AndroidTestCase {
final int primaryUserId = mUserManager.getPrimaryUser().id;
final long startTime = System.currentTimeMillis();
UserInfo profile = createProfileForUser("Managed 1",
- UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+ UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
final long endTime = System.currentTimeMillis();
assertNotNull(profile);
if (System.currentTimeMillis() > EPOCH_PLUS_30_YEARS) {
@@ -663,24 +765,32 @@ public class UserManagerTest extends AndroidTestCase {
return user;
}
- private UserInfo createProfileForUser(String name, int flags, int userHandle) {
- return createProfileForUser(name, flags, userHandle, null);
+ private UserInfo createUser(String name, String userType, int flags) {
+ UserInfo user = mUserManager.createUser(name, userType, flags);
+ if (user != null) {
+ usersToRemove.add(user.id);
+ }
+ return user;
+ }
+
+ private UserInfo createProfileForUser(String name, String userType, int userHandle) {
+ return createProfileForUser(name, userType, userHandle, null);
}
- private UserInfo createProfileForUser(String name, int flags, int userHandle,
+ private UserInfo createProfileForUser(String name, String userType, int userHandle,
String[] disallowedPackages) {
UserInfo profile = mUserManager.createProfileForUser(
- name, flags, userHandle, disallowedPackages);
+ name, userType, 0, userHandle, disallowedPackages);
if (profile != null) {
usersToRemove.add(profile.id);
}
return profile;
}
- private UserInfo createProfileEvenWhenDisallowedForUser(String name, int flags,
+ private UserInfo createProfileEvenWhenDisallowedForUser(String name, String userType,
int userHandle) {
UserInfo profile = mUserManager.createProfileForUserEvenWhenDisallowed(
- name, flags, userHandle, null);
+ name, userType, 0, userHandle, null);
if (profile != null) {
usersToRemove.add(profile.id);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index f0b0328ff7d4..f492932a0d77 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -147,7 +147,7 @@ public class UserSystemPackageInstallerTest {
final ArrayMap<String, Integer> expectedOutput = getNewPackageToWhitelistedFlagsMap();
expectedOutput.put("com.android.package1",
- UserInfo.PROFILE_FLAGS_MASK | FLAG_SYSTEM | FLAG_GUEST);
+ UserInfo.FLAG_PROFILE | FLAG_SYSTEM | FLAG_GUEST);
expectedOutput.put("com.android.package2",
UserInfo.FLAG_MANAGED_PROFILE);
@@ -376,9 +376,9 @@ public class UserSystemPackageInstallerTest {
/** Sets the whitelist mode to the desired value via adb's setprop. */
private void setUserTypePackageWhitelistMode(int mode) {
- UiDevice mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
try {
- String result = mUiDevice.executeShellCommand(String.format("setprop %s %d",
+ String result = uiDevice.executeShellCommand(String.format("setprop %s %d",
PACKAGE_WHITELIST_MODE_PROP, mode));
assertFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result,
result != null && result.contains("Failed"));
@@ -390,7 +390,7 @@ public class UserSystemPackageInstallerTest {
private ArrayMap<String, Integer> getNewPackageToWhitelistedFlagsMap() {
final ArrayMap<String, Integer> pkgFlagMap = new ArrayMap<>();
// "android" is always treated as whitelisted, regardless of the xml file.
- pkgFlagMap.put("android", FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
+ pkgFlagMap.put("android", FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.FLAG_PROFILE);
return pkgFlagMap;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserTests.java b/services/tests/servicestests/src/com/android/server/pm/UserTests.java
new file mode 100644
index 000000000000..525382d50d72
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserTests.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ UserDataPreparerTest.class,
+ UserLifecycleStressTest.class,
+ UserManagerServiceCreateProfileTest.class,
+ UserManagerServiceIdRecyclingTest.class,
+ UserManagerServiceTest.class,
+ UserManagerServiceUserInfoTest.class,
+ UserManagerServiceUserTypeTest.class,
+ UserManagerTest.class,
+ UserRestrictionsUtilsTest.class,
+ UserSystemPackageInstallerTest.class,
+})
+public class UserTests {
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 88de250e4b0d..592f4ec7caab 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -33,9 +33,11 @@ import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
+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;
@@ -62,11 +64,13 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.test.mock.MockContentResolver;
import android.view.Display;
import androidx.test.InstrumentationRegistry;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.lights.LightsManager;
@@ -109,6 +113,9 @@ public class PowerManagerServiceTest {
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
+ @Mock
+ private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
+
private PowerManagerService mService;
private PowerSaveState mPowerSaveState;
private DisplayPowerRequest mDisplayPowerRequest;
@@ -149,6 +156,9 @@ public class PowerManagerServiceTest {
when(mBatterySaverPolicyMock.getBatterySaverPolicy(
eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
.thenReturn(mPowerSaveState);
+ when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
+ when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
+ when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
mDisplayPowerRequest = new DisplayPowerRequest();
addLocalServiceMock(LightsManager.class, mLightsManagerMock);
@@ -161,7 +171,12 @@ public class PowerManagerServiceTest {
mResourcesSpy = spy(mContextSpy.getResources());
when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
- when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
+ MockContentResolver cr = new MockContentResolver(mContextSpy);
+ cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContextSpy.getContentResolver()).thenReturn(cr);
+
+ Settings.Global.putInt(mContextSpy.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
}
private PowerManagerService createService() {
@@ -198,6 +213,11 @@ public class PowerManagerServiceTest {
AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
return mAmbientDisplayConfigurationMock;
}
+
+ @Override
+ InattentiveSleepWarningController createInattentiveSleepWarningController() {
+ return mInattentiveSleepWarningControllerMock;
+ }
});
return mService;
}
@@ -208,8 +228,12 @@ public class PowerManagerServiceTest {
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.removeServiceForTest(BatteryManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+
Settings.Global.putInt(
mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0);
+ setAttentiveTimeout(-1);
+ Settings.Global.putInt(mContextSpy.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
}
/**
@@ -263,12 +287,30 @@ public class PowerManagerServiceTest {
private void setPluggedIn(boolean isPluggedIn) {
// Set the callback to return the new state
- when(mBatteryManagerInternalMock.isPowered(BatteryManager.BATTERY_PLUGGED_ANY))
+ when(mBatteryManagerInternalMock.isPowered(anyInt()))
.thenReturn(isPluggedIn);
// Trigger PowerManager to reread the plug-in state
mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED));
}
+ private void setAttentiveTimeout(int attentiveTimeoutMillis) {
+ Settings.Secure.putInt(
+ mContextSpy.getContentResolver(), Settings.Secure.ATTENTIVE_TIMEOUT,
+ attentiveTimeoutMillis);
+ }
+
+ private void setAttentiveWarningDuration(int attentiveWarningDurationMillis) {
+ when(mResourcesSpy.getInteger(
+ com.android.internal.R.integer.config_attentiveWarningDuration))
+ .thenReturn(attentiveWarningDurationMillis);
+ }
+
+ private void setMinimumScreenOffTimeoutConfig(int minimumScreenOffTimeoutConfigMillis) {
+ when(mResourcesSpy.getInteger(
+ com.android.internal.R.integer.config_minimumScreenOffTimeout))
+ .thenReturn(minimumScreenOffTimeoutConfigMillis);
+ }
+
@Test
public void testUpdatePowerScreenPolicy_UpdateDisplayPowerRequest() {
createService();
@@ -615,4 +657,97 @@ public class PowerManagerServiceTest {
.setDozeOverrideFromDreamManager(Display.STATE_ON, PowerManager.BRIGHTNESS_DEFAULT);
assertTrue(isAcquired[0]);
}
+
+ @Test
+ public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() throws Exception {
+ setAttentiveTimeout(15000);
+ Settings.Global.putInt(mContextSpy.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC);
+
+ createService();
+ startSystem();
+
+ verify(mInattentiveSleepWarningControllerMock, times(1)).show();
+ verify(mInattentiveSleepWarningControllerMock, never()).dismiss();
+ when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
+
+ setPluggedIn(true);
+ verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).dismiss();
+ }
+
+ @Test
+ public void testInattentive_userActivityDismissesWarning() throws Exception {
+ setMinimumScreenOffTimeoutConfig(5);
+ setAttentiveWarningDuration(30);
+ setAttentiveTimeout(100);
+
+ createService();
+ startSystem();
+
+ mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ verify(mInattentiveSleepWarningControllerMock, never()).show();
+
+ SystemClock.sleep(70);
+ verify(mInattentiveSleepWarningControllerMock, times(1)).show();
+ verify(mInattentiveSleepWarningControllerMock, never()).dismiss();
+ when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
+
+ mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss();
+ }
+
+ @Test
+ public void testInattentiveSleep_warningHiddenAfterWakingUp() throws Exception {
+ setMinimumScreenOffTimeoutConfig(5);
+ setAttentiveWarningDuration(20);
+ setAttentiveTimeout(30);
+
+ createService();
+ startSystem();
+ SystemClock.sleep(10);
+ verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).show();
+ when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
+ SystemClock.sleep(30);
+ forceAwake();
+ verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).dismiss();
+ }
+
+ @Test
+ public void testInattentiveSleep_noWarningShownIfInattentiveSleepDisabled() throws Exception {
+ setAttentiveTimeout(-1);
+ createService();
+ startSystem();
+ verify(mInattentiveSleepWarningControllerMock, never()).show();
+ }
+
+ @Test
+ public void testInattentiveSleep_goesToSleepAfterTimeout() throws Exception {
+ setMinimumScreenOffTimeoutConfig(5);
+ setAttentiveTimeout(5);
+ createService();
+ startSystem();
+ SystemClock.sleep(8);
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
+ }
+
+ @Test
+ public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception {
+ final String pkg = mContextSpy.getOpPackageName();
+ final Binder token = new Binder();
+ final String tag = "sleep_testWithWakeLock";
+
+ setMinimumScreenOffTimeoutConfig(5);
+ setAttentiveTimeout(10);
+ createService();
+ startSystem();
+
+ mService.getBinderServiceInstance().acquireWakeLock(token,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
+ null /* workSource */, null /* historyTag */);
+
+ SystemClock.sleep(11);
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index e560cb9a6cf7..9df7b4576427 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -346,7 +346,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
activity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
activity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- activity.mVisibleRequested = true;
+ activity.visible = true;
activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
final ArrayList<CompletableFuture<IBinder>> resultWrapper = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 734761fd8048..3c619f73aa6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -160,7 +160,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
public void testOnActivityLaunchCancelled_hasDrawn() {
onActivityLaunched();
- mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+ mTopActivity.visible = mTopActivity.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
@@ -171,7 +171,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
@Test
public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
- mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+ mTopActivity.visible = mTopActivity.mDrawn = true;
// Suppress resume when creating the record because we want to notify logger manually.
mSupervisor.beginDeferResume();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e22c419f5919..c51a46a76f4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -221,7 +221,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testRestartProcessIfVisible() {
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.setSavedState(null /* savedState */);
mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
prepareFixedAspectRatioUnresizableActivity();
@@ -502,7 +502,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
mTask.setBounds(100, 100, 400, 600);
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
final Rect bounds = new Rect(mActivity.getBounds());
@@ -547,7 +547,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
.when(mActivity).getRequestedOrientation();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
// The parent configuration doesn't change since the first resolved configuration, so the
// activity shouldn't be in the size compatibility mode.
@@ -589,7 +589,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setMaxAspectRatio(1.5f)
.build();
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
final Rect originalBounds = new Rect(mActivity.getBounds());
final int originalDpi = mActivity.getConfiguration().densityDpi;
@@ -614,7 +614,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -661,7 +661,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
prepareFixedAspectRatioUnresizableActivity();
mActivity.setState(STOPPED, "testSizeCompatMode");
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
// Make the parent bounds to be different so the activity is in size compatibility mode.
setupDisplayAndParentSize(600, 1200);
@@ -829,7 +829,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Prepare the activity record to be ready for immediate removal. It should be invisible and
// have no process. Otherwise, request to finish it will send a message to client first.
mActivity.setState(STOPPED, "test");
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.nowVisible = false;
// Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() -
// this will cause NPE when updating task's process.
@@ -838,7 +838,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Put a visible activity on top, so the finishing activity doesn't have to wait until the
// next activity reports idle to destroy it.
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.setState(RESUMED, "test");
@@ -924,7 +924,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
mActivity.finishing = false;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.setState(RESUMED, "test");
mActivity.finishIfPossible("test", false /* oomAdj */);
@@ -940,7 +940,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
mActivity.finishing = false;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.setState(PAUSED, "test");
mActivity.finishIfPossible("test", false /* oomAdj */);
@@ -958,7 +958,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Put an activity on top of test activity to make it invisible and prevent us from
// accidentally resuming the topmost one again.
new ActivityBuilder(mService).build();
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.setState(STOPPED, "test");
mActivity.finishIfPossible("test", false /* oomAdj */);
@@ -1010,7 +1010,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_keepStateOfNextInvisible() {
final ActivityRecord currentTop = mActivity;
- currentTop.mVisibleRequested = currentTop.nowVisible = true;
+ currentTop.visible = currentTop.nowVisible = true;
// Simulates that {@code currentTop} starts an existing activity from background (so its
// state is stopped) and the starting flow just goes to place it at top.
@@ -1036,13 +1036,13 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_waitForNextVisible() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as not visible, so that we will wait for it before removing
// the top one.
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.nowVisible = false;
mActivity.setState(STOPPED, "test");
@@ -1061,13 +1061,13 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = false;
+ topActivity.visible = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as not visible, so that we would wait for it before removing
// the top one.
- mActivity.mVisibleRequested = false;
+ mActivity.visible = false;
mActivity.nowVisible = false;
mActivity.setState(STOPPED, "test");
@@ -1083,12 +1083,12 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_waitForIdle() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.nowVisible = true;
mActivity.setState(RESUMED, "test");
@@ -1104,12 +1104,12 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_noWaitForNextVisible_stopped() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = false;
+ topActivity.visible = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
topActivity.setState(STOPPED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.nowVisible = true;
mActivity.setState(RESUMED, "test");
@@ -1125,12 +1125,12 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.mVisibleRequested = true;
+ topActivity.visible = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
mActivity.nowVisible = true;
mActivity.setState(RESUMED, "test");
@@ -1139,7 +1139,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
final ActivityRecord focusedActivity = stack.getChildAt(0).getChildAt(0);
focusedActivity.nowVisible = true;
- focusedActivity.mVisibleRequested = true;
+ focusedActivity.visible = true;
focusedActivity.setState(RESUMED, "test");
stack.mResumedActivity = focusedActivity;
@@ -1346,7 +1346,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
setupDisplayContentForCompatDisplayInsets();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.maxAspectRatio = 1.5f;
- mActivity.mVisibleRequested = true;
+ mActivity.visible = true;
ensureActivityConfiguration();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index fc44652cc668..d0e07b619ad6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1006,7 +1006,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// There is still an activity1 in stack1 so the activity2 should be added to finishing list
// that will be destroyed until idle.
- stack2.getTopActivity().mVisibleRequested = true;
+ stack2.getTopActivity().visible = true;
final ActivityRecord activity2 = finishTopActivity(stack2);
assertEquals(STOPPING, activity2.getState());
assertThat(mSupervisor.mStoppingActivities).contains(activity2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 39aa51a05764..a23e2f1589c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -24,6 +25,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
+import android.app.ActivityManager;
import android.graphics.Rect;
import android.view.WindowContainerTransaction;
@@ -76,5 +78,20 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
mService.applyContainerTransaction(t);
assertEquals(newBounds, task.getBounds());
}
+
+ @Test
+ public void testStackTransaction() {
+ removeGlobalMinSizeRestriction();
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ ActivityManager.StackInfo info =
+ mService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ assertEquals(stack.mRemoteToken, info.stackToken);
+ Rect newBounds = new Rect(10, 10, 100, 100);
+ t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
+ mService.applyContainerTransaction(t);
+ assertEquals(newBounds, stack.getBounds());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 163268191df1..47b39b042fb5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -271,7 +271,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
doReturn(true).when(activity).occludesParent();
mTask.addChild(activity);
// Make visible by default...
- activity.setVisible(true);
+ activity.setHidden(false);
}
final WindowProcessController wpc = new WindowProcessController(mService,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
index b6eaab7efec4..c6203c5736fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
@@ -37,7 +37,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
- * Tests for the {@link TaskStack} class.
+ * Tests for the {@link ActivityStack} class.
*
* Build/Install/Run:
* atest FrameworksServicesTests:AnimatingActivityRegistryTest
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 6ee9621f898b..fc94c5ec9344 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -28,7 +28,6 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.graphics.Rect;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
@@ -55,7 +54,7 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
public class AppChangeTransitionTests extends WindowTestsBase {
- private TaskStack mStack;
+ private ActivityStack mStack;
private Task mTask;
private ActivityRecord mActivity;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index d415f25baab9..60204539d178 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -62,7 +62,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentOpening.setOccludesParent(false);
- translucentOpening.setVisible(false);
+ translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
@@ -90,7 +90,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentOpening.setOccludesParent(false);
- translucentOpening.setVisible(false);
+ translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
assertEquals(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index d491569149a5..bd336ad2494f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -252,7 +252,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
@Presubmit
public void testGetOrientation() {
- mActivity.setVisible(true);
+ mActivity.setHidden(false);
mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -261,7 +261,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
mActivity.setOccludesParent(true);
- mActivity.setVisible(false);
+ mActivity.setHidden(true);
mActivity.sendingToBottom = true;
// Can not specify orientation if app isn't visible even though it occludes parent.
assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
@@ -314,7 +314,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testSetOrientation() {
- mActivity.setVisible(true);
+ mActivity.setHidden(false);
// Assert orientation is unspecified to start.
assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 8db48584295a..9f4143ff95fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -394,7 +394,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Make sure top focused display not changed if there is a focused app.
- window1.mActivityRecord.mVisibleRequested = false;
+ window1.mActivityRecord.hiddenRequested = true;
window1.getDisplayContent().setFocusedApp(window1.mActivityRecord);
updateFocusedWindow();
assertTrue(!window1.isFocused());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index e0ffb0d03f89..4f2d5d23f83d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -61,6 +61,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
@@ -92,7 +93,9 @@ public class RecentTasksTest extends ActivityTestsBase {
private static final int TEST_USER_1_ID = 10;
private static final int TEST_QUIET_USER_ID = 20;
private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
- private static final UserInfo QUIET_USER_INFO = new UserInfo();
+ private static final UserInfo QUIET_PROFILE_USER_INFO = new UserInfo(TEST_QUIET_USER_ID,
+ "quiet_profile", null /* iconPath */, UserInfo.FLAG_QUIET_MODE,
+ UserManager.USER_TYPE_PROFILE_MANAGED);
private static final int INVALID_STACK_ID = 999;
private ActivityDisplay mDisplay;
@@ -125,7 +128,6 @@ public class RecentTasksTest extends ActivityTestsBase {
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
- QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
mTasks = new ArrayList<>();
mTasks.add(createTaskBuilder(".Task1").build());
@@ -1220,7 +1222,7 @@ public class RecentTasksTest extends ActivityTestsBase {
case TEST_USER_1_ID:
return DEFAULT_USER_INFO;
case TEST_QUIET_USER_ID:
- return QUIET_USER_INFO;
+ return QUIET_PROFILE_USER_INFO;
}
return null;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 1abd3662165e..702600402d8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -149,7 +149,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final ActivityRecord hiddenActivity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- hiddenActivity.setVisible(false);
+ hiddenActivity.setHidden(true);
mDisplayContent.getConfiguration().windowConfiguration.setRotation(
mDisplayContent.getRotation());
mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 06d96fee3757..41cbd8137f5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -106,12 +106,12 @@ public class RecentsAnimationTest extends ActivityTestsBase {
RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
mRecentsComponent, true /* getRecentsAnimation */);
// The launch-behind state should make the recents activity visible.
- assertTrue(recentActivity.mVisibleRequested);
+ assertTrue(recentActivity.visible);
// Simulate the animation is cancelled without changing the stack order.
recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
// The non-top recents activity should be invisible by the restored launch-behind state.
- assertFalse(recentActivity.mVisibleRequested);
+ assertFalse(recentActivity.visible);
}
@Test
@@ -158,7 +158,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
// The activity is started in background so it should be invisible and will be stopped.
assertThat(recentsActivity).isNotNull();
assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
- assertFalse(recentsActivity.mVisibleRequested);
+ assertFalse(recentsActivity.visible);
// Assume it is stopped to test next use case.
recentsActivity.activityStoppedLocked(null /* newIcicle */, null /* newPersistentState */,
@@ -361,7 +361,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
true);
// Ensure we find the task for the right user and it is made visible
- assertTrue(otherUserHomeActivity.mVisibleRequested);
+ assertTrue(otherUserHomeActivity.visible);
}
private void startRecentsActivity() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index 8617fb258ba1..dbf61db088ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -68,8 +68,8 @@ public class TaskStackContainersTests extends WindowTestsBase {
@Test
public void testStackPositionChildAt() {
// Test that always-on-top stack can't be moved to position other than top.
- final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final TaskStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
final WindowContainer taskStackContainer = stack1.getParent();
@@ -93,7 +93,7 @@ public class TaskStackContainersTests extends WindowTestsBase {
@Test
public void testStackPositionBelowPinnedStack() {
// Test that no stack can be above pinned stack.
- final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+ final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent);
final WindowContainer taskStackContainer = stack1.getParent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 40ce36324578..58770418413c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -41,7 +41,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
/**
- * Tests for the {@link TaskStack} class.
+ * Tests for the {@link ActivityStack} class.
*
* Build/Install/Run:
* atest WmTests:TaskStackTests
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 0b7cbceb8f95..9e0d3a78c756 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -261,7 +261,7 @@ public class WindowStateTests extends WindowTestsBase {
// minimized and home stack is resizable, so that we should ignore input for the stack.
final DockedStackDividerController controller =
mDisplayContent.getDockedDividerController();
- final TaskStack stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ final ActivityStack stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
ACTIVITY_TYPE_STANDARD, mDisplayContent);
spyOn(appWindow);
spyOn(controller);
@@ -383,11 +383,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanAffectSystemUiFlags() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mActivityRecord.setVisible(true);
+ app.mToken.setHidden(false);
assertTrue(app.canAffectSystemUiFlags());
- app.mActivityRecord.setVisible(false);
+ app.mToken.setHidden(true);
assertFalse(app.canAffectSystemUiFlags());
- app.mActivityRecord.setVisible(true);
+ app.mToken.setHidden(false);
app.mAttrs.alpha = 0.0f;
assertFalse(app.canAffectSystemUiFlags());
}
@@ -395,7 +395,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCanAffectSystemUiFlags_disallow() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mActivityRecord.setVisible(true);
+ app.mToken.setHidden(false);
assertTrue(app.canAffectSystemUiFlags());
app.getTask().setCanAffectSystemUiFlags(false);
assertFalse(app.canAffectSystemUiFlags());
@@ -569,7 +569,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.mActivityRecord.mVisibleRequested = false;
+ win0.mActivityRecord.hiddenRequested = true;
assertTrue(win0.cantReceiveTouchInput());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 26743c842122..797a6bc7e087 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -74,8 +74,8 @@ class WindowTestUtils {
private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) {
activity.onDisplayChanged(dc);
activity.setOccludesParent(true);
- activity.setVisible(true);
- activity.mVisibleRequested = true;
+ activity.setHidden(false);
+ activity.hiddenRequested = false;
}
static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a7a78e141455..0da9dc40bb4d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -41,7 +41,6 @@ import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
import static org.mockito.Mockito.mock;
import android.content.Context;
-import android.content.res.Configuration;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
@@ -314,7 +313,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
}
- /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
+ /** Creates a {@link ActivityStack} and adds it to the specified {@link DisplayContent}. */
ActivityStack createTaskStackOnDisplay(DisplayContent dc) {
return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
}
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 3f20273a8d61..f42c755e3efd 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -17,6 +17,7 @@
android_app {
name: "startop_test_app",
srcs: [
+ "src/ApplicationBenchmarks.java",
"src/ComplexLayoutInflationActivity.java",
"src/CPUIntensiveBenchmarkActivity.java",
"src/CPUIntensiveBenchmarks.java",
@@ -25,8 +26,8 @@ android_app {
"src/InitCheckOverheadBenchmarkActivity.java",
"src/InitCheckOverheadBenchmarks.java",
"src/LayoutInflationActivity.java",
- "src/NonInteractiveSystemServerBenchmarkActivity.java",
- "src/SystemServerBenchmarkActivity.java",
+ "src/NonInteractiveMicrobenchmarkActivity.java",
+ "src/InteractiveMicrobenchmarkActivity.java",
"src/SystemServerBenchmarks.java",
"src/TextViewInflationActivity.java",
],
diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml
index 235aa0d25e86..adaab61778ed 100644
--- a/startop/apps/test/AndroidManifest.xml
+++ b/startop/apps/test/AndroidManifest.xml
@@ -97,8 +97,8 @@
</activity>
<activity
- android:label="SystemServer Benchmark"
- android:name=".SystemServerBenchmarkActivity"
+ android:label="Interactive Microbenchmarks"
+ android:name=".InteractiveMicrobenchmarkActivity"
android:exported="true" >
<intent-filter>
@@ -109,8 +109,8 @@
</activity>
<activity
- android:label="Non-interactive SystemServer Benchmark"
- android:name=".NonInteractiveSystemServerBenchmarkActivity"
+ android:label="Non-interactive Microbenchmarks"
+ android:name=".NonInteractiveMicrobenchmarkActivity"
android:exported="true" />
</application>
diff --git a/startop/apps/test/src/ApplicationBenchmarks.java b/startop/apps/test/src/ApplicationBenchmarks.java
new file mode 100644
index 000000000000..7d71916e6d8d
--- /dev/null
+++ b/startop/apps/test/src/ApplicationBenchmarks.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.app.Activity;
+import android.view.LayoutInflater;
+
+final class ApplicationBenchmarks {
+
+ public static final void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
+ LayoutInflater inflater = LayoutInflater.from(parent);
+
+ benchmarks.addBenchmark("Complex Layout", () -> {
+ inflater.inflate(R.layout.activity_main, null);
+ });
+
+ benchmarks.addBenchmark("TextView List Layout", () -> {
+ inflater.inflate(R.layout.textview_list, null);
+ });
+
+ benchmarks.addBenchmark("FrameLayout List Layout", () -> {
+ inflater.inflate(R.layout.framelayout_list, null);
+ });
+ }
+}
diff --git a/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java b/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java
index 2ec5308afe14..db3234a72129 100644
--- a/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java
+++ b/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java
@@ -18,7 +18,7 @@ package com.android.startop.test;
import android.os.Bundle;
-public class CPUIntensiveBenchmarkActivity extends SystemServerBenchmarkActivity {
+public class CPUIntensiveBenchmarkActivity extends InteractiveMicrobenchmarkActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.system_server_benchmark_page);
diff --git a/startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java b/startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java
index 3e0e3b113a29..92d8638092d3 100644
--- a/startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java
+++ b/startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java
@@ -18,7 +18,7 @@ package com.android.startop.test;
import android.os.Bundle;
-public class InitCheckOverheadBenchmarkActivity extends SystemServerBenchmarkActivity {
+public class InitCheckOverheadBenchmarkActivity extends InteractiveMicrobenchmarkActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.system_server_benchmark_page);
diff --git a/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/InteractiveMicrobenchmarkActivity.java
index 6be8df3728af..c3839c17e4d8 100644
--- a/startop/apps/test/src/SystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/InteractiveMicrobenchmarkActivity.java
@@ -18,12 +18,13 @@ package com.android.startop.test;
import android.app.Activity;
import android.content.Context;
+import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
-public class SystemServerBenchmarkActivity extends Activity implements BenchmarkRunner {
+public class InteractiveMicrobenchmarkActivity extends Activity implements BenchmarkRunner {
protected GridLayout mBenchmarkList;
protected void onCreate(Bundle savedInstanceState) {
@@ -32,10 +33,34 @@ public class SystemServerBenchmarkActivity extends Activity implements Benchmark
mBenchmarkList = findViewById(R.id.benchmark_list);
+ addBenchmark("Empty", () -> {
+ });
+ addHeader("Application Benchmarks");
+ ApplicationBenchmarks.initializeBenchmarks(this, this);
+ addHeader("CPU Intensive Benchmarks");
+ CPUIntensiveBenchmarks.initializeBenchmarks(this, this);
+ addHeader("Init Check Overhead Benchmarks");
+ InitCheckOverheadBenchmarks.initializeBenchmarks(this, this);
+ addHeader("System Server Benchmarks");
SystemServerBenchmarks.initializeBenchmarks(this, this);
}
/**
+ * Add a heading for a group of related benchmarks
+ *
+ * @param name The name of this group of benchmarks
+ */
+ public void addHeader(CharSequence name) {
+ Context context = mBenchmarkList.getContext();
+ TextView header = new TextView(context);
+ header.setText(name);
+ header.setTypeface(header.getTypeface(), Typeface.BOLD);
+ GridLayout.LayoutParams params = new GridLayout.LayoutParams();
+ params.columnSpec = GridLayout.spec(0, 3);
+ mBenchmarkList.addView(header, params);
+ }
+
+ /**
* Adds a benchmark to the set to run.
*
* @param name A short name that shows up in the UI or benchmark results
diff --git a/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java b/startop/apps/test/src/NonInteractiveMicrobenchmarkActivity.java
index a2dc2cf03d69..0162ac6c5d81 100644
--- a/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/NonInteractiveMicrobenchmarkActivity.java
@@ -36,7 +36,7 @@ import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
-public class NonInteractiveSystemServerBenchmarkActivity extends Activity {
+public class NonInteractiveMicrobenchmarkActivity extends Activity {
ArrayList<CharSequence> benchmarkNames = new ArrayList();
ArrayList<Runnable> benchmarkThunks = new ArrayList();
diff --git a/startop/apps/test/src/SystemServerBenchmarks.java b/startop/apps/test/src/SystemServerBenchmarks.java
index 25b43f4d53f6..8114bc225c23 100644
--- a/startop/apps/test/src/SystemServerBenchmarks.java
+++ b/startop/apps/test/src/SystemServerBenchmarks.java
@@ -57,9 +57,6 @@ class SystemServerBenchmarks {
static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
final String packageName = parent.getPackageName();
- benchmarks.addBenchmark("Empty", () -> {
- });
-
PackageManager pm = parent.getPackageManager();
benchmarks.addBenchmark("getInstalledApplications", () -> {
pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index 95a47e131272..d82e93fac76d 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -237,7 +237,10 @@ public class Session {
// keep track of calls and bail if we hit the recursion limit
private String getFullSessionId(int parentCount) {
if (parentCount >= SESSION_RECURSION_LIMIT) {
- Log.w(LOG_TAG, "getFullSessionId: Hit recursion limit!");
+ // Don't use Telecom's Log.w here or it will cause infinite recursion because it will
+ // try to add session information to this logging statement, which will cause it to hit
+ // this condition again and so on...
+ android.util.Slog.w(LOG_TAG, "getFullSessionId: Hit recursion limit!");
return TRUNCATE_STRING + mSessionId;
}
// Cache mParentSession locally to prevent a concurrency problem where
@@ -265,7 +268,11 @@ public class Session {
Session topNode = this;
while (topNode.getParentSession() != null) {
if (currParentCount >= SESSION_RECURSION_LIMIT) {
- Log.w(LOG_TAG, "getRootSession: Hit recursion limit from " + callingMethod);
+ // Don't use Telecom's Log.w here or it will cause infinite recursion because it
+ // will try to add session information to this logging statement, which will cause
+ // it to hit this condition again and so on...
+ android.util.Slog.w(LOG_TAG, "getRootSession: Hit recursion limit from "
+ + callingMethod);
break;
}
topNode = topNode.getParentSession();
@@ -289,7 +296,10 @@ public class Session {
private void printSessionTree(int tabI, StringBuilder sb, int currChildCount) {
// Prevent infinite recursion.
if (currChildCount >= SESSION_RECURSION_LIMIT) {
- Log.w(LOG_TAG, "printSessionTree: Hit recursion limit!");
+ // Don't use Telecom's Log.w here or it will cause infinite recursion because it will
+ // try to add session information to this logging statement, which will cause it to hit
+ // this condition again and so on...
+ android.util.Slog.w(LOG_TAG, "printSessionTree: Hit recursion limit!");
sb.append(TRUNCATE_STRING);
return;
}
@@ -315,7 +325,10 @@ public class Session {
private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath,
int parentCount) {
if (parentCount >= SESSION_RECURSION_LIMIT) {
- Log.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!");
+ // Don't use Telecom's Log.w here or it will cause infinite recursion because it will
+ // try to add session information to this logging statement, which will cause it to hit
+ // this condition again and so on...
+ android.util.Slog.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!");
sb.append(TRUNCATE_STRING);
return;
}
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 49c3a7205d59..ac300587cef8 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -202,7 +202,18 @@ public class SessionManager {
return createSubsession(false);
}
- private synchronized Session createSubsession(boolean isStartedFromActiveSession) {
+ /**
+ * Creates a new subsession based on an existing session. Will not be started until
+ * {@link #continueSession(Session, String)} or {@link #cancelSubsession(Session)} is called.
+ * <p>
+ * Only public for testing!
+ * @param isStartedFromActiveSession true if this subsession is being created for a task on the
+ * same thread, false if it is being created for a related task on another thread.
+ * @return a new {@link Session}, call {@link #continueSession(Session, String)} to continue the
+ * session and {@link #endSession()} when done with this subsession.
+ */
+ @VisibleForTesting
+ public synchronized Session createSubsession(boolean isStartedFromActiveSession) {
int threadId = getCallingThreadId();
Session threadSession = mSessionMapper.get(threadId);
if (threadSession == null) {
diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
new file mode 100644
index 000000000000..922af126e73e
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import com.android.internal.os.BackgroundThread;
+
+/**
+ * Helper class for monitoring the state of packages: adding, removing,
+ * updating, and disappearing and reappearing on the SD card.
+ */
+public abstract class PackageChangeReceiver extends BroadcastReceiver {
+ static final IntentFilter sPackageIntentFilter = new IntentFilter();
+ static {
+ sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ sPackageIntentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+ sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ sPackageIntentFilter.addDataScheme("package");
+ }
+ Context mRegisteredContext;
+
+ /**
+ * To register the intents that needed for monitoring the state of packages
+ */
+ public void register(@NonNull Context context, @Nullable Looper thread,
+ @Nullable UserHandle user) {
+ if (mRegisteredContext != null) {
+ throw new IllegalStateException("Already registered");
+ }
+ Handler handler = (thread == null) ? BackgroundThread.getHandler() : new Handler(thread);
+ mRegisteredContext = context;
+ if (handler != null) {
+ if (user != null) {
+ context.registerReceiverAsUser(this, user, sPackageIntentFilter, null, handler);
+ } else {
+ context.registerReceiver(this, sPackageIntentFilter,
+ null, handler);
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * To unregister the intents for monitoring the state of packages
+ */
+ public void unregister() {
+ if (mRegisteredContext == null) {
+ throw new IllegalStateException("Not registered");
+ }
+ mRegisteredContext.unregisterReceiver(this);
+ mRegisteredContext = null;
+ }
+
+ /**
+ * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED
+ */
+ public void onPackageAdded(@Nullable String packageName) {
+ }
+
+ /**
+ * This method is invoked when receive the Intent.ACTION_PACKAGE_REMOVED
+ */
+ public void onPackageRemoved(@Nullable String packageName) {
+ }
+
+ /**
+ * This method is invoked when Intent.EXTRA_REPLACING as extra field is true
+ */
+ public void onPackageUpdateFinished(@Nullable String packageName) {
+ }
+
+ /**
+ * This method is invoked when receive the Intent.ACTION_PACKAGE_CHANGED or
+ * Intent.EXTRA_REPLACING as extra field is true
+ */
+ public void onPackageModified(@Nullable String packageName) {
+ }
+
+ /**
+ * This method is invoked when receive the Intent.ACTION_QUERY_PACKAGE_RESTART and
+ * Intent.ACTION_PACKAGE_RESTARTED
+ */
+ public void onHandleForceStop(@Nullable String[] packages, boolean doit) {
+ }
+
+ /**
+ * This method is invoked when receive the Intent.ACTION_PACKAGE_REMOVED
+ */
+ public void onPackageDisappeared() {
+ }
+
+ /**
+ * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED
+ */
+ public void onPackageAppeared() {
+ }
+
+ @Override
+ public void onReceive(@Nullable Context context, @Nullable Intent intent) {
+ String action = intent.getAction();
+
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ String pkg = getPackageName(intent);
+ if (pkg != null) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ onPackageUpdateFinished(pkg);
+ onPackageModified(pkg);
+ } else {
+ onPackageAdded(pkg);
+ }
+ onPackageAppeared();
+ }
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ String pkg = getPackageName(intent);
+ if (pkg != null) {
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ onPackageRemoved(pkg);
+ }
+ onPackageDisappeared();
+ }
+ } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ String pkg = getPackageName(intent);
+ if (pkg != null) {
+ onPackageModified(pkg);
+ }
+ } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
+ String[] disappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+ onHandleForceStop(disappearingPackages, false);
+ } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
+ String[] disappearingPackages = new String[] {getPackageName(intent)};
+ onHandleForceStop(disappearingPackages, true);
+ }
+ }
+
+ String getPackageName(Intent intent) {
+ Uri uri = intent.getData();
+ String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+ return pkg;
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index f4eae8ef4b2a..70ce1bfc4bff 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -39,11 +39,11 @@ import android.os.Process;
import android.os.UserHandle;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
+import android.telephony.PackageChangeReceiver;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.util.Log;
-import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -777,7 +777,7 @@ public final class SmsApplication {
* Tracks package changes and ensures that the default SMS app is always configured to be the
* preferred activity for SENDTO sms/mms intents.
*/
- private static final class SmsPackageMonitor extends PackageMonitor {
+ private static final class SmsPackageMonitor extends PackageChangeReceiver {
final Context mContext;
public SmsPackageMonitor(Context context) {
@@ -786,12 +786,12 @@ public final class SmsApplication {
}
@Override
- public void onPackageDisappeared(String packageName, int reason) {
+ public void onPackageDisappeared() {
onPackageChanged();
}
@Override
- public void onPackageAppeared(String packageName, int reason) {
+ public void onPackageAppeared() {
onPackageChanged();
}
@@ -824,7 +824,7 @@ public final class SmsApplication {
public static void initSmsPackageMonitor(Context context) {
sSmsPackageMonitor = new SmsPackageMonitor(context);
- sSmsPackageMonitor.register(context, context.getMainLooper(), UserHandle.ALL, false);
+ sSmsPackageMonitor.register(context, context.getMainLooper(), UserHandle.ALL);
}
@UnsupportedAppUsage
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a315e6d39935..2cdf21ddca04 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3797,7 +3797,8 @@ public class CarrierConfigManager {
+ " ICarrierConfigLoader is null");
return null;
}
- return loader.getConfigForSubId(subId, mContext.getOpPackageName());
+ return loader.getConfigForSubIdWithFeature(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
Rlog.e(TAG, "Error getting config for subId " + subId + ": "
+ ex.toString());
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 407ced71a0e7..270eafe642b7 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -203,9 +203,12 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
}
/**
+ * Get whether network has configured carrier aggregation or not.
+ *
* @return {@code true} if using carrier aggregation.
* @hide
*/
+ @SystemApi
public boolean isUsingCarrierAggregation() {
return mIsUsingCarrierAggregation;
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 56b723631115..fc717e7465b1 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -323,9 +323,12 @@ public final class NetworkRegistrationInfo implements Parcelable {
public @Domain int getDomain() { return mDomain; }
/**
+ * Get the 5G NR connection state.
+ *
* @return the 5G NR connection state.
* @hide
*/
+ @SystemApi
public @NRState int getNrState() {
return mNrState;
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f5033487ad1f..9ace86cea0c1 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1384,9 +1384,12 @@ public class ServiceState implements Parcelable {
}
/**
+ * Get the 5G NR frequency range the device is currently registered.
+ *
* @return the frequency range of 5G NR.
* @hide
*/
+ @SystemApi
public @FrequencyRange int getNrFrequencyRange() {
return mNrFrequencyRange;
}
@@ -1464,44 +1467,44 @@ public class ServiceState implements Parcelable {
/** @hide */
public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rat) {
switch(rat) {
- case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
+ case RIL_RADIO_TECHNOLOGY_GPRS:
return TelephonyManager.NETWORK_TYPE_GPRS;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
+ case RIL_RADIO_TECHNOLOGY_EDGE:
return TelephonyManager.NETWORK_TYPE_EDGE;
- case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
+ case RIL_RADIO_TECHNOLOGY_UMTS:
return TelephonyManager.NETWORK_TYPE_UMTS;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
+ case RIL_RADIO_TECHNOLOGY_HSDPA:
return TelephonyManager.NETWORK_TYPE_HSDPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
+ case RIL_RADIO_TECHNOLOGY_HSUPA:
return TelephonyManager.NETWORK_TYPE_HSUPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
+ case RIL_RADIO_TECHNOLOGY_HSPA:
return TelephonyManager.NETWORK_TYPE_HSPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
- case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
+ case RIL_RADIO_TECHNOLOGY_IS95A:
+ case RIL_RADIO_TECHNOLOGY_IS95B:
return TelephonyManager.NETWORK_TYPE_CDMA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
+ case RIL_RADIO_TECHNOLOGY_1xRTT:
return TelephonyManager.NETWORK_TYPE_1xRTT;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
+ case RIL_RADIO_TECHNOLOGY_EVDO_0:
return TelephonyManager.NETWORK_TYPE_EVDO_0;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
+ case RIL_RADIO_TECHNOLOGY_EVDO_A:
return TelephonyManager.NETWORK_TYPE_EVDO_A;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
+ case RIL_RADIO_TECHNOLOGY_EVDO_B:
return TelephonyManager.NETWORK_TYPE_EVDO_B;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
+ case RIL_RADIO_TECHNOLOGY_EHRPD:
return TelephonyManager.NETWORK_TYPE_EHRPD;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+ case RIL_RADIO_TECHNOLOGY_LTE:
return TelephonyManager.NETWORK_TYPE_LTE;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
+ case RIL_RADIO_TECHNOLOGY_HSPAP:
return TelephonyManager.NETWORK_TYPE_HSPAP;
- case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
+ case RIL_RADIO_TECHNOLOGY_GSM:
return TelephonyManager.NETWORK_TYPE_GSM;
- case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
+ case RIL_RADIO_TECHNOLOGY_IWLAN:
return TelephonyManager.NETWORK_TYPE_IWLAN;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
+ case RIL_RADIO_TECHNOLOGY_LTE_CA:
return TelephonyManager.NETWORK_TYPE_LTE_CA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_NR:
+ case RIL_RADIO_TECHNOLOGY_NR:
return TelephonyManager.NETWORK_TYPE_NR;
default:
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
@@ -1546,45 +1549,45 @@ public class ServiceState implements Parcelable {
public static int networkTypeToRilRadioTechnology(int networkType) {
switch(networkType) {
case TelephonyManager.NETWORK_TYPE_GPRS:
- return ServiceState.RIL_RADIO_TECHNOLOGY_GPRS;
+ return RIL_RADIO_TECHNOLOGY_GPRS;
case TelephonyManager.NETWORK_TYPE_EDGE:
- return ServiceState.RIL_RADIO_TECHNOLOGY_EDGE;
+ return RIL_RADIO_TECHNOLOGY_EDGE;
case TelephonyManager.NETWORK_TYPE_UMTS:
- return ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+ return RIL_RADIO_TECHNOLOGY_UMTS;
case TelephonyManager.NETWORK_TYPE_HSDPA:
- return ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA;
+ return RIL_RADIO_TECHNOLOGY_HSDPA;
case TelephonyManager.NETWORK_TYPE_HSUPA:
- return ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA;
+ return RIL_RADIO_TECHNOLOGY_HSUPA;
case TelephonyManager.NETWORK_TYPE_HSPA:
- return ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ return RIL_RADIO_TECHNOLOGY_HSPA;
case TelephonyManager.NETWORK_TYPE_CDMA:
- return ServiceState.RIL_RADIO_TECHNOLOGY_IS95A;
+ return RIL_RADIO_TECHNOLOGY_IS95A;
case TelephonyManager.NETWORK_TYPE_1xRTT:
- return ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT;
+ return RIL_RADIO_TECHNOLOGY_1xRTT;
case TelephonyManager.NETWORK_TYPE_EVDO_0:
- return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0;
+ return RIL_RADIO_TECHNOLOGY_EVDO_0;
case TelephonyManager.NETWORK_TYPE_EVDO_A:
- return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A;
+ return RIL_RADIO_TECHNOLOGY_EVDO_A;
case TelephonyManager.NETWORK_TYPE_EVDO_B:
- return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B;
+ return RIL_RADIO_TECHNOLOGY_EVDO_B;
case TelephonyManager.NETWORK_TYPE_EHRPD:
- return ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD;
+ return RIL_RADIO_TECHNOLOGY_EHRPD;
case TelephonyManager.NETWORK_TYPE_LTE:
- return ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ return RIL_RADIO_TECHNOLOGY_LTE;
case TelephonyManager.NETWORK_TYPE_HSPAP:
- return ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP;
+ return RIL_RADIO_TECHNOLOGY_HSPAP;
case TelephonyManager.NETWORK_TYPE_GSM:
- return ServiceState.RIL_RADIO_TECHNOLOGY_GSM;
+ return RIL_RADIO_TECHNOLOGY_GSM;
case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
- return ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+ return RIL_RADIO_TECHNOLOGY_TD_SCDMA;
case TelephonyManager.NETWORK_TYPE_IWLAN:
- return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+ return RIL_RADIO_TECHNOLOGY_IWLAN;
case TelephonyManager.NETWORK_TYPE_LTE_CA:
- return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
+ return RIL_RADIO_TECHNOLOGY_LTE_CA;
case TelephonyManager.NETWORK_TYPE_NR:
- return ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ return RIL_RADIO_TECHNOLOGY_NR;
default:
- return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ return RIL_RADIO_TECHNOLOGY_UNKNOWN;
}
}
@@ -1950,8 +1953,11 @@ public class ServiceState implements Parcelable {
/**
* The current registered raw data network operator name in long alphanumeric format.
*
+ * @return long raw name of operator, null if unregistered or unknown
* @hide
*/
+ @Nullable
+ @SystemApi
public String getOperatorAlphaLongRaw() {
return mOperatorAlphaLongRaw;
}
@@ -1966,8 +1972,11 @@ public class ServiceState implements Parcelable {
/**
* The current registered raw data network operator name in short alphanumeric format.
*
+ * @return short raw name of operator, null if unregistered or unknown
* @hide
*/
+ @Nullable
+ @SystemApi
public String getOperatorAlphaShortRaw() {
return mOperatorAlphaShortRaw;
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 1309b4d5f872..89c4e905f503 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -514,7 +514,6 @@ public final class SmsManager {
throw new IllegalArgumentException("Invalid message body");
}
- final Context context = ActivityThread.currentApplication().getApplicationContext();
// We will only show the SMS disambiguation dialog in the case that the message is being
// persisted. This is for two reasons:
// 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
@@ -623,7 +622,6 @@ public final class SmsManager {
final int finalPriority = priority;
final int finalValidity = validityPeriod;
- final Context context = ActivityThread.currentApplication().getApplicationContext();
// We will only show the SMS disambiguation dialog in the case that the message is being
// persisted. This is for two reasons:
// 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
@@ -927,7 +925,6 @@ public final class SmsManager {
}
if (parts.size() > 1) {
- final Context context = ActivityThread.currentApplication().getApplicationContext();
// We will only show the SMS disambiguation dialog in the case that the message is being
// persisted. This is for two reasons:
// 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
@@ -1168,7 +1165,6 @@ public final class SmsManager {
if (parts.size() > 1) {
final int finalPriority = priority;
final int finalValidity = validityPeriod;
- final Context context = ActivityThread.currentApplication().getApplicationContext();
if (persistMessage) {
resolveSubscriptionForOperation(new SubscriptionResolverResult() {
@Override
@@ -1325,7 +1321,6 @@ public final class SmsManager {
throw new IllegalArgumentException("Invalid message data");
}
- final Context context = ActivityThread.currentApplication().getApplicationContext();
resolveSubscriptionForOperation(new SubscriptionResolverResult() {
@Override
public void onSuccess(int subId) {
@@ -2659,7 +2654,7 @@ public final class SmsManager {
ISms iccISms = getISmsServiceOrThrow();
if (iccISms != null) {
return iccISms.checkSmsShortCodeDestination(getSubscriptionId(),
- ActivityThread.currentPackageName(), destAddress, countryIso);
+ ActivityThread.currentPackageName(), null, destAddress, countryIso);
}
} catch (RemoteException e) {
Log.e(TAG, "checkSmsShortCodeDestination() RemoteException", e);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index f527bc3b6df6..9eff809eaf5d 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -654,6 +654,7 @@ public class SubscriptionInfo implements Parcelable {
* Return whether the subscription's group is disabled.
* @hide
*/
+ @SystemApi
public boolean isGroupDisabled() {
return mIsGroupDisabled;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index dfcfed715523..3d63e4a37604 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1154,7 +1154,8 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName());
+ subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -1182,7 +1183,8 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName());
+ result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -1216,7 +1218,7 @@ public class SubscriptionManager {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -1239,7 +1241,8 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getAllSubInfoList(mContext.getOpPackageName());
+ result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -1283,18 +1286,39 @@ public class SubscriptionManager {
}
/**
- * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
- * is true, it will filter out the hidden subscriptions.
+ * Get both hidden and visible SubscriptionInfo(s) of the currently active SIM(s).
+ * The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex}
+ * then by {@link SubscriptionInfo#getSubscriptionId}.
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
+ * to the calling app are returned.
+ *
+ * @return Sorted list of the currently available {@link SubscriptionInfo}
+ * records on the device.
+ * This is similar to {@link #getActiveSubscriptionInfoList} except that it will return
+ * both active and hidden SubscriptionInfos.
*
- * @hide
*/
- public List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+ public @Nullable List<SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList() {
+ return getActiveSubscriptionInfoList(/* userVisibleonly */false);
+ }
+
+ /**
+ * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
+ * is true, it will filter out the hidden subscriptions.
+ *
+ * @hide
+ */
+ public @Nullable List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
List<SubscriptionInfo> activeList = null;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
+ activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -1344,7 +1368,8 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName());
+ result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -1461,7 +1486,8 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getAllSubInfoCount(mContext.getOpPackageName());
+ result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -1489,7 +1515,8 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getActiveSubInfoCount(mContext.getOpPackageName());
+ result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -2227,7 +2254,7 @@ public class SubscriptionManager {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
resultValue = iSub.getSubscriptionProperty(subId, propKey,
- context.getOpPackageName());
+ context.getOpPackageName(), context.getFeatureId());
}
} catch (RemoteException ex) {
// ignore it
@@ -2350,7 +2377,8 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- return iSub.isActiveSubId(subId, mContext.getOpPackageName());
+ return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
}
@@ -2715,13 +2743,14 @@ public class SubscriptionManager {
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions() {
- String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String contextFeature = mContext != null ? mContext.getFeatureId() : null;
List<SubscriptionInfo> subInfoList = null;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- subInfoList = iSub.getOpportunisticSubscriptions(pkgForDebug);
+ subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
}
} catch (RemoteException ex) {
// ignore it
@@ -2959,7 +2988,8 @@ public class SubscriptionManager {
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public @NonNull List<SubscriptionInfo> getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid) {
Preconditions.checkNotNull(groupUuid, "groupUuid can't be null");
- String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String contextFeature = mContext != null ? mContext.getFeatureId() : null;
if (VDBG) {
logd("[getSubscriptionsInGroup]+ groupUuid:" + groupUuid);
}
@@ -2968,7 +2998,7 @@ public class SubscriptionManager {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getSubscriptionsInGroup(groupUuid, pkgForDebug);
+ result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
} else {
if (!isSystemProcess()) {
throw new IllegalStateException("telephony service is null.");
@@ -3182,13 +3212,14 @@ public class SubscriptionManager {
}
/**
- * Get active data subscription id.
- * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details.
+ * Get active data subscription id. Active data subscription refers to the subscription
+ * currently chosen to provide cellular internet connection to the user. This may be
+ * different from getDefaultDataSubscriptionId(). Eg. Opportunistics data
*
- * @return Active data subscription id
+ * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details.
*
- * //TODO: Refactor this API in b/134702460
- * @hide
+ * @return Active data subscription id if any is chosen, or
+ * SubscriptionManager.INVALID_SUBSCRIPTION_ID if not.
*/
public static int getActiveDataSubscriptionId() {
try {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 864bf03c1448..64b5aa5539c7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1689,7 +1689,8 @@ public class TelephonyManager {
if (telephony == null) return null;
try {
- return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName());
+ return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1730,7 +1731,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
- return telephony.getDeviceId(mContext.getOpPackageName());
+ return telephony.getDeviceIdWithFeature(mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1774,7 +1776,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName());
+ return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1832,7 +1835,7 @@ public class TelephonyManager {
if (telephony == null) return null;
try {
- return telephony.getImeiForSlot(slotIndex, getOpPackageName());
+ return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1926,7 +1929,7 @@ public class TelephonyManager {
if (telephony == null) return null;
try {
- String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName());
+ String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(), getFeatureId());
if (TextUtils.isEmpty(meid)) {
Log.d(TAG, "getMeid: return null because MEID is not available");
return null;
@@ -2027,7 +2030,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName());
+ String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Rlog.v(TAG, "Nai = " + nai);
}
@@ -2578,7 +2582,7 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null) return "";
return telephony.getNetworkCountryIsoForPhone(getPhoneId(),
- null /* no permission check */);
+ null /* no permission check */, null);
} catch (RemoteException ex) {
return "";
}
@@ -2618,7 +2622,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null) return "";
- return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName());
+ return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
return "";
}
@@ -2722,7 +2727,8 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
+ return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_UNKNOWN;
@@ -2786,7 +2792,8 @@ public class TelephonyManager {
try{
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName());
+ return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_UNKNOWN;
@@ -2822,7 +2829,8 @@ public class TelephonyManager {
try{
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName());
+ return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_UNKNOWN;
@@ -3566,13 +3574,36 @@ public class TelephonyManager {
}
/**
+ * Returns the ISO-3166 country code equivalent for the SIM provider's country code
+ * of the default subscription
+ * <p>
+ * The ISO-3166 country code is provided in lowercase 2 character format.
+ * @return the lowercase 2 character ISO-3166 country code, or empty string is not available.
+ * <p>
+ * Note: This API is introduced to unblock mainlining work as the following APIs in
+ * Linkify.java invokes getSimCountryIso() without a context. TODO(Bug 144576376): remove
+ * this API once the following APIs are redesigned to access telephonymanager with a context.
+ *
+ * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask)}
+ * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask,
+ @Nullable Function<String, URLSpan> urlSpanFactory)}
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public static String getDefaultSimCountryIso() {
+ return getSimCountryIso(SubscriptionManager.getDefaultSubscriptionId());
+ }
+
+ /**
* Returns the ISO country code equivalent for the SIM provider's country code.
*
* @param subId for which SimCountryIso is returned
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- public String getSimCountryIso(int subId) {
+ public static String getSimCountryIso(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getSimCountryIsoForPhone(phoneId);
}
@@ -3583,7 +3614,7 @@ public class TelephonyManager {
* @hide
*/
@UnsupportedAppUsage
- public String getSimCountryIsoForPhone(int phoneId) {
+ public static String getSimCountryIsoForPhone(int phoneId) {
return getTelephonyProperty(phoneId, TelephonyProperties.icc_operator_iso_country(), "");
}
@@ -3647,7 +3678,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName());
+ return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -3691,7 +3723,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
- return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName());
+ return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
// Assume no ICC card if remote exception which shouldn't happen
return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
@@ -3919,7 +3952,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName());
+ return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4086,7 +4120,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName());
+ return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4109,7 +4144,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName());
+ return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4159,7 +4195,8 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName());
+ number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
@@ -4170,7 +4207,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName());
+ return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4249,7 +4287,7 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony != null)
alphaTag = telephony.getLine1AlphaTagForDisplay(subId,
- getOpPackageName());
+ getOpPackageName(), getFeatureId());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
@@ -4260,7 +4298,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName());
+ return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4289,7 +4328,8 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName());
+ return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
@@ -4344,7 +4384,7 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getMsisdnForSubscriber(subId, getOpPackageName());
+ return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4378,7 +4418,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName());
+ return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4502,8 +4543,8 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony
- .getVisualVoicemailPackageName(mContext.getOpPackageName(), getSubId());
+ return telephony.getVisualVoicemailPackageName(mContext.getOpPackageName(),
+ getFeatureId(), getSubId());
}
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
@@ -4950,7 +4991,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return 0;
- return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName());
+ return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
return 0;
} catch (NullPointerException ex) {
@@ -4986,7 +5028,8 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName());
+ return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -5365,7 +5408,7 @@ public class TelephonyManager {
} else if (listener.mSubId != null) {
subId = listener.mSubId;
}
- registry.listenForSubscriber(subId, getOpPackageName(),
+ registry.listenForSubscriber(subId, getOpPackageName(), getFeatureId(),
listener.callback, events, notifyNow);
} else {
Rlog.w(TAG, "telephony registry not ready.");
@@ -5395,7 +5438,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return -1;
- return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName());
+ return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
// the phone process is restarting.
return -1;
@@ -5430,7 +5474,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return -1;
- return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName());
+ return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
// the phone process is restarting.
return -1;
@@ -5461,7 +5506,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
- return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName());
+ return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
// the phone process is restarting.
return null;
@@ -6964,7 +7010,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
- return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName());
+ return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName(),
+ getFeatureId());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -6997,7 +7044,7 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null) return 0;
return telephony.setForbiddenPlmns(
- getSubId(), APPTYPE_USIM, fplmns, getOpPackageName());
+ getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getFeatureId());
} catch (RemoteException ex) {
Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage());
} catch (NullPointerException ex) {
@@ -7018,7 +7065,7 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return new String[0];
- return telephony.getPcscfAddress(apnType, getOpPackageName());
+ return telephony.getPcscfAddress(apnType, getOpPackageName(), getFeatureId());
} catch (RemoteException e) {
return new String[0];
}
@@ -8249,7 +8296,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.isRadioOn(getOpPackageName());
+ return telephony.isRadioOnWithFeature(getOpPackageName(), getFeatureId());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isRadioOn", e);
}
@@ -8524,7 +8571,8 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName());
+ return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
@@ -8882,7 +8930,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.isVideoCallingEnabled(getOpPackageName());
+ return telephony.isVideoCallingEnabled(getOpPackageName(), getFeatureId());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isVideoCallingEnabled", e);
}
@@ -8898,7 +8946,8 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName());
+ return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName(),
+ getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e);
@@ -8917,7 +8966,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.isWorldPhone(mSubId, getOpPackageName());
+ return telephony.isWorldPhone(mSubId, getOpPackageName(), getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isWorldPhone", e);
@@ -9640,7 +9689,7 @@ public class TelephonyManager {
ITelephony service = getITelephony();
if (service != null) {
retval = service.getSubIdForPhoneAccountHandle(
- phoneAccountHandle, mContext.getOpPackageName());
+ phoneAccountHandle, mContext.getOpPackageName(), mContext.getFeatureId());
}
} catch (RemoteException ex) {
Log.e(TAG, "getSubscriptionId RemoteException", ex);
@@ -10502,7 +10551,7 @@ public class TelephonyManager {
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.getClientRequestStats(getOpPackageName(), subId);
+ return service.getClientRequestStats(getOpPackageName(), getFeatureId(), subId);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e);
@@ -10790,7 +10839,7 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getNumberOfModemsWithSimultaneousDataConnections(
- getSubId(), getOpPackageName());
+ getSubId(), getOpPackageName(), getFeatureId());
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
@@ -10813,6 +10862,7 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
public boolean setOpportunisticNetworkState(boolean enable) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
boolean ret = false;
@@ -10840,6 +10890,7 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
public boolean isOpportunisticNetworkEnabled() {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
boolean isEnabled = false;
@@ -11092,7 +11143,8 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getEmergencyNumberList(mContext.getOpPackageName());
+ return telephony.getEmergencyNumberList(mContext.getOpPackageName(),
+ mContext.getFeatureId());
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -11147,7 +11199,7 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony != null) {
emergencyNumberList = telephony.getEmergencyNumberList(
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getFeatureId());
if (emergencyNumberList != null) {
for (Integer subscriptionId : emergencyNumberList.keySet()) {
List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId);
@@ -11368,12 +11420,14 @@ public class TelephonyManager {
android.Manifest.permission.READ_PHONE_STATE
})
public int getPreferredOpportunisticDataSubscription() {
- String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String packageName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String featureId = mContext != null ? mContext.getFeatureId() : null;
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
try {
IOns iOpportunisticNetworkService = getIOns();
if (iOpportunisticNetworkService != null) {
- subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(pkgForDebug);
+ subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(
+ packageName, featureId);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex);
@@ -11477,11 +11531,13 @@ public class TelephonyManager {
* @param slotIndex which slot it's checking.
* @hide
*/
+ @SystemApi
public boolean isModemEnabledForSlot(int slotIndex) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName());
+ return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException ex) {
Log.e(TAG, "enableModem RemoteException", ex);
@@ -11586,7 +11642,7 @@ public class TelephonyManager {
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.isMultiSimSupported(getOpPackageName());
+ return service.isMultiSimSupported(getOpPackageName(), getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "isMultiSimSupported RemoteException", e);
@@ -11638,7 +11694,7 @@ public class TelephonyManager {
ITelephony service = getITelephony();
if (service != null) {
return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(),
- getOpPackageName());
+ getOpPackageName(), getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e);
diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
index 8e50a8f9d7d5..ee09c1c9c62f 100644
--- a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
+++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
@@ -23,9 +23,13 @@ import android.os.PersistableBundle;
*/
interface ICarrierConfigLoader {
+ /** @deprecated Use {@link #getConfigForSubIdWithFeature(int, String, String) instead */
@UnsupportedAppUsage
PersistableBundle getConfigForSubId(int subId, String callingPackage);
+ PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
+ String callingFeatureId);
+
void overrideConfig(int subId, in PersistableBundle overrides);
void notifyConfigChangedForSubId(int subId);
diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl
index 2c48b6512421..76b6951f213d 100755
--- a/telephony/java/com/android/internal/telephony/IOns.aidl
+++ b/telephony/java/com/android/internal/telephony/IOns.aidl
@@ -83,7 +83,7 @@ interface IOns {
* subscription id
*
*/
- int getPreferredDataSubscriptionId(String callingPackage);
+ int getPreferredDataSubscriptionId(String callingPackage, String callingFeatureId);
/**
* Update availability of a list of networks in the current location.
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 15e7fc26ed47..28ef235bf398 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -24,114 +24,128 @@ import android.telephony.ImsiEncryptionInfo;
*/
interface IPhoneSubInfo {
+ /** @deprecated Use {@link #getDeviceIdWithFeature(String, String) instead */
+ @UnsupportedAppUsage
+ String getDeviceId(String callingPackage);
+
/**
* Retrieves the unique device ID, e.g., IMEI for GSM phones.
*/
- @UnsupportedAppUsage
- String getDeviceId(String callingPackage);
+ String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);
/**
* Retrieves the unique Network Access ID
*/
- String getNaiForSubscriber(int subId, String callingPackage);
+ String getNaiForSubscriber(int subId, String callingPackage, String callingFeatureId);
/**
* Retrieves the unique device ID of a phone for the device, e.g., IMEI
* for GSM phones.
*/
- String getDeviceIdForPhone(int phoneId, String callingPackage);
+ String getDeviceIdForPhone(int phoneId, String callingPackage, String callingFeatureId);
/**
* Retrieves the IMEI.
*/
- String getImeiForSubscriber(int subId, String callingPackage);
+ String getImeiForSubscriber(int subId, String callingPackage, String callingFeatureId);
/**
* Retrieves the software version number for the device, e.g., IMEI/SV
* for GSM phones.
*/
- String getDeviceSvn(String callingPackage);
+ String getDeviceSvn(String callingPackage, String callingFeatureId);
/**
* Retrieves the software version number of a subId for the device, e.g., IMEI/SV
* for GSM phones.
*/
- String getDeviceSvnUsingSubId(int subId, String callingPackage);
+ String getDeviceSvnUsingSubId(int subId, String callingPackage, String callingFeatureId);
+
+ /** @deprecated Use {@link #getSubscriberIdWithFeature(String, String) instead */
+ @UnsupportedAppUsage
+ String getSubscriberId(String callingPackage);
/**
* Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones.
*/
- @UnsupportedAppUsage
- String getSubscriberId(String callingPackage);
+ String getSubscriberIdWithFeature(String callingPackage, String callingComponenId);
/**
* Retrieves the unique subscriber ID of a given subId, e.g., IMSI for GSM phones.
*/
- String getSubscriberIdForSubscriber(int subId, String callingPackage);
+ String getSubscriberIdForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Retrieves the Group Identifier Level1 for GSM phones of a subId.
*/
- String getGroupIdLevel1ForSubscriber(int subId, String callingPackage);
+ String getGroupIdLevel1ForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
+
+ /** @deprecared Use {@link getIccSerialNumberWithFeature(String, String)} instead */
+ @UnsupportedAppUsage
+ String getIccSerialNumber(String callingPackage);
/**
* Retrieves the serial number of the ICC, if applicable.
*/
- @UnsupportedAppUsage
- String getIccSerialNumber(String callingPackage);
+ String getIccSerialNumberWithFeature(String callingPackage, String callingFeatureId);
/**
* Retrieves the serial number of a given subId.
*/
- String getIccSerialNumberForSubscriber(int subId, String callingPackage);
+ String getIccSerialNumberForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Retrieves the phone number string for line 1.
*/
- String getLine1Number(String callingPackage);
+ String getLine1Number(String callingPackage, String callingFeatureId);
/**
* Retrieves the phone number string for line 1 of a subcription.
*/
- String getLine1NumberForSubscriber(int subId, String callingPackage);
+ String getLine1NumberForSubscriber(int subId, String callingPackage, String callingFeatureId);
/**
* Retrieves the alpha identifier for line 1.
*/
- String getLine1AlphaTag(String callingPackage);
+ String getLine1AlphaTag(String callingPackage, String callingFeatureId);
/**
* Retrieves the alpha identifier for line 1 of a subId.
*/
- String getLine1AlphaTagForSubscriber(int subId, String callingPackage);
+ String getLine1AlphaTagForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Retrieves MSISDN Number.
*/
- String getMsisdn(String callingPackage);
+ String getMsisdn(String callingPackage, String callingFeatureId);
/**
* Retrieves the Msisdn of a subId.
*/
- String getMsisdnForSubscriber(int subId, String callingPackage);
+ String getMsisdnForSubscriber(int subId, String callingPackage, String callingFeatureId);
/**
* Retrieves the voice mail number.
*/
- String getVoiceMailNumber(String callingPackage);
+ String getVoiceMailNumber(String callingPackage, String callingFeatureId);
/**
* Retrieves the voice mail number of a given subId.
*/
- String getVoiceMailNumberForSubscriber(int subId, String callingPackage);
+ String getVoiceMailNumberForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Retrieves the Carrier information used to encrypt IMSI and IMPI.
*/
ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int subId, int keyType,
- String callingPackage);
+ String callingPackage);
/**
* Stores the Carrier information used to encrypt IMSI and IMPI.
@@ -149,13 +163,14 @@ interface IPhoneSubInfo {
/**
* Retrieves the alpha identifier associated with the voice mail number.
*/
- String getVoiceMailAlphaTag(String callingPackage);
+ String getVoiceMailAlphaTag(String callingPackage, String callingFeatureId);
/**
* Retrieves the alpha identifier associated with the voice mail number
* of a subId.
*/
- String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage);
+ String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 91aa3ce62cf2..ac4c8ecea842 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -573,7 +573,8 @@ interface ISms {
*
* @param destAddress the destination address to test for possible short code
*/
- int checkSmsShortCodeDestination(int subId, String callingApk, String destAddress, String countryIso);
+ int checkSmsShortCodeDestination(int subId, String callingApk, String callingFeatureId,
+ String destAddress, String countryIso);
/**
* Gets the SMSC address from (U)SIM.
diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
index d9d4b6002206..9865f76d2580 100644
--- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -18,7 +18,6 @@ package com.android.internal.telephony;
import android.app.PendingIntent;
import android.net.Uri;
-import android.os.Bundle;
import java.util.List;
@@ -197,8 +196,8 @@ public class ISmsImplBase extends ISms.Stub {
}
@Override
- public int checkSmsShortCodeDestination(
- int subid, String callingApk, String destAddress, String countryIso) {
+ public int checkSmsShortCodeDestination(int subid, String callingPackage,
+ String callingFeatureId, String destAddress, String countryIso) {
throw new UnsupportedOperationException();
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 7cc37d0d2b15..151aae89a3ff 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -23,47 +23,56 @@ import com.android.internal.telephony.ISetOpportunisticDataCallback;
interface ISub {
/**
* @param callingPackage The package maing the call.
+ * @param callingFeatureId The feature in the package
* @return a list of all subscriptions in the database, this includes
* all subscriptions that have been seen.
*/
- List<SubscriptionInfo> getAllSubInfoList(String callingPackage);
+ List<SubscriptionInfo> getAllSubInfoList(String callingPackage, String callingFeatureId);
/**
* @param callingPackage The package maing the call.
+ * @param callingFeatureId The feature in the package
* @return the count of all subscriptions in the database, this includes
* all subscriptions that have been seen.
*/
- int getAllSubInfoCount(String callingPackage);
+ int getAllSubInfoCount(String callingPackage, String callingFeatureId);
/**
* Get the active SubscriptionInfo with the subId key
* @param subId The unique SubscriptionInfo key in database
* @param callingPackage The package maing the call.
+ * @param callingFeatureId The feature in the package
* @return SubscriptionInfo, maybe null if its not active
*/
- SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage);
+ SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Get the active SubscriptionInfo associated with the iccId
* @param iccId the IccId of SIM card
* @param callingPackage The package maing the call.
+ * @param callingFeatureId The feature in the package
* @return SubscriptionInfo, maybe null if its not active
*/
- SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage);
+ SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage,
+ String callingFeatureId);
/**
* Get the active SubscriptionInfo associated with the slotIndex
* @param slotIndex the slot which the subscription is inserted
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package
* @return SubscriptionInfo, null for Remote-SIMs or non-active slotIndex.
*/
- SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex, String callingPackage);
+ SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex, String callingPackage,
+ String callingFeatureId);
/**
* Get the SubscriptionInfo(s) of the active subscriptions. The records will be sorted
* by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
*
* @param callingPackage The package maing the call.
+ * @param callingFeatureId The feature in the package
* @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
* <ul>
* <li>
@@ -80,13 +89,15 @@ interface ISub {
* </li>
* </ul>
*/
- List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage);
+ List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage,
+ String callingFeatureId);
/**
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* @return the number of active subscriptions
*/
- int getActiveSubInfoCount(String callingPackage);
+ int getActiveSubInfoCount(String callingPackage, String callingFeatureId);
/**
* @return the maximum number of subscriptions this device will support at any one time.
@@ -96,7 +107,8 @@ interface ISub {
/**
* @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
*/
- List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage);
+ List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage,
+ String callingFeatureId);
/**
* @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
@@ -225,7 +237,8 @@ interface ISub {
* Return opportunistic subscriptions that can be visible to the caller.
* @return the list of opportunistic subscription info. If none exists, an empty list.
*/
- List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage);
+ List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage,
+ String callingFeatureId);
void removeSubscriptionsFromGroup(in int[] subIdList, in ParcelUuid groupUuid,
String callingPackage);
@@ -233,7 +246,8 @@ interface ISub {
void addSubscriptionsIntoGroup(in int[] subIdList, in ParcelUuid groupUuid,
String callingPackage);
- List<SubscriptionInfo> getSubscriptionsInGroup(in ParcelUuid groupUuid, String callingPackage);
+ List<SubscriptionInfo> getSubscriptionsInGroup(in ParcelUuid groupUuid, String callingPackage,
+ String callingFeatureId);
int getSlotIndex(int subId);
@@ -265,7 +279,8 @@ interface ISub {
int setSubscriptionProperty(int subId, String propKey, String propValue);
- String getSubscriptionProperty(int subId, String propKey, String callingPackage);
+ String getSubscriptionProperty(int subId, String propKey, String callingPackage,
+ String callingFeatureId);
boolean setSubscriptionEnabled(boolean enable, int subId);
@@ -278,7 +293,7 @@ interface ISub {
*/
int getSimStateForSlotIndex(int slotIndex);
- boolean isActiveSubId(int subId, String callingPackage);
+ boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId);
boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 050388c2b021..e96a0787c9af 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -89,22 +89,32 @@ interface ITelephony {
@UnsupportedAppUsage
void call(String callingPackage, String number);
+ /** @deprecated Use {@link #isRadioOnWithFeature(String, String) instead */
+ @UnsupportedAppUsage
+ boolean isRadioOn(String callingPackage);
+
/**
* Check to see if the radio is on or not.
* @param callingPackage the name of the package making the call.
+ * @param callingFeatureId The feature in the package.
* @return returns true if the radio is on.
*/
+ boolean isRadioOnWithFeature(String callingPackage, String callingFeatureId);
+
+ /**
+ * @deprecated Use {@link #isRadioOnForSubscriberWithFeature(int, String, String) instead
+ */
@UnsupportedAppUsage
- boolean isRadioOn(String callingPackage);
+ boolean isRadioOnForSubscriber(int subId, String callingPackage);
/**
* Check to see if the radio is on or not on particular subId.
* @param subId user preferred subId.
* @param callingPackage the name of the package making the call.
+ * @param callingFeatureId The feature in the package.
* @return returns true if the radio is on.
*/
- @UnsupportedAppUsage
- boolean isRadioOnForSubscriber(int subId, String callingPackage);
+ boolean isRadioOnForSubscriberWithFeature(int subId, String callingPackage, String callingFeatureId);
/**
* Supply a pin to unlock the SIM. Blocks until a result is determined.
@@ -302,7 +312,7 @@ interface ITelephony {
* operator's MCC (Mobile Country Code).
* @see android.telephony.TelephonyManager#getNetworkCountryIso
*/
- String getNetworkCountryIsoForPhone(int phoneId, String callingPkg);
+ String getNetworkCountryIsoForPhone(int phoneId, String callingPkg, String callingFeatureId);
/**
* Returns the neighboring cell information of the device.
@@ -371,23 +381,27 @@ interface ITelephony {
/**
* Returns the CDMA ERI icon index to display
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- int getCdmaEriIconIndex(String callingPackage);
+ int getCdmaEriIconIndex(String callingPackage, String callingFeatureId);
/**
* Returns the CDMA ERI icon index to display on particular subId.
* @param subId user preferred subId.
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage);
+ int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Returns the CDMA ERI icon mode,
* 0 - ON
* 1 - FLASHING
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- int getCdmaEriIconMode(String callingPackage);
+ int getCdmaEriIconMode(String callingPackage, String callingFeatureId);
/**
* Returns the CDMA ERI icon mode on particular subId,
@@ -395,21 +409,25 @@ interface ITelephony {
* 1 - FLASHING
* @param subId user preferred subId.
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- int getCdmaEriIconModeForSubscriber(int subId, String callingPackage);
+ int getCdmaEriIconModeForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Returns the CDMA ERI text,
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- String getCdmaEriText(String callingPackage);
+ String getCdmaEriText(String callingPackage, String callingFeatureId);
/**
* Returns the CDMA ERI text for particular subId,
* @param subId user preferred subId.
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- String getCdmaEriTextForSubscriber(int subId, String callingPackage);
+ String getCdmaEriTextForSubscriber(int subId, String callingPackage, String callingFeatureId);
/**
* Returns true if OTA service provisioning needs to run.
@@ -452,7 +470,8 @@ interface ITelephony {
* @param subId user preferred subId.
* Returns the unread count of voicemails
*/
- int getVoiceMessageCountForSubscriber(int subId, String callingPackage);
+ int getVoiceMessageCountForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Returns true if current state supports both voice and data
@@ -462,7 +481,7 @@ interface ITelephony {
Bundle getVisualVoicemailSettings(String callingPackage, int subId);
- String getVisualVoicemailPackageName(String callingPackage, int subId);
+ String getVisualVoicemailPackageName(String callingPackage, String callingFeatureId, int subId);
// Not oneway, caller needs to make sure the vaule is set before receiving a SMS
void enableVisualVoicemailSmsFilter(String callingPackage, int subId,
@@ -494,29 +513,35 @@ interface ITelephony {
* Returns the network type of a subId.
* @param subId user preferred subId.
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- int getNetworkTypeForSubscriber(int subId, String callingPackage);
+ int getNetworkTypeForSubscriber(int subId, String callingPackage, String callingFeatureId);
/**
* Returns the network type for data transmission
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- int getDataNetworkType(String callingPackage);
+ int getDataNetworkType(String callingPackage, String callingFeatureId);
/**
* Returns the data network type of a subId
* @param subId user preferred subId.
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- int getDataNetworkTypeForSubscriber(int subId, String callingPackage);
+ int getDataNetworkTypeForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Returns the voice network type of a subId
* @param subId user preferred subId.
- * @param callingPackage package making the call.
+ * @param callingPackage package making the call.getLteOnCdmaMode
+ * @param callingFeatureId The feature in the package.
* Returns the network type
*/
- int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage);
+ int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Return true if an ICC card is present
@@ -537,10 +562,11 @@ interface ITelephony {
* the mode may be unknown.
*
* @param callingPackage the name of the calling package
+ * @param callingFeatureId The feature in the package.
* @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
* or {@link PHone#LTE_ON_CDMA_TRUE}
*/
- int getLteOnCdmaMode(String callingPackage);
+ int getLteOnCdmaMode(String callingPackage, String callingFeatureId);
/**
* Return if the current radio is LTE on CDMA. This
@@ -548,10 +574,11 @@ interface ITelephony {
* the mode may be unknown.
*
* @param callingPackage the name of the calling package
+ * @param callingFeatureId The feature in the package.
* @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
* or {@link PHone#LTE_ON_CDMA_TRUE}
*/
- int getLteOnCdmaModeForSubscriber(int subId, String callingPackage);
+ int getLteOnCdmaModeForSubscriber(int subId, String callingPackage, String callingFeatureId);
/**
* Returns all observed cell information of the device.
@@ -800,10 +827,11 @@ interface ITelephony {
* Get the calculated preferred network type.
* Used for device configuration by some CDMA operators.
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
*
* @return the calculated preferred network type, defined in RILConstants.java.
*/
- int getCalculatedPreferredNetworkType(String callingPackage);
+ int getCalculatedPreferredNetworkType(String callingPackage, String callingFeatureId);
/*
* Get the preferred network type.
@@ -981,8 +1009,9 @@ interface ITelephony {
* Get P-CSCF address from PCO after data connection is established or modified.
* @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
*/
- String[] getPcscfAddress(String apnType, String callingPackage);
+ String[] getPcscfAddress(String apnType, String callingPackage, String callingFeatureId);
/**
* Set IMS registration state
@@ -1072,9 +1101,10 @@ interface ITelephony {
*
* @param subId whose dialing number for line 1 is returned.
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* @return the displayed dialing number if set, or null if not set.
*/
- String getLine1NumberForDisplay(int subId, String callingPackage);
+ String getLine1NumberForDisplay(int subId, String callingPackage, String callingFeatureId);
/**
* Returns the displayed alphatag of the dialing number if it was set
@@ -1082,10 +1112,11 @@ interface ITelephony {
*
* @param subId whose alphatag associated with line 1 is returned.
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* @return the displayed alphatag of the dialing number if set, or null if
* not set.
*/
- String getLine1AlphaTagForDisplay(int subId, String callingPackage);
+ String getLine1AlphaTagForDisplay(int subId, String callingPackage, String callingFeatureId);
/**
* Return the set of subscriber IDs that should be considered "merged together" for data usage
@@ -1097,7 +1128,7 @@ interface ITelephony {
*
* @hide
*/
- String[] getMergedSubscriberIds(int subId, String callingPackage);
+ String[] getMergedSubscriberIds(int subId, String callingPackage, String callingFeatureId);
/**
* @hide
@@ -1196,26 +1227,29 @@ interface ITelephony {
* Whether video calling has been enabled by the user.
*
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* @return {@code true} if the user has enabled video calling, {@code false} otherwise.
*/
- boolean isVideoCallingEnabled(String callingPackage);
+ boolean isVideoCallingEnabled(String callingPackage, String callingFeatureId);
/**
* Whether the DTMF tone length can be changed.
*
* @param subId The subscription to use.
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* @return {@code true} if the DTMF tone length can be changed.
*/
- boolean canChangeDtmfToneLength(int subId, String callingPackage);
+ boolean canChangeDtmfToneLength(int subId, String callingPackage, String callingFeatureId);
/**
* Whether the device is a world phone.
*
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* @return {@code true} if the devices is a world phone.
*/
- boolean isWorldPhone(int subId, String callingPackage);
+ boolean isWorldPhone(int subId, String callingPackage, String callingFeatureId);
/**
* Whether the phone supports TTY mode.
@@ -1257,26 +1291,31 @@ interface ITelephony {
*/
int getImsRegTechnologyForMmTel(int subId);
+ /** @deprecated Use {@link #getDeviceIdWithFeature(String, String) instead */
+ @UnsupportedAppUsage
+ String getDeviceId(String callingPackage);
+
/**
* Returns the unique device ID of phone, for example, the IMEI for
* GSM and the MEID for CDMA phones. Return null if device ID is not available.
*
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- @UnsupportedAppUsage
- String getDeviceId(String callingPackage);
+ String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);
/**
* Returns the IMEI for the given slot.
*
* @param slotIndex - device slot.
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- String getImeiForSlot(int slotIndex, String callingPackage);
+ String getImeiForSlot(int slotIndex, String callingPackage, String callingFeatureId);
/**
* Returns the Type Allocation Code from the IMEI for the given slot.
@@ -1290,10 +1329,11 @@ interface ITelephony {
*
* @param slotIndex - device slot.
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- String getMeidForSlot(int slotIndex, String callingPackage);
+ String getMeidForSlot(int slotIndex, String callingPackage, String callingFeatureId);
/**
* Returns the Manufacturer Code from the MEID for the given slot.
@@ -1307,10 +1347,12 @@ interface ITelephony {
*
* @param slotIndex - device slot.
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage);
+ String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage,
+ String callingFeatureId);
/**
* Returns the subscription ID associated with the specified PhoneAccount.
@@ -1321,7 +1363,7 @@ interface ITelephony {
* Returns the subscription ID associated with the specified PhoneAccountHandle.
*/
int getSubIdForPhoneAccountHandle(in PhoneAccountHandle phoneAccountHandle,
- String callingPackage);
+ String callingPackage, String callingFeatureId);
/**
* Returns the PhoneAccountHandle associated with a subscription ID.
@@ -1605,10 +1647,12 @@ interface ITelephony {
* Get Client request stats which will contain statistical information
* on each request made by client.
* @param callingPackage package making the call.
+ * @param callingFeatureId The feature in the package.
* @param subId Subscription index
* @hide
*/
- List<ClientRequestStats> getClientRequestStats(String callingPackage, int subid);
+ List<ClientRequestStats> getClientRequestStats(String callingPackage, String callingFeatureId,
+ int subid);
/**
* Set SIM card power state.
@@ -1627,7 +1671,8 @@ interface ITelephony {
* @param subId subscription ID used for authentication
* @param appType the icc application type, like {@link #APPTYPE_USIM}
*/
- String[] getForbiddenPlmns(int subId, int appType, String callingPackage);
+ String[] getForbiddenPlmns(int subId, int appType, String callingPackage,
+ String callingFeatureId);
/**
* Set the forbidden PLMN list from the givven app type (ex APPTYPE_USIM) on a particular
@@ -1636,10 +1681,12 @@ interface ITelephony {
* @param subId subId the id of the subscription
* @param appType appType the uicc app type, must be USIM or SIM.
* @param fplmns plmns the Forbiden plmns list that needed to be written to the SIM.
- * @param content callingPackage the op Package name.
+ * @param callingPackage the op Package name.
+ * @param callingFeatureId the feature in the package.
* @return number of fplmns that is successfully written to the SIM
*/
- int setForbiddenPlmns(int subId, int appType, in List<String> fplmns, String callingPackage);
+ int setForbiddenPlmns(int subId, int appType, in List<String> fplmns, String callingPackage,
+ String callingFeatureId);
/**
* Check if phone is in emergency callback mode
@@ -1783,7 +1830,8 @@ interface ITelephony {
* How many modems can have simultaneous data connections.
* @hide
*/
- int getNumberOfModemsWithSimultaneousDataConnections(int subId, String callingPackage);
+ int getNumberOfModemsWithSimultaneousDataConnections(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Return the network selection mode on the subscription with id {@code subId}.
@@ -1799,7 +1847,7 @@ interface ITelephony {
* Return the modem radio power state for slot index.
*
*/
- int getRadioPowerState(int slotIndex, String callingPackage);
+ int getRadioPowerState(int slotIndex, String callingPackage, String callingFeatureId);
// IMS specific AIDL commands, see ImsMmTelManager.java
@@ -1929,7 +1977,7 @@ interface ITelephony {
/**
* Return the emergency number list from all the active subscriptions.
*/
- Map getEmergencyNumberList(String callingPackage);
+ Map getEmergencyNumberList(String callingPackage, String callingFeatureId);
/**
* Identify if the number is emergency number, based on all the active subscriptions.
@@ -2014,12 +2062,13 @@ interface ITelephony {
* Returns if the usage of multiple SIM cards at the same time is supported.
*
* @param callingPackage The package making the call.
+ * @param callingFeatureId The feature in the package.
* @return {@link #MULTISIM_ALLOWED} if the device supports multiple SIMs.
* {@link #MULTISIM_NOT_SUPPORTED_BY_HARDWARE} if the device does not support multiple SIMs.
* {@link #MULTISIM_NOT_SUPPORTED_BY_CARRIER} in the device supports multiple SIMs, but the
* functionality is restricted by the carrier.
*/
- int isMultiSimSupported(String callingPackage);
+ int isMultiSimSupported(String callingPackage, String callingFeatureId);
/**
* Switch configs to enable multi-sim or switch back to single-sim
@@ -2031,7 +2080,8 @@ interface ITelephony {
* Get if altering modems configurations will trigger reboot.
* @hide
*/
- boolean doesSwitchMultiSimConfigTriggerReboot(int subId, String callingPackage);
+ boolean doesSwitchMultiSimConfigTriggerReboot(int subId, String callingPackage,
+ String callingFeatureId);
/**
* Get the mapping from logical slots to physical slots.
@@ -2050,7 +2100,7 @@ interface ITelephony {
*/
boolean isApplicationOnUicc(int subId, int appType);
- boolean isModemEnabledForSlot(int slotIndex, String callingPackage);
+ boolean isModemEnabledForSlot(int slotIndex, String callingPackage, String callingFeatureId);
boolean isDataEnabledForApn(int apnType, int subId, String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 8a852eea5610..6e20621e82f2 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.Manifest;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
@@ -95,16 +96,19 @@ public final class TelephonyPermissions {
* inaccesible to carrier-privileged apps).
*/
public static boolean checkCallingOrSelfReadPhoneState(
- Context context, int subId, String callingPackage, String message) {
+ Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
+ String message) {
return checkReadPhoneState(context, subId, Binder.getCallingPid(), Binder.getCallingUid(),
- callingPackage, message);
+ callingPackage, callingFeatureId, message);
}
/** Identical to checkCallingOrSelfReadPhoneState but never throws SecurityException */
public static boolean checkCallingOrSelfReadPhoneStateNoThrow(
- Context context, int subId, String callingPackage, String message) {
+ Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
+ String message) {
try {
- return checkCallingOrSelfReadPhoneState(context, subId, callingPackage, message);
+ return checkCallingOrSelfReadPhoneState(context, subId, callingPackage,
+ callingFeatureId, message);
} catch (SecurityException se) {
return false;
}
@@ -132,9 +136,11 @@ public final class TelephonyPermissions {
* devices.
*/
public static boolean checkReadPhoneState(
- Context context, int subId, int pid, int uid, String callingPackage, String message) {
+ Context context, int subId, int pid, int uid, String callingPackage,
+ @Nullable String callingFeatureId, String message) {
return checkReadPhoneState(
- context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, message);
+ context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, callingFeatureId,
+ message);
}
/**
@@ -153,7 +159,7 @@ public final class TelephonyPermissions {
@VisibleForTesting
public static boolean checkReadPhoneState(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
- String callingPackage, String message) {
+ String callingPackage, @Nullable String callingFeatureId, String message) {
try {
context.enforcePermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
@@ -178,8 +184,8 @@ public final class TelephonyPermissions {
// We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
// revoked.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage)
- == AppOpsManager.MODE_ALLOWED;
+ return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
+ callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
}
/**
@@ -196,16 +202,16 @@ public final class TelephonyPermissions {
* @return {@code true} if the app can read phone state or has carrier privilege;
* {@code false} otherwise.
*/
- public static boolean checkReadPhoneStateOnAnyActiveSub(
- Context context, int pid, int uid, String callingPackage, String message) {
+ public static boolean checkReadPhoneStateOnAnyActiveSub(Context context, int pid, int uid,
+ String callingPackage, @Nullable String callingFeatureId, String message) {
return checkReadPhoneStateOnAnyActiveSub(context, TELEPHONY_SUPPLIER, pid, uid,
- callingPackage, message);
+ callingPackage, callingFeatureId, message);
}
@VisibleForTesting
public static boolean checkReadPhoneStateOnAnyActiveSub(
Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid,
- String callingPackage, String message) {
+ String callingPackage, @Nullable String callingFeatureId, String message) {
try {
context.enforcePermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
@@ -226,8 +232,8 @@ public final class TelephonyPermissions {
// We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
// revoked.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage) ==
- AppOpsManager.MODE_ALLOWED;
+ return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
+ callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
}
/**
@@ -248,9 +254,10 @@ public final class TelephonyPermissions {
* </ul>
*/
public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context,
- String callingPackage, String message) {
+ String callingPackage, @Nullable String callingFeatureId, String message) {
return checkCallingOrSelfReadDeviceIdentifiers(context,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, message);
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, callingFeatureId,
+ message);
}
/**
@@ -271,9 +278,9 @@ public final class TelephonyPermissions {
* </ul>
*/
public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, int subId,
- String callingPackage, String message) {
+ String callingPackage, @Nullable String callingFeatureId, String message) {
return checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
- context, subId, callingPackage, message, true);
+ context, subId, callingPackage, callingFeatureId, message, true);
}
/**
@@ -293,9 +300,9 @@ public final class TelephonyPermissions {
* </ul>
*/
public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
- String callingPackage, String message) {
+ String callingPackage, @Nullable String callingFeatureId, String message) {
return checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
- context, subId, callingPackage, message, false);
+ context, subId, callingPackage, callingFeatureId, message, false);
}
/**
@@ -317,8 +324,8 @@ public final class TelephonyPermissions {
* </ul>
*/
private static boolean checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
- Context context, int subId, String callingPackage, String message,
- boolean allowCarrierPrivilegeOnAnySub) {
+ Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
+ String message, boolean allowCarrierPrivilegeOnAnySub) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
// Allow system and root access to the device identifiers.
@@ -351,7 +358,7 @@ public final class TelephonyPermissions {
Context.APP_OPS_SERVICE);
try {
if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid,
- callingPackage) == AppOpsManager.MODE_ALLOWED) {
+ callingPackage, callingFeatureId, null) == AppOpsManager.MODE_ALLOWED) {
return true;
}
} finally {
@@ -444,15 +451,16 @@ public final class TelephonyPermissions {
* to it, {@code false} otherwise.
*/
public static boolean checkReadCallLog(
- Context context, int subId, int pid, int uid, String callingPackage) {
+ Context context, int subId, int pid, int uid, String callingPackage,
+ @Nullable String callingPackageName) {
return checkReadCallLog(
- context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage);
+ context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, callingPackageName);
}
@VisibleForTesting
public static boolean checkReadCallLog(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
- String callingPackage) {
+ String callingPackage, @Nullable String callingFeatureId) {
if (context.checkPermission(Manifest.permission.READ_CALL_LOG, pid, uid)
!= PERMISSION_GRANTED) {
@@ -468,8 +476,8 @@ public final class TelephonyPermissions {
// We have READ_CALL_LOG permission, so return true as long as the AppOps bit hasn't been
// revoked.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage) ==
- AppOpsManager.MODE_ALLOWED;
+ return appOps.noteOp(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage,
+ callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
}
/**
@@ -479,20 +487,21 @@ public final class TelephonyPermissions {
* default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
*/
public static boolean checkCallingOrSelfReadPhoneNumber(
- Context context, int subId, String callingPackage, String message) {
+ Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
+ String message) {
return checkReadPhoneNumber(
context, TELEPHONY_SUPPLIER, subId, Binder.getCallingPid(), Binder.getCallingUid(),
- callingPackage, message);
+ callingPackage, callingFeatureId, message);
}
@VisibleForTesting
public static boolean checkReadPhoneNumber(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
- String callingPackage, String message) {
+ String callingPackage, @Nullable String callingFeatureId, String message) {
// Default SMS app can always read it.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- if (appOps.noteOp(AppOpsManager.OPSTR_WRITE_SMS, uid, callingPackage) ==
- AppOpsManager.MODE_ALLOWED) {
+ if (appOps.noteOp(AppOpsManager.OPSTR_WRITE_SMS, uid, callingPackage, callingFeatureId,
+ null) == AppOpsManager.MODE_ALLOWED) {
return true;
}
@@ -502,14 +511,15 @@ public final class TelephonyPermissions {
// First, check if we can read the phone state.
try {
return checkReadPhoneState(
- context, telephonySupplier, subId, pid, uid, callingPackage, message);
+ context, telephonySupplier, subId, pid, uid, callingPackage, callingFeatureId,
+ message);
} catch (SecurityException readPhoneStateSecurityException) {
}
// Can be read with READ_SMS too.
try {
context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_SMS, uid, callingPackage)
- == AppOpsManager.MODE_ALLOWED;
+ return appOps.noteOp(AppOpsManager.OPSTR_READ_SMS, uid, callingPackage,
+ callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
} catch (SecurityException readSmsSecurityException) {
}
@@ -517,8 +527,8 @@ public final class TelephonyPermissions {
try {
context.enforcePermission(android.Manifest.permission.READ_PHONE_NUMBERS, pid, uid,
message);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_NUMBERS, uid, callingPackage)
- == AppOpsManager.MODE_ALLOWED;
+ return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_NUMBERS, uid, callingPackage,
+ callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
} catch (SecurityException readPhoneNumberSecurityException) {
}
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 9d3e12050193..85e5916a63df 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -71,8 +71,8 @@ public class MockContentProvider extends ContentProvider {
@Override
public int delete(String callingPackage, @Nullable String featureId, Uri url,
- String selection, String[] selectionArgs) throws RemoteException {
- return MockContentProvider.this.delete(url, selection, selectionArgs);
+ Bundle extras) throws RemoteException {
+ return MockContentProvider.this.delete(url, extras);
}
@Override
@@ -82,8 +82,8 @@ public class MockContentProvider extends ContentProvider {
@Override
public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
- ContentValues initialValues) throws RemoteException {
- return MockContentProvider.this.insert(url, initialValues);
+ ContentValues initialValues, Bundle extras) throws RemoteException {
+ return MockContentProvider.this.insert(url, initialValues, extras);
}
@Override
@@ -109,9 +109,8 @@ public class MockContentProvider extends ContentProvider {
@Override
public int update(String callingPackage, @Nullable String featureId, Uri url,
- ContentValues values, String selection, String[] selectionArgs)
- throws RemoteException {
- return MockContentProvider.this.update(url, values, selection, selectionArgs);
+ ContentValues values, Bundle extras) throws RemoteException {
+ return MockContentProvider.this.update(url, values, extras);
}
@Override
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index e512b52643f3..464abfb1a514 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -51,7 +51,7 @@ public class MockIContentProvider implements IContentProvider {
@Override
@SuppressWarnings("unused")
public int delete(String callingPackage, @Nullable String featureId, Uri url,
- String selection, String[] selectionArgs) throws RemoteException {
+ Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -63,7 +63,7 @@ public class MockIContentProvider implements IContentProvider {
@Override
@SuppressWarnings("unused")
public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
- ContentValues initialValues) throws RemoteException {
+ ContentValues initialValues, Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -99,7 +99,7 @@ public class MockIContentProvider implements IContentProvider {
@Override
public int update(String callingPackage, @Nullable String featureId, Uri url,
- ContentValues values, String selection, String[] selectionArgs) throws RemoteException {
+ ContentValues values, Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index cd04c2e197f9..3d72ee67a227 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -18,13 +18,13 @@ package com.android.statusbartest;
import android.app.Notification;
import android.app.NotificationManager;
-import android.view.View;
-import android.content.Intent;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.content.Intent;
import android.os.Handler;
-import android.util.Log;
import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
import android.view.Window;
import android.view.WindowManager;
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 5f62c08f55f3..9e5717b4bd64 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -208,6 +208,12 @@ public class TetheringTest {
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
return super.getSystemService(name);
}
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
+ return super.getSystemServiceName(serviceClass);
+ }
}
public class MockIpServerDependencies extends IpServer.Dependencies {
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 1d29a824d10d..4d42a612030d 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -192,8 +192,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
mService = new NetworkStatsService(
mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock,
- TelephonyManager.getDefault(), mSettings, mStatsFactory,
- new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir));
+ mServiceContext.getSystemService(TelephonyManager.class), mSettings,
+ mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir));
mHandlerThread = new HandlerThread("HandlerThread");
mHandlerThread.start();
Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 1be4ea8eccda..6f442300bce7 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -210,6 +210,7 @@ genrule {
tools: [":soong_zip"],
srcs: [
"Configuration.proto",
+ "ResourcesInternal.proto",
"Resources.proto",
],
out: ["aapt2-protos.zip"],
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index e883c6bed755..46105f4d66b0 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -241,8 +241,6 @@ class FlagsDict:
flags = csv[1:]
if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags):
flags.append(FLAG_WHITELIST)
- elif FLAG_TEST_API in flags:
- flags.append(FLAG_GREYLIST)
self._dict[csv[0]].update(flags)
def assign_flag(self, flag, apis, source="<unknown>"):
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
index 4dc880b107d3..55c3a7d718db 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py
@@ -53,14 +53,22 @@ class TestHiddenapiListGeneration(unittest.TestCase):
# Test new additions.
flags.parse_and_merge_csv([
'A,' + FLAG_GREYLIST,
- 'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O ])
- self.assertEqual(flags.generate_csv(),
- [ 'A,' + FLAG_GREYLIST,
- 'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O ])
+ 'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O,
+ 'C,' + FLAG_SYSTEM_API + ',' + FLAG_WHITELIST,
+ 'D,' + FLAG_GREYLIST+ ',' + FLAG_TEST_API,
+ 'E,' + FLAG_BLACKLIST+ ',' + FLAG_TEST_API,
+ ])
+ self.assertEqual(flags.generate_csv(), [
+ 'A,' + FLAG_GREYLIST,
+ 'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O,
+ 'C,' + FLAG_SYSTEM_API + ',' + FLAG_WHITELIST,
+ 'D,' + FLAG_GREYLIST+ ',' + FLAG_TEST_API,
+ 'E,' + FLAG_BLACKLIST+ ',' + FLAG_TEST_API,
+ ])
# Test unknown flag.
with self.assertRaises(AssertionError):
- flags.parse_and_merge_csv([ 'C,foo' ])
+ flags.parse_and_merge_csv([ 'Z,foo' ])
def test_assign_flag(self):
flags = FlagsDict()
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 93960de89d89..a61a5afc310e 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2257,6 +2257,8 @@ public class WifiManager {
public static final long WIFI_FEATURE_MBO = 0x800000000L; // MBO Support
/** @hide */
public static final long WIFI_FEATURE_OCE = 0x1000000000L; // OCE Support
+ /** @hide */
+ public static final long WIFI_FEATURE_INFRA_6G = 0x2000000000L; // Support 6 GHz band
private long getSupportedFeatures() {
try {
@@ -2271,6 +2273,7 @@ public class WifiManager {
private boolean isFeatureSupported(long feature) {
return (getSupportedFeatures() & feature) == feature;
}
+
/**
* @return true if this adapter supports 5 GHz band
*/
@@ -2279,6 +2282,14 @@ public class WifiManager {
}
/**
+ * @return true if the device supports operating in the 6 GHz band and Wi-Fi is enabled,
+ * false otherwise.
+ */
+ public boolean is6GHzBandSupported() {
+ return isFeatureSupported(WIFI_FEATURE_INFRA_6G);
+ }
+
+ /**
* @return true if this adapter supports Passpoint
* @hide
*/
@@ -3351,7 +3362,7 @@ public class WifiManager {
/**
* Base class for soft AP callback. Should be extended by applications and set when calling
- * {@link WifiManager#registerSoftApCallback(SoftApCallback, Handler)}.
+ * {@link WifiManager#registerSoftApCallback(Executor, SoftApCallback)}.
*
* @hide
*/
@@ -3452,16 +3463,16 @@ public class WifiManager {
* without the permission will trigger a {@link java.lang.SecurityException}.
* <p>
*
- * @param callback Callback for soft AP events
* @param executor The executor to execute the callbacks of the {@code executor}
* object. If null, then the application's main executor will be used.
+ * @param callback Callback for soft AP events
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerSoftApCallback(@NonNull SoftApCallback callback,
- @Nullable @CallbackExecutor Executor executor) {
+ public void registerSoftApCallback(@Nullable @CallbackExecutor Executor executor,
+ @NonNull SoftApCallback callback) {
if (callback == null) throw new IllegalArgumentException("callback cannot be null");
Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", executor=" + executor);
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index 0511f2411647..5a4ed3c2f5e3 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -20,7 +20,6 @@ import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.net.NetworkSpecifier;
import android.os.Parcel;
import android.os.Parcelable;
@@ -337,7 +336,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements
* Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. This method
* is optional - if not called, then an Open (unencrypted) connection will be created.
*
- * @param pskPassphrase The (optional) passphrase to be used to encrypt the link.
+ * @param pskPassphrase The (optional) passphrase to be used to encrypt the link. Use the
+ * {@link #setPmk(byte[])} to specify a PMK.
* @return the current {@link Builder} builder, enabling chaining of builder
* methods.
*/
@@ -358,9 +358,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements
* specify a Passphrase.
* @return the current {@link Builder} builder, enabling chaining of builder
* methods.
- * @hide
*/
- @SystemApi
public @NonNull Builder setPmk(@NonNull byte[] pmk) {
if (!WifiAwareUtils.validatePmk(pmk)) {
throw new IllegalArgumentException("PMK must 32 bytes");
@@ -377,7 +375,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements
* <ul>
* <li>The server device must be the Publisher device!
* <li>The port information can only be specified on secure links, specified using
- * {@link #setPskPassphrase(String)}.
+ * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}.
* </ul>
*
* @param port A positive integer indicating the port to be used for communication.
@@ -400,7 +398,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements
* <ul>
* <li>The server device must be the Publisher device!
* <li>The transport protocol information can only be specified on secure links,
- * specified using {@link #setPskPassphrase(String)}.
+ * specified using {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}.
* </ul>
* The transport protocol number is assigned by the Internet Assigned Numbers Authority
* (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml.
@@ -426,7 +424,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p> The default builder constructor will initialize a NetworkSpecifier which requests an
* open (non-encrypted) link. To request an encrypted link use the
- * {@link #setPskPassphrase(String)} builder method.
+ * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])} builder methods.
*
* @return A {@link NetworkSpecifier} to be used to construct
* {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 6a6e7a1a202e..cf74ff07d2ee 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -289,18 +289,6 @@ public class BaseWifiService extends IWifiManager.Stub {
throw new UnsupportedOperationException();
}
- /** @deprecated replaced by {@link #startLocalOnlyHotspot(ILocalOnlyHotspotCallback, String)} */
- @Deprecated
- public int startLocalOnlyHotspot(Messenger messenger, IBinder binder, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- /** @deprecated replaced by newer signature */
- @Deprecated
- public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName) {
- return startLocalOnlyHotspot(callback, packageName, null, null);
- }
-
@Override
public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName,
String featureId, SoftApConfiguration customConfig) {
@@ -312,12 +300,6 @@ public class BaseWifiService extends IWifiManager.Stub {
throw new UnsupportedOperationException();
}
- /** @deprecated replaced by {@link #startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback)} */
- @Deprecated
- public void startWatchLocalOnlyHotspot(Messenger messenger, IBinder binder) {
- throw new UnsupportedOperationException();
- }
-
@Override
public void startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback callback) {
throw new UnsupportedOperationException();
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 17f3bb2b130b..630527726964 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -702,7 +702,7 @@ public class WifiManagerTest {
@Test
public void registerSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForCallback() {
try {
- mWifiManager.registerSoftApCallback(null, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), null);
fail("expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
@@ -726,7 +726,7 @@ public class WifiManagerTest {
@Test
public void registerSoftApCallbackUsesMainLooperOnNullArgumentForHandler() {
when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
- mWifiManager.registerSoftApCallback(mSoftApCallback, null);
+ mWifiManager.registerSoftApCallback(null, mSoftApCallback);
verify(mContext).getMainExecutor();
}
@@ -735,7 +735,7 @@ public class WifiManagerTest {
*/
@Test
public void registerSoftApCallbackCallGoesToWifiServiceImpl() throws Exception {
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
verify(mWifiService).registerSoftApCallback(any(IBinder.class),
any(ISoftApCallback.Stub.class), anyInt());
}
@@ -746,7 +746,7 @@ public class WifiManagerTest {
@Test
public void unregisterSoftApCallbackCallGoesToWifiServiceImpl() throws Exception {
ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class);
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
verify(mWifiService).registerSoftApCallback(any(IBinder.class),
any(ISoftApCallback.Stub.class), callbackIdentifier.capture());
@@ -761,7 +761,7 @@ public class WifiManagerTest {
public void softApCallbackProxyCallsOnStateChanged() throws Exception {
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
@@ -777,7 +777,7 @@ public class WifiManagerTest {
public void softApCallbackProxyCallsOnConnectedClientsChanged() throws Exception {
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
@@ -798,7 +798,7 @@ public class WifiManagerTest {
testSoftApInfo.setBandwidth(TEST_AP_BANDWIDTH);
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
@@ -817,7 +817,7 @@ public class WifiManagerTest {
testSoftApInfo.setBandwidth(TEST_AP_BANDWIDTH);
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
@@ -843,7 +843,7 @@ public class WifiManagerTest {
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
TestLooper altLooper = new TestLooper();
Handler altHandler = new Handler(altLooper.getLooper());
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(altHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(altHandler), mSoftApCallback);
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
@@ -857,7 +857,7 @@ public class WifiManagerTest {
*/
@Test
public void testCorrectLooperIsUsedForSoftApCallbackHandler() throws Exception {
- mWifiManager.registerSoftApCallback(mSoftApCallback, new HandlerExecutor(mHandler));
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
mLooper.dispatchAll();
verify(mWifiService).registerSoftApCallback(any(IBinder.class),
any(ISoftApCallback.Stub.class), anyInt());
@@ -1586,6 +1586,7 @@ public class WifiManagerTest {
assertTrue(mWifiManager.isP2pSupported());
assertFalse(mWifiManager.isPortableHotspotSupported());
assertFalse(mWifiManager.is5GHzBandSupported());
+ assertFalse(mWifiManager.is6GHzBandSupported());
assertFalse(mWifiManager.isDeviceToDeviceRttSupported());
assertFalse(mWifiManager.isDeviceToApRttSupported());
assertFalse(mWifiManager.isPreferredNetworkOffloadSupported());