summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp17
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java69
-rwxr-xr-xapct-tests/perftests/textclassifier/run.sh6
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java201
-rw-r--r--api/current.txt21
-rw-r--r--api/system-current.txt24
-rw-r--r--api/test-current.txt7
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp16
-rw-r--r--cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp126
-rw-r--r--core/java/android/app/ActivityTaskManager.java13
-rw-r--r--core/java/android/app/ActivityThread.java88
-rw-r--r--core/java/android/app/ActivityView.java29
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl6
-rw-r--r--core/java/android/app/ITaskStackListener.aidl15
-rw-r--r--core/java/android/app/Notification.java27
-rw-r--r--core/java/android/app/NotificationChannel.java2
-rw-r--r--core/java/android/app/SystemServiceRegistry.java4
-rw-r--r--core/java/android/app/TaskStackListener.java8
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java12
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java2
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java2
-rw-r--r--core/java/android/content/ContentProviderOperation.java12
-rw-r--r--core/java/android/content/ContentProviderResult.java8
-rw-r--r--core/java/android/content/Intent.java13
-rw-r--r--core/java/android/content/SyncResult.java2
-rw-r--r--core/java/android/content/integrity/AtomicFormula.java19
-rw-r--r--core/java/android/content/integrity/CompoundFormula.java14
-rw-r--r--core/java/android/content/integrity/IntegrityUtils.java2
-rw-r--r--core/java/android/content/integrity/Rule.java2
-rw-r--r--core/java/android/content/pm/PackageManager.java48
-rw-r--r--core/java/android/content/pm/PermissionInfo.java55
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java6
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java29
-rw-r--r--core/java/android/database/CursorWindow.java4
-rw-r--r--core/java/android/database/DatabaseUtils.java6
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java32
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java4
-rw-r--r--core/java/android/hardware/camera2/params/ColorSpaceTransform.java4
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java340
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java39
-rw-r--r--core/java/android/hardware/hdmi/HdmiDeviceInfo.java2
-rw-r--r--core/java/android/hardware/hdmi/HdmiPortInfo.java2
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlService.aidl4
-rw-r--r--core/java/android/os/BatteryStats.java27
-rw-r--r--core/java/android/os/CombinedVibrationEffect.aidl19
-rw-r--r--core/java/android/os/CombinedVibrationEffect.java150
-rw-r--r--core/java/android/os/Debug.java6
-rw-r--r--core/java/android/os/FileObserver.java13
-rw-r--r--core/java/android/os/IVibratorManagerService.aidl4
-rw-r--r--core/java/android/os/Parcel.java10
-rw-r--r--core/java/android/os/PowerManager.java2
-rw-r--r--core/java/android/os/StrictMode.java45
-rw-r--r--core/java/android/os/VibrationEffect.java2
-rw-r--r--core/java/android/permission/PermissionManager.java15
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--core/java/android/service/autofill/BatchUpdates.java2
-rw-r--r--core/java/android/service/autofill/CustomDescription.java6
-rw-r--r--core/java/android/service/autofill/SaveInfo.java4
-rw-r--r--core/java/android/service/autofill/UserData.java4
-rw-r--r--core/java/android/service/autofill/Validators.java4
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java6
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java47
-rw-r--r--core/java/android/text/TextLine.java3
-rw-r--r--core/java/android/text/TextShaper.java6
-rw-r--r--core/java/android/text/TextUtils.java31
-rwxr-xr-xcore/java/android/text/format/DateFormat.java3
-rw-r--r--core/java/android/text/style/LineHeightSpan.java2
-rw-r--r--core/java/android/util/LocalLog.java5
-rw-r--r--core/java/android/util/proto/EncodedBuffer.java4
-rw-r--r--core/java/android/util/proto/ProtoInputStream.java7
-rw-r--r--core/java/android/view/IWindowManager.aidl1
-rw-r--r--core/java/android/view/IWindowSession.aidl16
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java15
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java6
-rw-r--r--core/java/android/view/InsetsController.java13
-rw-r--r--core/java/android/view/InsetsState.java19
-rw-r--r--core/java/android/view/NotificationHeaderView.java32
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/view/View.java11
-rw-r--r--core/java/android/view/ViewGroup.java5
-rw-r--r--core/java/android/view/ViewRootImpl.java65
-rw-r--r--core/java/android/view/ViewRootInsetsControllerHost.java9
-rw-r--r--core/java/android/view/WindowInsets.java9
-rw-r--r--core/java/android/view/WindowManager.java10
-rw-r--r--core/java/android/view/WindowManagerImpl.java5
-rw-r--r--core/java/android/view/WindowlessWindowManager.java11
-rw-r--r--core/java/android/view/autofill/AutofillValue.java10
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java44
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java81
-rw-r--r--core/java/android/view/inputmethod/InputConnectionInspector.java19
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java11
-rw-r--r--core/java/android/view/inputmethod/SurroundingText.aidl19
-rw-r--r--core/java/android/view/inputmethod/SurroundingText.java150
-rw-r--r--core/java/android/widget/AbsListView.java7
-rw-r--r--core/java/android/widget/TextView.java8
-rw-r--r--core/java/android/window/ClientWindowFrames.java20
-rw-r--r--core/java/android/window/ITaskOrganizerController.aidl8
-rw-r--r--core/java/android/window/TaskOrganizer.java21
-rw-r--r--core/java/android/window/VirtualDisplayTaskEmbedder.java12
-rw-r--r--core/java/com/android/internal/app/SuspendedAppActivity.java5
-rw-r--r--core/java/com/android/internal/compat/ChangeReporter.java4
-rw-r--r--core/java/com/android/internal/inputmethod/CancellationGroup.java20
-rw-r--r--core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl23
-rw-r--r--core/java/com/android/internal/inputmethod/ResultCallbacks.java28
-rw-r--r--core/java/com/android/internal/os/AppIdToPackageMap.java12
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java1
-rw-r--r--core/java/com/android/internal/util/LocalLog.java15
-rw-r--r--core/java/com/android/internal/util/NotificationMessagingUtil.java4
-rw-r--r--core/java/com/android/internal/util/Preconditions.java23
-rw-r--r--core/java/com/android/internal/util/XmlUtils.java6
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java46
-rw-r--r--core/java/com/android/internal/view/IInputContext.aidl4
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java33
-rw-r--r--core/java/com/android/internal/widget/CachingIconView.java50
-rw-r--r--core/java/com/android/internal/widget/ColoredIconHelper.java57
-rw-r--r--core/java/com/android/internal/widget/NotificationExpandButton.java37
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp8
-rw-r--r--core/jni/android_opengl_GLES10.cpp13
-rw-r--r--core/jni/android_opengl_GLES11.cpp4
-rw-r--r--core/jni/android_opengl_GLES11Ext.cpp6
-rw-r--r--core/jni/android_opengl_GLES20.cpp22
-rw-r--r--core/jni/android_opengl_GLES30.cpp18
-rw-r--r--core/jni/android_opengl_GLES32.cpp7
-rw-r--r--core/jni/android_view_Surface.cpp6
-rw-r--r--core/jni/android_view_SurfaceControl.cpp9
-rw-r--r--core/jni/com_google_android_gles_jni_GLImpl.cpp17
-rw-r--r--core/proto/android/os/incident.proto9
-rw-r--r--core/proto/android/server/powerstatsservice.proto99
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/proto/android/service/package.proto12
-rw-r--r--core/proto/android/telephony/enums.proto2
-rw-r--r--core/res/res/values-mcc310-mnc560-as/strings.xml26
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml3
-rw-r--r--core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java52
-rw-r--r--core/tests/coretests/src/android/os/VibrationEffectTest.java68
-rw-r--r--core/tests/coretests/src/android/text/TextUtilsTest.java28
-rw-r--r--core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java2
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java73
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java4
-rw-r--r--core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java20
-rw-r--r--data/etc/car/com.android.car.provision.xml4
-rw-r--r--data/etc/platform.xml3
-rw-r--r--data/etc/services.core.protolog.json88
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java (renamed from errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java)4
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java94
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientParcelableCheckerTest.java (renamed from errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java)4
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java132
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java12
-rw-r--r--graphics/java/android/graphics/Typeface.java2
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java6
-rw-r--r--graphics/java/android/graphics/text/MeasuredText.java14
-rw-r--r--graphics/java/android/graphics/text/PositionedGlyphs.java3
-rw-r--r--graphics/java/android/graphics/text/TextRunShaper.java6
-rw-r--r--libs/WindowManager/Shell/res/raw/wm_shell_protolog.json6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java238
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java47
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java74
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java42
-rw-r--r--location/java/android/location/Geofence.java2
-rw-r--r--location/java/android/location/LocationRequest.java2
-rw-r--r--media/Android.bp3
-rw-r--r--media/java/android/media/AudioManager.java6
-rw-r--r--native/android/surface_control.cpp24
-rw-r--r--non-updatable-api/current.txt21
-rw-r--r--non-updatable-api/system-current.txt22
-rwxr-xr-xpackages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java5
-rw-r--r--packages/PrintSpooler/res/values-mr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml20
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/res/values-mr/strings.xml4
-rw-r--r--packages/SystemUI/AndroidManifest.xml18
-rw-r--r--packages/SystemUI/docs/broadcasts.md14
-rw-r--r--packages/SystemUI/res-keyguard/font/clock.xml28
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_check_box.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml31
-rw-r--r--packages/SystemUI/res/layout/global_screenshot.xml4
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item.xml177
-rw-r--r--packages/SystemUI/res/layout/people_space_widget.xml27
-rw-r--r--packages/SystemUI/res/layout/people_space_widget_item.xml59
-rw-r--r--packages/SystemUI/res/values-af/strings.xml6
-rw-r--r--packages/SystemUI/res/values-am/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml6
-rw-r--r--packages/SystemUI/res/values-as/strings.xml6
-rw-r--r--packages/SystemUI/res/values-az/strings.xml6
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml6
-rw-r--r--packages/SystemUI/res/values-be/strings.xml6
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml6
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml6
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml6
-rw-r--r--packages/SystemUI/res/values-da/strings.xml6
-rw-r--r--packages/SystemUI/res/values-de/strings.xml6
-rw-r--r--packages/SystemUI/res/values-el/strings.xml6
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml6
-rw-r--r--packages/SystemUI/res/values-es/strings.xml6
-rw-r--r--packages/SystemUI/res/values-et/strings.xml6
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml6
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml6
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml6
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml6
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml6
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml6
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml6
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml6
-rw-r--r--packages/SystemUI/res/values-in/strings.xml6
-rw-r--r--packages/SystemUI/res/values-is/strings.xml6
-rw-r--r--packages/SystemUI/res/values-it/strings.xml6
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml6
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml6
-rw-r--r--packages/SystemUI/res/values-km/strings.xml6
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml6
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml2
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml6
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml6
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml6
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml6
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml18
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml6
-rw-r--r--packages/SystemUI/res/values-my/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml6
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-or/strings.xml6
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml6
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml6
-rw-r--r--packages/SystemUI/res/values-si/strings.xml6
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml6
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml6
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml6
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml6
-rw-r--r--packages/SystemUI/res/values-te/strings.xml6
-rw-r--r--packages/SystemUI/res/values-th/strings.xml6
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml6
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml6
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml2
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml6
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml6
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml6
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml6
-rw-r--r--packages/SystemUI/res/xml/people_space_widget_info.xml24
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java15
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java36
-rw-r--r--packages/SystemUI/src/com/android/keyguard/GradientTextClock.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java220
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java158
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetService.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java1199
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java665
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java197
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java611
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ViewController.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java152
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java248
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java13
-rw-r--r--packages/Tethering/apex/manifest.json2
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java47
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java10
-rw-r--r--services/core/java/com/android/server/NsdService.java2
-rw-r--r--services/core/java/com/android/server/VibratorManagerService.java14
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java4
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java3
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java41
-rw-r--r--services/core/java/com/android/server/am/ContentProviderConnection.java9
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java65
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java76
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java122
-rw-r--r--services/core/java/com/android/server/connectivity/DataConnectionStats.java27
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java2
-rw-r--r--services/core/java/com/android/server/display/utils/History.java2
-rw-r--r--services/core/java/com/android/server/display/utils/RollingBuffer.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecConfig.java334
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessage.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java66
-rw-r--r--services/core/java/com/android/server/input/InputShellCommand.java2
-rw-r--r--services/core/java/com/android/server/location/ContextHubService.java26
-rw-r--r--services/core/java/com/android/server/location/IContextHubWrapper.java36
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java3
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java2
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java176
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java29
-rw-r--r--services/core/java/com/android/server/pm/PackageSignatures.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageUsage.java10
-rw-r--r--services/core/java/com/android/server/pm/Settings.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java2
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsData.java286
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java26
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java180
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsLogger.java117
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java36
-rw-r--r--services/core/java/com/android/server/powerstats/ProtoStreamUtils.java273
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java11
-rw-r--r--services/core/java/com/android/server/testharness/TestHarnessModeService.java12
-rw-r--r--services/core/java/com/android/server/webkit/SystemImpl.java13
-rw-r--r--services/core/java/com/android/server/webkit/SystemInterface.java3
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java595
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdater.java599
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java63
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java47
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java4
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java7
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java150
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java46
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java138
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java9
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java21
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java30
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java8
-rw-r--r--services/core/java/com/android/server/wm/ResetTargetTaskHelper.java5
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java30
-rw-r--r--services/core/java/com/android/server/wm/Session.java24
-rw-r--r--services/core/java/com/android/server/wm/Task.java44
-rw-r--r--services/core/java/com/android/server/wm/TaskChangeNotificationController.java49
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java11
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java5
-rw-r--r--services/core/java/com/android/server/wm/Transition.java28
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java211
-rw-r--r--services/core/java/com/android/server/wm/WindowFrames.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java21
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java32
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java130
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java7
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp17
-rw-r--r--services/core/jni/com_android_server_powerstats_PowerStatsService.cpp222
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/core/xsd/Android.bp8
-rw-r--r--services/core/xsd/cec-config/cec-config.xsd27
-rw-r--r--services/core/xsd/cec-config/schema/current.txt40
-rw-r--r--services/core/xsd/cec-config/schema/last_current.txt0
-rw-r--r--services/core/xsd/cec-config/schema/last_removed.txt0
-rw-r--r--services/core/xsd/cec-config/schema/removed.txt1
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java129
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java414
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java295
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java76
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java58
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java89
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java390
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java91
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java190
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java4
-rw-r--r--telephony/api/system-current.txt20
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java15
-rw-r--r--telephony/java/android/telephony/CdmaEriInformation.java169
-rw-r--r--telephony/java/android/telephony/ImsManager.java21
-rw-r--r--telephony/java/android/telephony/SignalStrength.java3
-rw-r--r--telephony/java/android/telephony/SmsManager.java1
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java71
-rw-r--r--telephony/java/android/telephony/ims/ImsService.java12
-rw-r--r--telephony/java/android/telephony/ims/SipDelegateManager.java93
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl3
-rw-r--r--telephony/java/android/telephony/ims/stub/SipTransportImplBase.java5
-rw-r--r--tests/Input/Android.bp1
-rw-r--r--tests/Input/TEST_MAPPING7
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java4
-rw-r--r--tools/powerstats/PowerStatsServiceProtoParser.java95
-rw-r--r--wifi/api/system-current.txt2
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java3
463 files changed, 11102 insertions, 6223 deletions
diff --git a/Android.bp b/Android.bp
index 1c16e466703c..f3225e2e3ad0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -540,8 +540,8 @@ filegroup {
path: "core/java",
}
-java_library {
- name: "framework-minus-apex",
+java_defaults {
+ name: "framework-minus-apex-defaults",
defaults: ["framework-aidl-export-defaults"],
srcs: [
":framework-non-updatable-sources",
@@ -557,7 +557,6 @@ java_library {
"--core-library",
"--multi-dex",
],
- installable: true,
jarjar_rules: ":framework-jarjar-rules",
javac_shard_size: 150,
plugins: [
@@ -590,6 +589,12 @@ java_library {
"mediatranscoding_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
],
+}
+
+java_library {
+ name: "framework-minus-apex",
+ defaults: ["framework-minus-apex-defaults"],
+ installable: true,
// For backwards compatibility.
stem: "framework",
apex_available: ["//apex_available:platform"],
@@ -610,6 +615,12 @@ java_library {
},
}
+java_library {
+ name: "framework-minus-apex-intdefs",
+ defaults: ["framework-minus-apex-defaults"],
+ plugins: ["intdef-annotation-processor"],
+}
+
// This "framework" module is NOT installed to the device. It's
// "framework-minus-apex" that gets installed to the device. Note that
// the filename is still framework.jar (via the stem property) for
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index f66d12a69594..cdf5df6c6bd3 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/uinput/
core/jni/
libs/input/
+ native/
services/core/jni/
services/incremental/
tests/
diff --git a/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
new file mode 100644
index 000000000000..a82fab4a789d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageManagerBenchmark {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void createUserContextBenchmark() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0);
+ }
+ }
+
+ @Test
+ public void getResourcesForApplication_byStarAsUser()
+ throws PackageManager.NameNotFoundException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.getPackageManager().getResourcesForApplicationAsUser(context.getPackageName(),
+ UserHandle.USER_SYSTEM);
+ }
+ }
+
+ @Test
+ public void getResourcesApplication_byCreateContextAsUser()
+ throws PackageManager.NameNotFoundException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0).getPackageManager()
+ .getResourcesForApplication(context.getPackageName());
+ }
+ }
+}
diff --git a/apct-tests/perftests/textclassifier/run.sh b/apct-tests/perftests/textclassifier/run.sh
index d36d190a573a..9a0f4f9fab73 100755
--- a/apct-tests/perftests/textclassifier/run.sh
+++ b/apct-tests/perftests/textclassifier/run.sh
@@ -1,8 +1,8 @@
set -e
-build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup.sh
+build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup
adb install ${OUT}/testcases/TextClassifierPerfTests/arm64/TextClassifierPerfTests.apk
adb shell cmd package compile -m speed -f com.android.perftests.textclassifier
-adb push ${OUT}/obj/EXECUTABLES/perf-setup.sh_intermediates/perf-setup.sh /data/local/tmp/
+adb push ${OUT}/obj/EXECUTABLES/perf-setup_intermediates/perf-setup.sh /data/local/tmp/
adb shell chmod +x /data/local/tmp/perf-setup.sh
adb shell /data/local/tmp/perf-setup.sh
-adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner \ No newline at end of file
+adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index 6f0001dcc0ad..29606030a041 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -86,8 +86,6 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
final InsetsState mRequestedVisibility = new InsetsState();
final Rect mOutFrame = new Rect();
- final Rect mOutContentInsets = new Rect();
- final Rect mOutStableInsets = new Rect();
final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
new DisplayCutout.ParcelableWrapper();
final InsetsState mOutInsetsState = new InsetsState();
@@ -110,7 +108,6 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
long startTime = SystemClock.elapsedRealtimeNanos();
session.addToDisplay(this, mLayoutParams, View.VISIBLE,
Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame,
- mOutContentInsets, mOutStableInsets,
mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 8e26052ee08b..4dc9cf850893 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -1269,19 +1269,14 @@ public class AppStateTrackerImpl implements AppStateTracker {
}
/**
- * @deprecated use {@link #dump(IndentingPrintWriter)} instead.
- */
- @Deprecated
- public void dump(PrintWriter pw, String prefix) {
- dump(new IndentingPrintWriter(pw, " ").setIndent(prefix));
- }
-
- /**
* Dump the internal state to the given PrintWriter. Can be included in the dump
* of a binder service to be output on the shell command "dumpsys".
*/
public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
+ pw.println("Current AppStateTracker State:");
+
+ pw.increaseIndent();
pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
pw.print("Force all apps standby: ");
@@ -1339,6 +1334,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
pw.decreaseIndent();
mStatLogger.dump(pw);
+ pw.decreaseIndent();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index 45ea23321d15..04feef485048 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -31,7 +31,6 @@ import android.util.IndentingPrintWriter;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -200,15 +199,6 @@ class Alarm {
return sb.toString();
}
- /**
- * @deprecated Use {{@link #dump(IndentingPrintWriter, long, SimpleDateFormat)}} instead.
- */
- @Deprecated
- public void dump(PrintWriter pw, String prefix, long nowELAPSED, SimpleDateFormat sdf) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix);
- dump(ipw, nowELAPSED, sdf);
- }
-
private static String policyIndexToString(int index) {
switch (index) {
case REQUESTER_POLICY_INDEX:
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 82819dab0f69..f196567bc391 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -345,10 +345,6 @@ public class AlarmManagerService extends SystemService {
return (i < 0) ? 0 : history.get(i);
}
- void dump(PrintWriter pw, String prefix, long nowElapsed) {
- dump(new IndentingPrintWriter(pw, " ").setIndent(prefix), nowElapsed);
- }
-
void dump(IndentingPrintWriter pw, long nowElapsed) {
pw.println("App Alarm history:");
pw.increaseIndent();
@@ -598,10 +594,6 @@ public class AlarmManagerService extends SystemService {
DEFAULT_APP_STANDBY_RESTRICTED_WINDOW));
}
- void dump(PrintWriter pw, String prefix) {
- dump(new IndentingPrintWriter(pw, " ").setIndent(prefix));
- }
-
void dump(IndentingPrintWriter pw) {
pw.println("Settings:");
@@ -1880,7 +1872,7 @@ public class AlarmManagerService extends SystemService {
if (args.length > 0 && "--proto".equals(args[0])) {
dumpProto(fd);
} else {
- dumpImpl(pw);
+ dumpImpl(new IndentingPrintWriter(pw, " "));
}
}
@@ -1892,18 +1884,20 @@ public class AlarmManagerService extends SystemService {
}
};
- void dumpImpl(PrintWriter pw) {
+ void dumpImpl(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.println("Current Alarm Manager state:");
- mConstants.dump(pw, " ");
+ pw.increaseIndent();
+
+ mConstants.dump(pw);
pw.println();
if (mAppStateTracker != null) {
- mAppStateTracker.dump(pw, " ");
+ mAppStateTracker.dump(pw);
pw.println();
}
- pw.println(" App Standby Parole: " + mAppStandbyParole);
+ pw.println("App Standby Parole: " + mAppStandbyParole);
pw.println();
final long nowELAPSED = mInjector.getElapsedRealtime();
@@ -1911,7 +1905,7 @@ public class AlarmManagerService extends SystemService {
final long nowRTC = mInjector.getCurrentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- pw.print(" nowRTC=");
+ pw.print("nowRTC=");
pw.print(nowRTC);
pw.print("=");
pw.print(sdf.format(new Date(nowRTC)));
@@ -1919,110 +1913,125 @@ public class AlarmManagerService extends SystemService {
pw.print(nowELAPSED);
pw.println();
- pw.print(" mLastTimeChangeClockTime=");
+ pw.print("mLastTimeChangeClockTime=");
pw.print(mLastTimeChangeClockTime);
pw.print("=");
pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
- pw.print(" mLastTimeChangeRealtime=");
+ pw.print("mLastTimeChangeRealtime=");
pw.println(mLastTimeChangeRealtime);
- pw.print(" mLastTickReceived=");
+ pw.print("mLastTickReceived=");
pw.println(sdf.format(new Date(mLastTickReceived)));
- pw.print(" mLastTickSet=");
+ pw.print("mLastTickSet=");
pw.println(sdf.format(new Date(mLastTickSet)));
if (RECORD_ALARMS_IN_HISTORY) {
pw.println();
- pw.println(" Recent TIME_TICK history:");
+ pw.println("Recent TIME_TICK history:");
+ pw.increaseIndent();
int i = mNextTickHistory;
do {
i--;
if (i < 0) i = TICK_HISTORY_DEPTH - 1;
final long time = mTickHistory[i];
- pw.print(" ");
pw.println((time > 0)
? sdf.format(new Date(nowRTC - (nowELAPSED - time)))
: "-");
} while (i != mNextTickHistory);
+ pw.decreaseIndent();
}
SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class);
if (ssm != null) {
pw.println();
- pw.print(" RuntimeStarted=");
+ pw.print("RuntimeStarted=");
pw.print(sdf.format(
new Date(nowRTC - nowELAPSED + ssm.getRuntimeStartElapsedTime())));
if (ssm.isRuntimeRestarted()) {
pw.print(" (Runtime restarted)");
}
pw.println();
- pw.print(" Runtime uptime (elapsed): ");
+
+ pw.print("Runtime uptime (elapsed): ");
TimeUtils.formatDuration(nowELAPSED, ssm.getRuntimeStartElapsedTime(), pw);
pw.println();
- pw.print(" Runtime uptime (uptime): ");
+
+ pw.print("Runtime uptime (uptime): ");
TimeUtils.formatDuration(nowUPTIME, ssm.getRuntimeStartUptime(), pw);
pw.println();
}
pw.println();
if (!mInteractive) {
- pw.print(" Time since non-interactive: ");
+ pw.print("Time since non-interactive: ");
TimeUtils.formatDuration(nowELAPSED - mNonInteractiveStartTime, pw);
pw.println();
}
- pw.print(" Max wakeup delay: ");
+ pw.print("Max wakeup delay: ");
TimeUtils.formatDuration(currentNonWakeupFuzzLocked(nowELAPSED), pw);
pw.println();
- pw.print(" Time since last dispatch: ");
+
+ pw.print("Time since last dispatch: ");
TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
pw.println();
- pw.print(" Next non-wakeup delivery time: ");
+
+ pw.print("Next non-wakeup delivery time: ");
TimeUtils.formatDuration(mNextNonWakeupDeliveryTime, nowELAPSED, pw);
pw.println();
long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
- pw.print(" Next non-wakeup alarm: ");
+ pw.print("Next non-wakeup alarm: ");
TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
pw.print(" = ");
pw.print(mNextNonWakeup);
pw.print(" = ");
pw.println(sdf.format(new Date(nextNonWakeupRTC)));
- pw.print(" set at ");
+
+ pw.increaseIndent();
+ pw.print("set at ");
TimeUtils.formatDuration(mNextNonWakeUpSetAt, nowELAPSED, pw);
+ pw.decreaseIndent();
pw.println();
- pw.print(" Next wakeup alarm: ");
+
+ pw.print("Next wakeup alarm: ");
TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
pw.print(" = ");
pw.print(mNextWakeup);
pw.print(" = ");
pw.println(sdf.format(new Date(nextWakeupRTC)));
- pw.print(" set at ");
+
+ pw.increaseIndent();
+ pw.print("set at ");
TimeUtils.formatDuration(mNextWakeUpSetAt, nowELAPSED, pw);
+ pw.decreaseIndent();
pw.println();
- pw.print(" Next kernel non-wakeup alarm: ");
+ pw.print("Next kernel non-wakeup alarm: ");
TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME), pw);
pw.println();
- pw.print(" Next kernel wakeup alarm: ");
+ pw.print("Next kernel wakeup alarm: ");
TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP), pw);
pw.println();
- pw.print(" Last wakeup: ");
+ pw.print("Last wakeup: ");
TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
pw.print(" = ");
pw.println(mLastWakeup);
- pw.print(" Last trigger: ");
+
+ pw.print("Last trigger: ");
TimeUtils.formatDuration(mLastTrigger, nowELAPSED, pw);
pw.print(" = ");
pw.println(mLastTrigger);
- pw.print(" Num time change events: ");
+
+ pw.print("Num time change events: ");
pw.println(mNumTimeChanged);
pw.println();
- pw.println(" Next alarm clock information: ");
+ pw.println("Next alarm clock information: ");
+ pw.increaseIndent();
final TreeSet<Integer> users = new TreeSet<>();
for (int i = 0; i < mNextAlarmClockForUser.size(); i++) {
users.add(mNextAlarmClockForUser.keyAt(i));
@@ -2034,7 +2043,7 @@ public class AlarmManagerService extends SystemService {
final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
final long time = next != null ? next.getTriggerTime() : 0;
final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
- pw.print(" user:");
+ pw.print("user:");
pw.print(user);
pw.print(" pendingSend:");
pw.print(pendingSend);
@@ -2048,26 +2057,31 @@ public class AlarmManagerService extends SystemService {
}
pw.println();
}
+ pw.decreaseIndent();
+
if (mAlarmStore.size() > 0) {
pw.println();
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", " ");
- mAlarmStore.dump(ipw, nowELAPSED, sdf);
+ mAlarmStore.dump(pw, nowELAPSED, sdf);
}
pw.println();
- pw.println(" Pending user blocked background alarms: ");
+
+ pw.println("Pending user blocked background alarms: ");
+ pw.increaseIndent();
boolean blocked = false;
for (int i = 0; i < mPendingBackgroundAlarms.size(); i++) {
final ArrayList<Alarm> blockedAlarms = mPendingBackgroundAlarms.valueAt(i);
if (blockedAlarms != null && blockedAlarms.size() > 0) {
blocked = true;
- dumpAlarmList(pw, blockedAlarms, " ", nowELAPSED, sdf);
+ dumpAlarmList(pw, blockedAlarms, nowELAPSED, sdf);
}
}
if (!blocked) {
- pw.println(" none");
+ pw.println("none");
}
+ pw.decreaseIndent();
pw.println();
- pw.print(" Pending alarms per uid: [");
+
+ pw.print("Pending alarms per uid: [");
for (int i = 0; i < mAlarmsPerUid.size(); i++) {
if (i > 0) {
pw.print(", ");
@@ -2079,75 +2093,90 @@ public class AlarmManagerService extends SystemService {
pw.println("]");
pw.println();
- mAppWakeupHistory.dump(pw, " ", nowELAPSED);
+ mAppWakeupHistory.dump(pw, nowELAPSED);
if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
pw.println();
- pw.println(" Idle mode state:");
- pw.print(" Idling until: ");
+ pw.println("Idle mode state:");
+
+ pw.increaseIndent();
+ pw.print("Idling until: ");
if (mPendingIdleUntil != null) {
pw.println(mPendingIdleUntil);
- mPendingIdleUntil.dump(pw, " ", nowELAPSED, sdf);
+ mPendingIdleUntil.dump(pw, nowELAPSED, sdf);
} else {
pw.println("null");
}
- pw.println(" Pending alarms:");
- dumpAlarmList(pw, mPendingWhileIdleAlarms, " ", nowELAPSED, sdf);
+ pw.println("Pending alarms:");
+ dumpAlarmList(pw, mPendingWhileIdleAlarms, nowELAPSED, sdf);
+ pw.decreaseIndent();
}
if (mNextWakeFromIdle != null) {
pw.println();
- pw.print(" Next wake from idle: ");
+ pw.print("Next wake from idle: ");
pw.println(mNextWakeFromIdle);
- mNextWakeFromIdle.dump(pw, " ", nowELAPSED, sdf);
+
+ pw.increaseIndent();
+ mNextWakeFromIdle.dump(pw, nowELAPSED, sdf);
+ pw.decreaseIndent();
}
pw.println();
- pw.print(" Past-due non-wakeup alarms: ");
+ pw.print("Past-due non-wakeup alarms: ");
if (mPendingNonWakeupAlarms.size() > 0) {
pw.println(mPendingNonWakeupAlarms.size());
- dumpAlarmList(pw, mPendingNonWakeupAlarms, " ", nowELAPSED, sdf);
+
+ pw.increaseIndent();
+ dumpAlarmList(pw, mPendingNonWakeupAlarms, nowELAPSED, sdf);
+ pw.decreaseIndent();
} else {
pw.println("(none)");
}
- pw.print(" Number of delayed alarms: ");
+ pw.increaseIndent();
+ pw.print("Number of delayed alarms: ");
pw.print(mNumDelayedAlarms);
pw.print(", total delay time: ");
TimeUtils.formatDuration(mTotalDelayTime, pw);
pw.println();
- pw.print(" Max delay time: ");
+
+ pw.print("Max delay time: ");
TimeUtils.formatDuration(mMaxDelayTime, pw);
pw.print(", max non-interactive time: ");
TimeUtils.formatDuration(mNonInteractiveTime, pw);
pw.println();
+ pw.decreaseIndent();
pw.println();
- pw.print(" Broadcast ref count: ");
+ pw.print("Broadcast ref count: ");
pw.println(mBroadcastRefCount);
- pw.print(" PendingIntent send count: ");
+ pw.print("PendingIntent send count: ");
pw.println(mSendCount);
- pw.print(" PendingIntent finish count: ");
+ pw.print("PendingIntent finish count: ");
pw.println(mSendFinishCount);
- pw.print(" Listener send count: ");
+ pw.print("Listener send count: ");
pw.println(mListenerCount);
- pw.print(" Listener finish count: ");
+ pw.print("Listener finish count: ");
pw.println(mListenerFinishCount);
pw.println();
if (mInFlight.size() > 0) {
pw.println("Outstanding deliveries:");
+ pw.increaseIndent();
for (int i = 0; i < mInFlight.size(); i++) {
- pw.print(" #");
+ pw.print("#");
pw.print(i);
pw.print(": ");
pw.println(mInFlight.get(i));
}
+ pw.decreaseIndent();
pw.println();
}
if (mLastAllowWhileIdleDispatch.size() > 0) {
- pw.println(" Last allow while idle dispatch times:");
+ pw.println("Last allow while idle dispatch times:");
+ pw.increaseIndent();
for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); i++) {
- pw.print(" UID ");
+ pw.print("UID ");
final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
UserHandle.formatUid(pw, uid);
pw.print(": ");
@@ -2163,9 +2192,10 @@ public class AlarmManagerService extends SystemService {
pw.println();
}
+ pw.decreaseIndent();
}
- pw.print(" mUseAllowWhileIdleShortTime: [");
+ pw.print("mUseAllowWhileIdleShortTime: [");
for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
if (mUseAllowWhileIdleShortTime.valueAt(i)) {
UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
@@ -2175,7 +2205,7 @@ public class AlarmManagerService extends SystemService {
pw.println("]");
pw.println();
- if (mLog.dump(pw, " Recent problems", " ")) {
+ if (mLog.dump(pw, "Recent problems:")) {
pw.println();
}
@@ -2218,10 +2248,10 @@ public class AlarmManagerService extends SystemService {
}
}
if (len > 0) {
- pw.println(" Top Alarms:");
+ pw.println("Top Alarms:");
+ pw.increaseIndent();
for (int i = 0; i < len; i++) {
FilterStats fs = topFilters[i];
- pw.print(" ");
if (fs.nesting > 0) pw.print("*ACTIVE* ");
TimeUtils.formatDuration(fs.aggregateTime, pw);
pw.print(" running, ");
@@ -2233,20 +2263,22 @@ public class AlarmManagerService extends SystemService {
pw.print(":");
pw.print(fs.mBroadcastStats.mPackageName);
pw.println();
- pw.print(" ");
+
+ pw.increaseIndent();
pw.print(fs.mTag);
pw.println();
+ pw.decreaseIndent();
}
+ pw.decreaseIndent();
}
- pw.println(" ");
- pw.println(" Alarm Stats:");
+ pw.println();
+ pw.println("Alarm Stats:");
final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
for (int iu = 0; iu < mBroadcastStats.size(); iu++) {
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
for (int ip = 0; ip < uidStats.size(); ip++) {
BroadcastStats bs = uidStats.valueAt(ip);
- pw.print(" ");
if (bs.nesting > 0) pw.print("*ACTIVE* ");
UserHandle.formatUid(pw, bs.mUid);
pw.print(":");
@@ -2256,14 +2288,15 @@ public class AlarmManagerService extends SystemService {
pw.print(" running, ");
pw.print(bs.numWakeup);
pw.println(" wakeups:");
+
tmpFilters.clear();
for (int is = 0; is < bs.filterStats.size(); is++) {
tmpFilters.add(bs.filterStats.valueAt(is));
}
Collections.sort(tmpFilters, comparator);
+ pw.increaseIndent();
for (int i = 0; i < tmpFilters.size(); i++) {
FilterStats fs = tmpFilters.get(i);
- pw.print(" ");
if (fs.nesting > 0) pw.print("*ACTIVE* ");
TimeUtils.formatDuration(fs.aggregateTime, pw);
pw.print(" ");
@@ -2273,28 +2306,32 @@ public class AlarmManagerService extends SystemService {
pw.print(" alarms, last ");
TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
pw.println(":");
- pw.print(" ");
+
+ pw.increaseIndent();
pw.print(fs.mTag);
pw.println();
+ pw.decreaseIndent();
}
+ pw.decreaseIndent();
}
}
pw.println();
- mStatLogger.dump(pw, " ");
+ mStatLogger.dump(pw);
if (RECORD_DEVICE_IDLE_ALARMS) {
pw.println();
- pw.println(" Allow while idle dispatches:");
+ pw.println("Allow while idle dispatches:");
+ pw.increaseIndent();
for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
- pw.print(" ");
TimeUtils.formatDuration(ent.elapsedRealtime, nowELAPSED, pw);
pw.print(": ");
UserHandle.formatUid(pw, ent.uid);
pw.print(":");
pw.println(ent.pkg);
+
+ pw.increaseIndent();
if (ent.op != null) {
- pw.print(" ");
pw.print(ent.op);
pw.print(" / ");
pw.print(ent.tag);
@@ -2305,7 +2342,9 @@ public class AlarmManagerService extends SystemService {
}
pw.println();
}
+ pw.decreaseIndent();
}
+ pw.decreaseIndent();
}
}
}
@@ -3044,12 +3083,6 @@ public class AlarmManagerService extends SystemService {
}
}
- private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
- String prefix, long nowELAPSED, SimpleDateFormat sdf) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix);
- dumpAlarmList(ipw, list, nowELAPSED, sdf);
- }
-
static final void dumpAlarmList(IndentingPrintWriter ipw, ArrayList<Alarm> list,
long nowELAPSED, SimpleDateFormat sdf) {
for (int i = list.size() - 1; i >= 0; i--) {
diff --git a/api/current.txt b/api/current.txt
index ce82b5ec8cba..600adf686a2e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10820,7 +10820,6 @@ package android.content {
field public static final String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
field public static final String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
- field public static final String EXTRA_REMOVED_BY_SYSTEM = "android.intent.extra.REMOVED_BY_SYSTEM";
field public static final String EXTRA_REPLACEMENT_EXTRAS = "android.intent.extra.REPLACEMENT_EXTRAS";
field public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
field public static final String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
@@ -10846,6 +10845,7 @@ package android.content {
field public static final String EXTRA_UID = "android.intent.extra.UID";
field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
field public static final String EXTRA_USER = "android.intent.extra.USER";
+ field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
field public static final int FILL_IN_ACTION = 1; // 0x1
field public static final int FILL_IN_CATEGORIES = 4; // 0x4
field public static final int FILL_IN_CLIP_DATA = 128; // 0x80
@@ -12265,7 +12265,7 @@ package android.content.pm {
field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
- field public static final int GET_INTENT_FILTERS = 32; // 0x20
+ field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
field public static final int GET_META_DATA = 128; // 0x80
field public static final int GET_PERMISSIONS = 4096; // 0x1000
field public static final int GET_PROVIDERS = 8; // 0x8
@@ -47086,6 +47086,7 @@ package android.telephony {
}
public static final class CarrierConfigManager.Ims {
+ field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
field public static final String KEY_PREFIX = "ims.";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
}
@@ -48090,6 +48091,7 @@ package android.telephony {
}
public class SignalStrength implements android.os.Parcelable {
+ ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
method public int describeContents();
method @Deprecated public int getCdmaDbm();
method @Deprecated public int getCdmaEcio();
@@ -48597,6 +48599,9 @@ package android.telephony {
field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+ field public static final int ERI_FLASH = 2; // 0x2
+ field public static final int ERI_OFF = 1; // 0x1
+ field public static final int ERI_ON = 0; // 0x0
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
field public static final String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
@@ -57408,6 +57413,7 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
+ method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method public CharSequence getTextAfterCursor(int, int);
method public CharSequence getTextBeforeCursor(int, int);
method public boolean performContextMenuAction(int);
@@ -57616,6 +57622,17 @@ package android.view.inputmethod {
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
+ public final class SurroundingText implements android.os.Parcelable {
+ ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
+ method public int describeContents();
+ method @IntRange(from=0xffffffff) public int getOffset();
+ method @IntRange(from=0) public int getSelectionEnd();
+ method @IntRange(from=0) public int getSelectionStart();
+ method @NonNull public CharSequence getText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
+ }
+
}
package android.view.inspector {
diff --git a/api/system-current.txt b/api/system-current.txt
index b2f1cfe04638..230ced836c09 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -310,6 +310,7 @@ package android {
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+ field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemVideoCall = 17039401; // 0x1040029
}
@@ -916,6 +917,7 @@ package android.app.admin {
field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
+ field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
@@ -7499,7 +7501,7 @@ package android.net.wifi {
field public static final int BAND_2GHZ = 1; // 0x1
field public static final int BAND_5GHZ = 2; // 0x2
field public static final int BAND_6GHZ = 4; // 0x4
- field public static final int BAND_ANY = 7; // 0x7
+ field @Deprecated public static final int BAND_ANY = 7; // 0x7
field public static final int RANDOMIZATION_NONE = 0; // 0x0
field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
}
@@ -11525,10 +11527,6 @@ package android.telephony {
field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
- public class SignalStrength implements android.os.Parcelable {
- ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
- }
-
public final class SmsCbCmasInfo implements android.os.Parcelable {
ctor public SmsCbCmasInfo(int, int, int, int, int, int);
method public int describeContents();
@@ -11756,6 +11754,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
@@ -12489,6 +12488,10 @@ package android.telephony.ims {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
+ public class ImsManager {
+ method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int);
+ }
+
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -12524,10 +12527,13 @@ package android.telephony.ims {
method public void disableIms(int);
method public void enableIms(int);
method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method public long getImsServiceCapabilities();
method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+ method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
method public void readyForFeatureCreation();
+ field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L
}
public final class ImsSsData implements android.os.Parcelable {
@@ -12773,6 +12779,10 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
+ public class SipDelegateManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+ }
+
}
package android.telephony.ims.feature {
@@ -13022,6 +13032,10 @@ package android.telephony.ims.stub {
method public int updateColr(int);
}
+ public class SipTransportImplBase {
+ ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+ }
+
}
package android.telephony.mbms {
diff --git a/api/test-current.txt b/api/test-current.txt
index 011e86f6cd4f..c92fb6c32b8f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -46,6 +46,7 @@ package android {
field public static final int config_defaultAssistant = 17039393; // 0x1040021
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+ field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemVideoCall = 17039401; // 0x1040029
}
@@ -136,7 +137,6 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePrimarySplitScreen(@NonNull android.graphics.Rect, @NonNull android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void startSystemLockTaskMode(int);
@@ -152,7 +152,7 @@ package android.app {
public class ActivityView extends android.view.ViewGroup {
ctor public ActivityView(android.content.Context);
ctor public ActivityView(android.content.Context, android.util.AttributeSet);
- ctor public ActivityView(@NonNull android.content.Context, @NonNull android.util.AttributeSet, int, boolean, boolean);
+ ctor public ActivityView(@NonNull android.content.Context, @NonNull android.util.AttributeSet, int, boolean);
method public int getVirtualDisplayId();
method public void onLayout(boolean, int, int, int, int);
method public void onLocationChanged();
@@ -172,7 +172,6 @@ package android.app {
method @NonNull public android.app.ActivityView.Builder setAttributeSet(@Nullable android.util.AttributeSet);
method @NonNull public android.app.ActivityView.Builder setDefaultStyle(int);
method @NonNull public android.app.ActivityView.Builder setDisableSurfaceViewBackgroundLayer(boolean);
- method @NonNull public android.app.ActivityView.Builder setSingleInstance(boolean);
method @NonNull public android.app.ActivityView.Builder setUsePublicVirtualDisplay(boolean);
method @NonNull public android.app.ActivityView.Builder setUseTrustedDisplay(boolean);
}
@@ -2374,7 +2373,7 @@ package android.window {
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.TaskAppearedInfo createRootTask(int, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.app.ActivityManager.RunningTaskInfo createRootTask(int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.WindowContainerToken getImeTarget(int);
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index cfc6e3f008d5..b6e5d8846fd9 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -546,7 +546,21 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
return false;
}
}
-
+ for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+ const ValueMetric& metric = config.value_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
const GaugeMetric& metric = config.gauge_metric(i);
set<int64_t> conditionDependencies;
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index dc951be72fe1..0066030ade54 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -170,6 +170,23 @@ DurationMetric createDurationMetric(string name, int64_t what, optional<int64_t>
}
return metric;
}
+
+ValueMetric createValueMetric(string name, const AtomMatcher& what, optional<int64_t> condition,
+ vector<int64_t> states) {
+ ValueMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what.id());
+ metric.set_bucket(TEN_MINUTES);
+ metric.mutable_value_field()->set_field(what.simple_atom_matcher().atom_id());
+ metric.mutable_value_field()->add_child()->set_field(2);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ for (const int64_t state : states) {
+ metric.add_slice_by_state(state);
+ }
+ return metric;
+}
} // anonymous namespace
TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -1537,6 +1554,115 @@ TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) {
EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
+TEST_F(ConfigUpdateTest, TestValueMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_value_metric() =
+ createValueMetric("VALUE1", whatMatcher, predicate.id(), {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ // Change skip zero diff output, which should change the proto, causing replacement.
+ config.mutable_value_metric(0)->set_skip_zero_diff_output(true);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, predicate.id(), {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_value_metric() =
+ createValueMetric("VALUE1", whatMatcher, nullopt, {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
StatsdConfig config;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index d0a0e30bad18..9edf81e8f651 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -406,19 +406,6 @@ public class ActivityTaskManager {
}
/**
- * Makes the display with the given id a single task instance display. I.e the display can only
- * contain one task.
- */
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public void setDisplayToSingleTaskInstance(int displayId) {
- try {
- getService().setDisplayToSingleTaskInstance(displayId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Requests that an activity should enter picture-in-picture mode if possible.
* @hide
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4ea2aacdf7c2..87c729b20c71 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -513,7 +513,6 @@ public final class ActivityThread extends ClientTransactionHandler {
@UnsupportedAppUsage
boolean stopped;
boolean hideForNow;
- Configuration newConfig;
Configuration createdConfig;
Configuration overrideConfig;
// Used to save the last reported configuration from server side so that activity
@@ -2222,21 +2221,6 @@ public final class ActivityThread extends ClientTransactionHandler {
return sPermissionManager;
}
- private Configuration mMainThreadConfig = new Configuration();
-
- Configuration applyConfigCompatMainThread(int displayDensity, Configuration config,
- CompatibilityInfo compat) {
- if (config == null) {
- return null;
- }
- if (!compat.supportsScreen()) {
- mMainThreadConfig.setTo(config);
- config = mMainThreadConfig;
- compat.applyToConfiguration(displayDensity, config);
- }
- return config;
- }
-
/**
* Creates the top level resources for the given package. Will return an existing
* Resources if one has already been created.
@@ -4597,14 +4581,6 @@ public final class ActivityThread extends ClientTransactionHandler {
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
- if (r.newConfig != null) {
- performConfigurationChangedForActivity(r, r.newConfig);
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
- + r.activity.mCurrentConfig);
- }
- r.newConfig = null;
- }
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
ViewRootImpl impl = r.window.getDecorView().getViewRootImpl();
WindowManager.LayoutParams l = impl != null
@@ -4910,13 +4886,6 @@ public final class ActivityThread extends ClientTransactionHandler {
r.activity.makeVisible();
}
}
- if (r.newConfig != null) {
- performConfigurationChangedForActivity(r, r.newConfig);
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating activity vis "
- + r.activityInfo.name + " with new config "
- + r.activity.mCurrentConfig);
- r.newConfig = null;
- }
} else {
if (r.activity.mVisibleFromServer) {
r.activity.mVisibleFromServer = false;
@@ -5488,8 +5457,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- ArrayList<ComponentCallbacks2> collectComponentCallbacks(
- boolean allActivities, Configuration newConfig) {
+ ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
ArrayList<ComponentCallbacks2> callbacks
= new ArrayList<ComponentCallbacks2>();
@@ -5498,29 +5466,11 @@ public final class ActivityThread extends ClientTransactionHandler {
for (int i=0; i<NAPP; i++) {
callbacks.add(mAllApplications.get(i));
}
- final int NACT = mActivities.size();
- for (int i=0; i<NACT; i++) {
- ActivityClientRecord ar = mActivities.valueAt(i);
- Activity a = ar.activity;
- if (a != null) {
- Configuration thisConfig = applyConfigCompatMainThread(
- mCurDefaultDisplayDpi, newConfig,
- ar.packageInfo.getCompatibilityInfo());
- if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
- // If the activity is currently resumed, its configuration
- // needs to change right now.
+ if (includeActivities) {
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final Activity a = mActivities.valueAt(i).activity;
+ if (a != null && !a.mFinished) {
callbacks.add(a);
- } else if (thisConfig != null) {
- // Otherwise, we will tell it about the change
- // the next time it is resumed or shown. Note that
- // the activity manager may, before then, decide the
- // activity needs to be destroyed to handle its new
- // configuration.
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Setting activity "
- + ar.activityInfo.name + " newConfig=" + thisConfig);
- }
- ar.newConfig = thisConfig;
}
}
}
@@ -5544,17 +5494,6 @@ public final class ActivityThread extends ClientTransactionHandler {
}
/**
- * Updates the configuration for an Activity in its current display.
- *
- * @see #performConfigurationChangedForActivity(ActivityClientRecord, Configuration, int,
- * boolean)
- */
- private void performConfigurationChangedForActivity(ActivityClientRecord r,
- Configuration newBaseConfig) {
- performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId());
- }
-
- /**
* Updates the configuration for an Activity. The ActivityClientRecord's
* {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for
* that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering
@@ -5804,7 +5743,8 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
+ final ArrayList<ComponentCallbacks2> callbacks =
+ collectComponentCallbacks(false /* includeActivities */);
freeTextLayoutCachesIfNeeded(configDiff);
@@ -5812,13 +5752,7 @@ public final class ActivityThread extends ClientTransactionHandler {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
ComponentCallbacks2 cb = callbacks.get(i);
- if (cb instanceof Activity) {
- // If callback is an Activity - call corresponding method to consider override
- // config and avoid onConfigurationChanged if it hasn't changed.
- Activity a = (Activity) cb;
- performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
- config);
- } else if (!equivalent) {
+ if (!equivalent) {
performConfigurationChanged(cb, config);
} else {
// TODO (b/135719017): Temporary log for debugging IME service.
@@ -6206,7 +6140,8 @@ public final class ActivityThread extends ClientTransactionHandler {
}
final void handleLowMemory() {
- ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
+ final ArrayList<ComponentCallbacks2> callbacks =
+ collectComponentCallbacks(true /* includeActivities */);
final int N = callbacks.size();
for (int i=0; i<N; i++) {
@@ -6238,7 +6173,8 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
+ final ArrayList<ComponentCallbacks2> callbacks =
+ collectComponentCallbacks(true /* includeActivities */);
final int N = callbacks.size();
for (int i = 0; i < N; i++) {
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index db95eae112b5..8c42a8a55bb1 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -85,8 +85,7 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
}
public ActivityView(Context context, AttributeSet attrs) {
- this(context, attrs, 0 /* defStyle */, false /* singleTaskInstance */,
- false /* usePublicVirtualDisplay */,
+ this(context, attrs, 0 /* defStyle */, false /* usePublicVirtualDisplay */,
false /* disableSurfaceViewBackgroundLayer */, false /* useTrustedDisplay */);
}
@@ -97,22 +96,21 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
*/
public ActivityView(
@NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
- boolean singleTaskInstance, boolean usePublicVirtualDisplay) {
- this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay,
- false /* disableSurfaceViewBackgroundLayer */,
- false /* useTrustedDisplay */);
+ boolean usePublicVirtualDisplay) {
+ this(context, attrs, defStyle, usePublicVirtualDisplay,
+ false /* disableSurfaceViewBackgroundLayer */, false /* useTrustedDisplay */);
}
private ActivityView(
@NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
- boolean singleTaskInstance, boolean usePublicVirtualDisplay,
- boolean disableSurfaceViewBackgroundLayer, boolean useTrustedDisplay) {
+ boolean usePublicVirtualDisplay, boolean disableSurfaceViewBackgroundLayer,
+ boolean useTrustedDisplay) {
super(context, attrs, defStyle);
if (useTaskOrganizer()) {
mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
} else {
- mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance,
- usePublicVirtualDisplay, useTrustedDisplay);
+ mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, usePublicVirtualDisplay,
+ useTrustedDisplay);
}
mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer);
// Since ActivityView#getAlpha has been overridden, we should use parent class's alpha
@@ -641,7 +639,6 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
private final Context mContext;
private AttributeSet mAttrs;
private int mDefStyle;
- private boolean mSingleInstance;
private boolean mUsePublicVirtualDisplay;
private boolean mDisableSurfaceViewBackgroundLayer;
private boolean mUseTrustedDisplay;
@@ -650,7 +647,6 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
mContext = context;
mAttrs = null;
mDefStyle = 0;
- mSingleInstance = false;
mUsePublicVirtualDisplay = false;
mDisableSurfaceViewBackgroundLayer = false;
mUseTrustedDisplay = false;
@@ -676,13 +672,6 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
return this;
}
- /** Sets to {@code true} to make the {@link ActivityView} single instance. */
- @NonNull
- public Builder setSingleInstance(boolean singleInstance) {
- mSingleInstance = singleInstance;
- return this;
- }
-
/**
* Sets to {@code true} to use public virtual display for the {@link ActivityView}.
* <p>
@@ -722,7 +711,7 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
/** Creates an {@link ActivityView} */
@NonNull
public ActivityView build() {
- return new ActivityView(mContext, mAttrs, mDefStyle, mSingleInstance,
+ return new ActivityView(mContext, mAttrs, mDefStyle,
mUsePublicVirtualDisplay, mDisableSurfaceViewBackgroundLayer,
mUseTrustedDisplay);
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 75302293088f..8a03fcc33d51 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -434,12 +434,6 @@ interface IActivityTaskManager {
void clearLaunchParamsForPackages(in List<String> packageNames);
/**
- * Makes the display with the given id a single task instance display. I.e the display can only
- * contain one task.
- */
- void setDisplayToSingleTaskInstance(int displayId);
-
- /**
* Restarts the activity by killing its process if it is visible. If the activity is not
* visible, the activity will not be restarted immediately and just keep the activity record in
* the stack. It also resets the current override configuration so the activity will use the
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index aec9f3e98960..dc7782a1ad3c 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -163,21 +163,6 @@ oneway interface ITaskStackListener {
*/
void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
- /*
- * Called when contents are drawn for the first time on a display which can only contain one
- * task.
- *
- * @param displayId the id of the display on which contents are drawn.
- */
- void onSingleTaskDisplayDrawn(int displayId);
-
- /*
- * Called when the last task is removed from a display which can only contain one task.
- *
- * @param displayId the id of the display from which the window is removed.
- */
- void onSingleTaskDisplayEmpty(int displayId);
-
/**
* Called when a task is reparented to a stack on a different display.
*
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 60e7f0bc91c0..5e50b96a23d5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -82,7 +82,6 @@ import android.util.Pair;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
-import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewGroup;
import android.view.contentcapture.ContentCaptureContext;
@@ -4825,7 +4824,7 @@ public class Notification implements Parcelable
private void resetNotificationHeader(RemoteViews contentView) {
// Small icon doesn't need to be reset, as it's always set. Resetting would prevent
// re-using the drawable when the notification is updated.
- contentView.setBoolean(R.id.notification_header, "setExpanded", false);
+ contentView.setBoolean(R.id.expand_button, "setExpanded", false);
contentView.setTextViewText(R.id.app_name_text, null);
contentView.setViewVisibility(R.id.chronometer, View.GONE);
contentView.setViewVisibility(R.id.header_text, View.GONE);
@@ -5569,7 +5568,7 @@ public class Notification implements Parcelable
*/
public static void makeHeaderExpanded(RemoteViews result) {
if (result != null) {
- result.setBoolean(R.id.notification_header, "setExpanded", true);
+ result.setBoolean(R.id.expand_button, "setExpanded", true);
}
}
@@ -5862,24 +5861,16 @@ public class Notification implements Parcelable
}
/**
- * Apply any necessariy colors to the small icon
+ * Apply any necessary colors to the small icon
*/
private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
StandardTemplateParams p) {
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
- int color;
- if (isColorized(p)) {
- color = getPrimaryTextColor(p);
- } else {
- color = resolveContrastColor(p);
- }
- if (colorable) {
- contentView.setDrawableTint(R.id.icon, false, color,
- PorterDuff.Mode.SRC_ATOP);
-
- }
+ int color = isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p);
+ contentView.setInt(R.id.icon, "setBackgroundColor",
+ resolveBackgroundColor(p));
contentView.setInt(R.id.icon, "setOriginalIconColor",
- colorable ? color : NotificationHeaderView.NO_COLOR);
+ colorable ? color : COLOR_INVALID);
}
/**
@@ -5892,8 +5883,8 @@ public class Notification implements Parcelable
if (largeIcon != null && isLegacy()
&& getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
// resolve color will fall back to the default when legacy
- contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(p),
- PorterDuff.Mode.SRC_ATOP);
+ int color = resolveContrastColor(p);
+ contentView.setInt(R.id.icon, "setOriginalIconColor", color);
}
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index f0e457e9691a..a06ffbdb4301 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -1144,7 +1144,7 @@ public final class NotificationChannel implements Parcelable {
}
private static String longArrayToString(long[] values) {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
if (values != null && values.length > 0) {
for (int i = 0; i < values.length - 1; i++) {
sb.append(values[i]).append(DELIMITER);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b020c7044a00..4b3bebe36c29 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1377,8 +1377,8 @@ public final class SystemServiceRegistry {
/** Throws {@link IllegalStateException} if not during a static initialization. */
private static void ensureInitializing(String methodName) {
- Preconditions.checkState(sInitializing, "Internal error: " + methodName
- + " can only be called during class initialization.");
+ Preconditions.checkState(sInitializing, "Internal error: %s"
+ + " can only be called during class initialization.", methodName);
}
/**
* Creates an array which is used to cache per-Context service instances.
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 17e5e09d76b1..e77d7ac6a4ad 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -173,14 +173,6 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
}
@Override
- public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
- }
-
- @Override
- public void onSingleTaskDisplayEmpty(int displayId) throws RemoteException {
- }
-
- @Override
public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ad902a028f13..054e8429ff86 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1830,6 +1830,15 @@ public class DevicePolicyManager {
public static final int STATE_USER_PROFILE_COMPLETE = 4;
/**
+ * Management setup on a managed profile.
+ * <p>This is used as an intermediate state after {@link #STATE_USER_PROFILE_COMPLETE} once the
+ * work profile has been created.
+ * @hide
+ */
+ @SystemApi
+ public static final int STATE_USER_PROFILE_FINALIZED = 5;
+
+ /**
* @hide
*/
@IntDef(prefix = { "STATE_USER_" }, value = {
@@ -1837,7 +1846,8 @@ public class DevicePolicyManager {
STATE_USER_SETUP_INCOMPLETE,
STATE_USER_SETUP_COMPLETE,
STATE_USER_SETUP_FINALIZED,
- STATE_USER_PROFILE_COMPLETE
+ STATE_USER_PROFILE_COMPLETE,
+ STATE_USER_PROFILE_FINALIZED
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserProvisioningState {}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 90890d58aaff..fbb37db52014 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -241,6 +241,8 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
int result = 17;
result = 31 * result + Objects.hashCode(mActivityCallbacks);
result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
+ result = 31 * result + Objects.hashCode(mClient);
+ result = 31 * result + Objects.hashCode(mActivityToken);
return result;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 6ce05f984cca..e6d6e7ac5dda 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -79,7 +79,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
/**
* Intent used to broadcast the change in the Audio Connection state of the
- * A2DP profile.
+ * HDP profile.
*
* <p>This intent will have 3 extras:
* <ul>
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 1fb426eb3cfc..30775b19ab00 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -533,21 +533,21 @@ public class ContentProviderOperation implements Parcelable {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ContentProviderOperation(");
- sb.append("type=" + typeToString(mType) + " ");
+ sb.append("type=").append(typeToString(mType)).append(' ');
if (mUri != null) {
- sb.append("uri=" + mUri + " ");
+ sb.append("uri=").append(mUri).append(' ');
}
if (mValues != null) {
- sb.append("values=" + mValues + " ");
+ sb.append("values=").append(mValues).append(' ');
}
if (mSelection != null) {
- sb.append("selection=" + mSelection + " ");
+ sb.append("selection=").append(mSelection).append(' ');
}
if (mSelectionArgs != null) {
- sb.append("selectionArgs=" + mSelectionArgs + " ");
+ sb.append("selectionArgs=").append(mSelectionArgs).append(' ');
}
if (mExpectedCount != null) {
- sb.append("expectedCount=" + mExpectedCount + " ");
+ sb.append("expectedCount=").append(mExpectedCount).append(' ');
}
if (mYieldAllowed) {
sb.append("yieldAllowed ");
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
index 4fb1ddb958d5..fdfe7eb33530 100644
--- a/core/java/android/content/ContentProviderResult.java
+++ b/core/java/android/content/ContentProviderResult.java
@@ -143,16 +143,16 @@ public class ContentProviderResult implements Parcelable {
public String toString() {
final StringBuilder sb = new StringBuilder("ContentProviderResult(");
if (uri != null) {
- sb.append("uri=" + uri + " ");
+ sb.append("uri=").append(uri).append(' ');
}
if (count != null) {
- sb.append("count=" + count + " ");
+ sb.append("count=").append(count).append(' ');
}
if (extras != null) {
- sb.append("extras=" + extras + " ");
+ sb.append("extras=").append(extras).append(' ');
}
if (exception != null) {
- sb.append("exception=" + exception + " ");
+ sb.append("exception=").append(exception).append(' ');
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c62194b380dd..9216a0871870 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -619,7 +619,6 @@ import java.util.TimeZone;
* <li> {@link #EXTRA_PHONE_NUMBER}
* <li> {@link #EXTRA_REFERRER}
* <li> {@link #EXTRA_REMOTE_INTENT_TOKEN}
- * <li> {@link #EXTRA_REMOVED_BY_SYSTEM}
* <li> {@link #EXTRA_REPLACING}
* <li> {@link #EXTRA_SHORTCUT_ICON}
* <li> {@link #EXTRA_SHORTCUT_ICON_RESOURCE}
@@ -631,6 +630,7 @@ import java.util.TimeZone;
* <li> {@link #EXTRA_TEXT}
* <li> {@link #EXTRA_TITLE}
* <li> {@link #EXTRA_UID}
+ * <li> {@link #EXTRA_USER_INITIATED}
* </ul>
*
* <h3>Flags</h3>
@@ -2461,8 +2461,8 @@ public class Intent implements Parcelable, Cloneable {
* application -- data and code -- is being removed.
* <li> {@link #EXTRA_REPLACING} is set to true if this will be followed
* by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package.
- * <li> {@link #EXTRA_REMOVED_BY_SYSTEM} containing boolean field to to signal that the
- * application was removed automatically without the user-initiated action.
+ * <li> {@link #EXTRA_USER_INITIATED} containing boolean field to signal that the application
+ * was removed with the user-initiated action.
* </ul>
*
* <p class="note">This is a protected intent that can only be sent
@@ -5555,11 +5555,9 @@ public class Intent implements Parcelable, Cloneable {
/**
* Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
- * intents to signal that the application was removed automatically without the user-initiated
- * action.
+ * intents to signal that the application was removed with the user-initiated action.
*/
- public static final String EXTRA_REMOVED_BY_SYSTEM =
- "android.intent.extra.REMOVED_BY_SYSTEM";
+ public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
/**
* A String holding the phone number originally entered in
@@ -7447,6 +7445,7 @@ public class Intent implements Parcelable, Cloneable {
/** @hide */
@UnsupportedAppUsage
+ @SuppressWarnings("AndroidFrameworkEfficientCollections")
public static Intent parseCommandArgs(ShellCommand cmd, CommandOptionHandler optionHandler)
throws URISyntaxException {
Intent intent = new Intent();
diff --git a/core/java/android/content/SyncResult.java b/core/java/android/content/SyncResult.java
index 8280f8e637ba..7e68dcafd1b5 100644
--- a/core/java/android/content/SyncResult.java
+++ b/core/java/android/content/SyncResult.java
@@ -292,7 +292,7 @@ public final class SyncResult implements Parcelable {
* @return debugging string.
*/
public String toDebugString() {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
if (fullSyncRequested) {
sb.append("f1");
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index a64f39367e26..e3598004d277 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -129,7 +129,7 @@ public abstract class AtomicFormula extends IntegrityFormula {
private final @Key int mKey;
public AtomicFormula(@Key int key) {
- checkArgument(isValidKey(key), String.format("Unknown key: %d", key));
+ checkArgument(isValidKey(key), "Unknown key: %d", key);
mKey = key;
}
@@ -149,8 +149,7 @@ public abstract class AtomicFormula extends IntegrityFormula {
super(key);
checkArgument(
key == VERSION_CODE,
- String.format(
- "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+ "Key %s cannot be used with LongAtomicFormula", keyToString(key));
mValue = null;
mOperator = null;
}
@@ -168,10 +167,9 @@ public abstract class AtomicFormula extends IntegrityFormula {
super(key);
checkArgument(
key == VERSION_CODE,
- String.format(
- "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+ "Key %s cannot be used with LongAtomicFormula", keyToString(key));
checkArgument(
- isValidOperator(operator), String.format("Unknown operator: %d", operator));
+ isValidOperator(operator), "Unknown operator: %d", operator);
mOperator = operator;
mValue = value;
}
@@ -317,8 +315,7 @@ public abstract class AtomicFormula extends IntegrityFormula {
|| key == INSTALLER_CERTIFICATE
|| key == INSTALLER_NAME
|| key == STAMP_CERTIFICATE_HASH,
- String.format(
- "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+ "Key %s cannot be used with StringAtomicFormula", keyToString(key));
mValue = null;
mIsHashedValue = null;
}
@@ -339,8 +336,7 @@ public abstract class AtomicFormula extends IntegrityFormula {
|| key == INSTALLER_CERTIFICATE
|| key == INSTALLER_NAME
|| key == STAMP_CERTIFICATE_HASH,
- String.format(
- "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+ "Key %s cannot be used with StringAtomicFormula", keyToString(key));
mValue = value;
mIsHashedValue = isHashed;
}
@@ -365,8 +361,7 @@ public abstract class AtomicFormula extends IntegrityFormula {
|| key == INSTALLER_CERTIFICATE
|| key == INSTALLER_NAME
|| key == STAMP_CERTIFICATE_HASH,
- String.format(
- "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+ "Key %s cannot be used with StringAtomicFormula", keyToString(key));
mValue = hashValue(key, value);
mIsHashedValue =
(key == APP_CERTIFICATE
diff --git a/core/java/android/content/integrity/CompoundFormula.java b/core/java/android/content/integrity/CompoundFormula.java
index 160bec9a51b9..1ffabd03cc26 100644
--- a/core/java/android/content/integrity/CompoundFormula.java
+++ b/core/java/android/content/integrity/CompoundFormula.java
@@ -84,7 +84,7 @@ public final class CompoundFormula extends IntegrityFormula implements Parcelabl
*/
public CompoundFormula(@Connector int connector, List<IntegrityFormula> formulas) {
checkArgument(
- isValidConnector(connector), String.format("Unknown connector: %d", connector));
+ isValidConnector(connector), "Unknown connector: %d", connector);
validateFormulas(connector, formulas);
this.mConnector = connector;
this.mFormulas = Collections.unmodifiableList(formulas);
@@ -93,7 +93,7 @@ public final class CompoundFormula extends IntegrityFormula implements Parcelabl
CompoundFormula(Parcel in) {
mConnector = in.readInt();
int length = in.readInt();
- checkArgument(length >= 0, "Must have non-negative length. Got " + length);
+ checkArgument(length >= 0, "Must have non-negative length. Got %d", length);
mFormulas = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
mFormulas.add(IntegrityFormula.readFromParcel(in));
@@ -196,16 +196,14 @@ public final class CompoundFormula extends IntegrityFormula implements Parcelabl
case OR:
checkArgument(
formulas.size() >= 2,
- String.format(
- "Connector %s must have at least 2 formulas",
- connectorToString(connector)));
+ "Connector %s must have at least 2 formulas",
+ connectorToString(connector));
break;
case NOT:
checkArgument(
formulas.size() == 1,
- String.format(
- "Connector %s must have 1 formula only",
- connectorToString(connector)));
+ "Connector %s must have 1 formula only",
+ connectorToString(connector));
break;
}
}
diff --git a/core/java/android/content/integrity/IntegrityUtils.java b/core/java/android/content/integrity/IntegrityUtils.java
index c3f762469348..c184c6999aa9 100644
--- a/core/java/android/content/integrity/IntegrityUtils.java
+++ b/core/java/android/content/integrity/IntegrityUtils.java
@@ -36,7 +36,7 @@ public class IntegrityUtils {
public static byte[] getBytesFromHexDigest(String hexDigest) {
checkArgument(
hexDigest.length() % 2 == 0,
- "Invalid hex encoding " + hexDigest + ": must have even length");
+ "Invalid hex encoding %s: must have even length", hexDigest);
byte[] rawBytes = new byte[hexDigest.length() / 2];
for (int i = 0; i < rawBytes.length; i++) {
diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java
index 391d1d04d553..34eb1e7a4857 100644
--- a/core/java/android/content/integrity/Rule.java
+++ b/core/java/android/content/integrity/Rule.java
@@ -64,7 +64,7 @@ public final class Rule implements Parcelable {
private final @Effect int mEffect;
public Rule(@NonNull IntegrityFormula formula, @Effect int effect) {
- checkArgument(isValidEffect(effect), String.format("Unknown effect: %d", effect));
+ checkArgument(isValidEffect(effect), "Unknown effect: %d", effect);
this.mFormula = Objects.requireNonNull(formula);
this.mEffect = effect;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fc7b3e011cad..3fb9a9e924e7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -301,7 +301,10 @@ public abstract class PackageManager {
/**
* {@link PackageInfo} flag: return information about the
* intent filters supported by the activity.
+ *
+ * @deprecated The platform does not support getting {@link IntentFilter}s for the package.
*/
+ @Deprecated
public static final int GET_INTENT_FILTERS = 0x00000020;
/**
@@ -1574,6 +1577,26 @@ public abstract class PackageManager {
*/
public static final int INSTALL_PARSE_FAILED_SKIPPED = -125;
+ /**
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because it is attempting to define a permission group that is already defined by some
+ * existing package.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP = -126;
+
+ /**
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because it is attempting to define a permission in a group that does not exists or that is
+ * defined by an packages with an incompatible certificate.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_BAD_PERMISSION_GROUP = -127;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -6168,9 +6191,30 @@ public abstract class PackageManager {
public abstract Resources getResourcesForApplication(@NonNull String packageName)
throws NameNotFoundException;
- /** @hide */
+ /**
+ * Please don't use this function because it is no longer supported.
+ *
+ * @deprecated Instead of using this function, please use
+ * {@link Context#createContextAsUser(UserHandle, int)} to create the specified user
+ * context, {@link Context#getPackageManager()} to get PackageManager instance for
+ * the specified user, and then
+ * {@link PackageManager#getResourcesForApplication(String)} to get the same
+ * Resources instance.
+ * @see {@link Context#createContextAsUser(android.os.UserHandle, int)}
+ * @see {@link Context#getPackageManager()}
+ * @see {@link android.content.pm.PackageManager#getResourcesForApplication(java.lang.String)}
+ * TODO(b/170852794): mark maxTargetSdk as {@code Build.VERSION_CODES.S}
+ * @hide
+ */
@NonNull
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170928809,
+ publicAlternatives = "Use {@code Context#createContextAsUser(UserHandle, int)}"
+ + " to create the relevant user context,"
+ + " {@link android.content.Context#getPackageManager()} and"
+ + " {@link android.content.pm.PackageManager#getResourcesForApplication("
+ + "java.lang.String)}"
+ + " instead.")
+ @Deprecated
public abstract Resources getResourcesForApplicationAsUser(@NonNull String packageName,
@UserIdInt int userId) throws NameNotFoundException;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index e730fee7f812..0f9e8e59ec94 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -453,82 +453,85 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
/** @hide */
@UnsupportedAppUsage
public static @NonNull String protectionToString(int level) {
- String protLevel = "????";
+ final StringBuilder protLevel = new StringBuilder();
switch (level & PROTECTION_MASK_BASE) {
case PermissionInfo.PROTECTION_DANGEROUS:
- protLevel = "dangerous";
+ protLevel.append("dangerous");
break;
case PermissionInfo.PROTECTION_NORMAL:
- protLevel = "normal";
+ protLevel.append("normal");
break;
case PermissionInfo.PROTECTION_SIGNATURE:
- protLevel = "signature";
+ protLevel.append("signature");
break;
case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
- protLevel = "signatureOrSystem";
+ protLevel.append("signatureOrSystem");
+ break;
+ default:
+ protLevel.append("????");
break;
}
if ((level & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
- protLevel += "|privileged";
+ protLevel.append("|privileged");
}
if ((level & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
- protLevel += "|development";
+ protLevel.append("|development");
}
if ((level & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- protLevel += "|appop";
+ protLevel.append("|appop");
}
if ((level & PermissionInfo.PROTECTION_FLAG_PRE23) != 0) {
- protLevel += "|pre23";
+ protLevel.append("|pre23");
}
if ((level & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0) {
- protLevel += "|installer";
+ protLevel.append("|installer");
}
if ((level & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0) {
- protLevel += "|verifier";
+ protLevel.append("|verifier");
}
if ((level & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
- protLevel += "|preinstalled";
+ protLevel.append("|preinstalled");
}
if ((level & PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
- protLevel += "|setup";
+ protLevel.append("|setup");
}
if ((level & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
- protLevel += "|instant";
+ protLevel.append("|instant");
}
if ((level & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
- protLevel += "|runtime";
+ protLevel.append("|runtime");
}
if ((level & PermissionInfo.PROTECTION_FLAG_OEM) != 0) {
- protLevel += "|oem";
+ protLevel.append("|oem");
}
if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
- protLevel += "|vendorPrivileged";
+ protLevel.append("|vendorPrivileged");
}
if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
- protLevel += "|textClassifier";
+ protLevel.append("|textClassifier");
}
if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
- protLevel += "|wellbeing";
+ protLevel.append("|wellbeing");
}
if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
- protLevel += "|documenter";
+ protLevel.append("|documenter");
}
if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
- protLevel += "|configurator";
+ protLevel.append("|configurator");
}
if ((level & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0) {
- protLevel += "|incidentReportApprover";
+ protLevel.append("|incidentReportApprover");
}
if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
- protLevel += "|appPredictor";
+ protLevel.append("|appPredictor");
}
if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
- protLevel += "|companion";
+ protLevel.append("|companion");
}
if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
- protLevel += "|retailDemo";
+ protLevel.append("|retailDemo");
}
- return protLevel;
+ return protLevel.toString();
}
/**
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 1b3c46f90851..7ed803f78260 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -630,7 +630,7 @@ public final class ShortcutInfo implements Parcelable {
* This will set {@link #FLAG_STRINGS_RESOLVED}.
*
* @param res {@link Resources} for the publisher. Must have been loaded with
- * {@link PackageManager#getResourcesForApplicationAsUser}.
+ * {@link PackageManager#getResourcesForApplication(String)}.
*
* @hide
*/
@@ -752,7 +752,7 @@ public final class ShortcutInfo implements Parcelable {
* aforementioned method would do internally, but not documented, so doing here explicitly.)
*
* @param res {@link Resources} for the publisher. Must have been loaded with
- * {@link PackageManager#getResourcesForApplicationAsUser}.
+ * {@link PackageManager#getResourcesForApplication(String)}.
*
* @hide
*/
@@ -782,7 +782,7 @@ public final class ShortcutInfo implements Parcelable {
* in the resource name fields.
*
* @param res {@link Resources} for the publisher. Must have been loaded with
- * {@link PackageManager#getResourcesForApplicationAsUser}.
+ * {@link PackageManager#getResourcesForApplication(String)}.
*
* @hide
*/
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index ccb8cdd71278..f8ed27a6646b 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -28,6 +28,7 @@ import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -330,15 +331,7 @@ public class CompatibilityInfo implements Parcelable {
}
/**
- * Translate the screen rect to the application frame.
- */
- @UnsupportedAppUsage
- public void translateRectInScreenToAppWinFrame(Rect rect) {
- rect.scale(applicationInvertedScale);
- }
-
- /**
- * Translate the region in window to screen.
+ * Translate the region in window to screen.
*/
@UnsupportedAppUsage
public void translateRegionInWindowToScreen(Region transparentRegion) {
@@ -388,7 +381,14 @@ public class CompatibilityInfo implements Parcelable {
public void translateWindowLayout(WindowManager.LayoutParams params) {
params.scale(applicationScale);
}
-
+
+ /**
+ * Translate a length in application's window to screen.
+ */
+ public float translateLengthInAppWindowToScreen(float length) {
+ return length * applicationScale;
+ }
+
/**
* Translate a Rect in application's window to screen.
*/
@@ -396,7 +396,7 @@ public class CompatibilityInfo implements Parcelable {
public void translateRectInAppWindowToScreen(Rect rect) {
rect.scale(applicationScale);
}
-
+
/**
* Translate a Rect in screen coordinates into the app window's coordinates.
*/
@@ -406,6 +406,13 @@ public class CompatibilityInfo implements Parcelable {
}
/**
+ * Translate an InsetsState in screen coordinates into the app window's coordinates.
+ */
+ public void translateInsetsStateInScreenToAppWindow(InsetsState state) {
+ state.scale(applicationInvertedScale);
+ }
+
+ /**
* Translate a Point in screen coordinates into the app window's coordinates.
*/
public void translatePointInScreenToAppWindow(PointF point) {
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index ac0593869d5a..edb7b715e944 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -792,10 +792,10 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
if (pid == myPid) {
buff.append("this proc=");
} else {
- buff.append("pid " + pid + "=");
+ buff.append("pid ").append(pid).append('=');
}
int num = pidCounts.get(pid);
- buff.append(num + ")");
+ buff.append(num).append(')');
total += num;
}
// limit the returned string size to 1000
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index b978ae559390..6c8a8500e4e3 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -672,7 +672,7 @@ public class DatabaseUtils {
* @param sb the StringBuilder to print to
*/
public static void dumpCursor(Cursor cursor, StringBuilder sb) {
- sb.append(">>>>> Dumping cursor " + cursor + "\n");
+ sb.append(">>>>> Dumping cursor ").append(cursor).append('\n');
if (cursor != null) {
int startPos = cursor.getPosition();
@@ -739,7 +739,7 @@ public class DatabaseUtils {
*/
public static void dumpCurrentRow(Cursor cursor, StringBuilder sb) {
String[] cols = cursor.getColumnNames();
- sb.append("" + cursor.getPosition() + " {\n");
+ sb.append(cursor.getPosition()).append(" {\n");
int length = cols.length;
for (int i = 0; i < length; i++) {
String value;
@@ -750,7 +750,7 @@ public class DatabaseUtils {
// representable by a string, e.g. it is a BLOB.
value = "<unprintable>";
}
- sb.append(" " + cols[i] + '=' + value + "\n");
+ sb.append(" ").append(cols[i]).append('=').append(value).append('\n');
}
sb.append("}\n");
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 2f67f6ddc082..865940e796c8 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -226,7 +226,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
} catch (SQLiteCantOpenDatabaseException e) {
- String message = String.format("Cannot open database '%s'", file);
+ final StringBuilder message = new StringBuilder("Cannot open database '")
+ .append(file).append('\'');
try {
// Try to diagnose for common reasons. If something fails in here, that's fine;
@@ -236,20 +237,21 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
final Path dir = path.getParent();
if (!Files.isDirectory(dir)) {
- message += ": Directory " + dir + " doesn't exist";
+ message.append(": Directory ").append(dir).append(" doesn't exist");
} else if (!Files.exists(path)) {
- message += ": File " + path + " doesn't exist";
+ message.append(": File ").append(path).append(" doesn't exist");
} else if (!Files.isReadable(path)) {
- message += ": File " + path + " is not readable";
+ message.append(": File ").append(path).append(" is not readable");
} else if (Files.isDirectory(path)) {
- message += ": Path " + path + " is a directory";
+ message.append(": Path ").append(path).append(" is a directory");
} else {
- message += ": Unknown reason";
+ message.append(": Unknown reason");
}
} catch (Throwable th) {
- message += ": Unknown reason; cannot examine filesystem: " + th.getMessage();
+ message.append(": Unknown reason; cannot examine filesystem: ")
+ .append(th.getMessage());
}
- throw new SQLiteCantOpenDatabaseException(message, e);
+ throw new SQLiteCantOpenDatabaseException(message.toString(), e);
} finally {
mRecentOperations.endOperation(cookie);
}
@@ -1293,11 +1295,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
} catch (SQLiteException ex) {
// Ignore.
}
- String label = " (attached) " + name;
+ StringBuilder label = new StringBuilder(" (attached) ").append(name);
if (!path.isEmpty()) {
- label += ": " + path;
+ label.append(": ").append(path);
}
- dbStatsList.add(new DbStats(label, pageCount, pageSize, 0, 0, 0, 0));
+ dbStatsList.add(new DbStats(label.toString(), pageCount, pageSize, 0, 0, 0, 0));
}
} catch (SQLiteException ex) {
// Ignore.
@@ -1319,9 +1321,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
private DbStats getMainDbStatsUnsafe(int lookaside, long pageCount, long pageSize) {
// The prepared statement cache is thread-safe so we can access its statistics
// even if we do not own the database connection.
- String label = mConfiguration.path;
- if (!mIsPrimaryConnection) {
- label += " (" + mConnectionId + ")";
+ String label;
+ if (mIsPrimaryConnection) {
+ label = mConfiguration.path;
+ } else {
+ label = mConfiguration.path + " (" + mConnectionId + ")";
}
return new DbStats(label, pageCount, pageSize, lookaside,
mPreparedStatementCache.hitCount(),
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 4559e918acc5..3bfbe7e7b93b 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -147,7 +147,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
clearOrCreateWindow(getDatabase().getPath());
try {
Preconditions.checkArgumentNonnegative(requiredPos,
- "requiredPos cannot be negative, but was " + requiredPos);
+ "requiredPos cannot be negative");
if (mCount == NO_COUNT) {
mCount = mQuery.fillWindow(mWindow, requiredPos, requiredPos, true);
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 0efd883e8b55..a2cbdd3829ff 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1692,7 +1692,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
sql.append((i > 0) ? ",?" : "?");
}
} else {
- sql.append(nullColumnHack + ") VALUES (NULL");
+ sql.append(nullColumnHack).append(") VALUES (NULL");
}
sql.append(')');
@@ -2674,7 +2674,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
"lookasideSlotSize cannot be negative");
Preconditions.checkArgument(
(slotSize > 0 && slotCount > 0) || (slotCount == 0 && slotSize == 0),
- "Invalid configuration: " + slotSize + ", " + slotCount);
+ "Invalid configuration: %d, %d", slotSize, slotCount);
mLookasideSlotSize = slotSize;
mLookasideSlotCount = slotCount;
diff --git a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
index 1e1c4b171e2e..5b3a6d726422 100644
--- a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
+++ b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
@@ -78,7 +78,7 @@ public final class ColorSpaceTransform {
mElements = new int[COUNT_INT];
for (int i = 0; i < elements.length; ++i) {
- checkNotNull(elements, "element[" + i + "] must not be null");
+ checkNotNull(elements, "element[%d] must not be null", i);
mElements[i * RATIONAL_SIZE + OFFSET_NUMERATOR] = elements[i].getNumerator();
mElements[i * RATIONAL_SIZE + OFFSET_DENOMINATOR] = elements[i].getDenominator();
}
@@ -116,7 +116,7 @@ public final class ColorSpaceTransform {
}
for (int i = 0; i < elements.length; ++i) {
- checkNotNull(elements, "element " + i + " must not be null");
+ checkNotNull(elements, "element %d must not be null", i);
}
mElements = Arrays.copyOf(elements, elements.length);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 5b186c78df54..401bb9d32492 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -319,6 +319,29 @@ public final class HdmiControlManager {
/** The HdmiControlService will be disabled to standby. */
public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3;
+ // -- Whether the HDMI CEC is enabled or disabled.
+ /**
+ * HDMI CEC enabled.
+ *
+ * @hide
+ */
+ public static final String HDMI_CEC_CONTROL_ENABLED = "1";
+ /**
+ * HDMI CEC disabled.
+ *
+ * @hide
+ */
+ public static final String HDMI_CEC_CONTROL_DISABLED = "0";
+ /**
+ * @hide
+ */
+ @StringDef({
+ HDMI_CEC_CONTROL_ENABLED,
+ HDMI_CEC_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HdmiCecControl {}
+
// -- Which devices the playback device can send a <Standby> message to upon going to sleep.
/**
* Send <Standby> to TV only.
@@ -347,8 +370,91 @@ public final class HdmiControlManager {
SEND_STANDBY_ON_SLEEP_NONE
})
@Retention(RetentionPolicy.SOURCE)
- public @interface StandbyBehavior {
- }
+ public @interface StandbyBehavior {}
+
+ // -- Which power state action should be taken when Active Source is lost.
+ /**
+ * No action to be taken.
+ *
+ * @hide
+ */
+ public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
+ /**
+ * Go to standby immediately.
+ *
+ * @hide
+ */
+ public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
+ /**
+ * @hide
+ */
+ @StringDef({
+ POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
+ POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActiveSourceLostBehavior {}
+
+ // -- Whether System Audio Mode muting is enabled or disabled.
+ /**
+ * System Audio Mode muting enabled.
+ *
+ * @hide
+ */
+ public static final String SYSTEM_AUDIO_MODE_MUTING_ENABLED = "1";
+ /**
+ * System Audio Mode muting disabled.
+ *
+ * @hide
+ */
+ public static final String SYSTEM_AUDIO_MODE_MUTING_DISABLED = "0";
+ /**
+ * @hide
+ */
+ @StringDef({
+ SYSTEM_AUDIO_MODE_MUTING_ENABLED,
+ SYSTEM_AUDIO_MODE_MUTING_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemAudioModeMuting {}
+
+ // -- Settings available in the CEC Configuration.
+ /**
+ * Name of a setting deciding whether the CEC is enabled.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled";
+ /**
+ * Name of a setting deciding on the Standby message behaviour on sleep.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP = "send_standby_on_sleep";
+ /**
+ * Name of a setting deciding on power state action when losing Active Source.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+ "power_state_change_on_active_source_lost";
+ /**
+ * Name of a setting deciding whether System Audio Muting is allowed.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING =
+ "system_audio_mode_muting";
+ /**
+ * @hide
+ */
+ @StringDef({
+ CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+ })
+ public @interface CecSettingName {}
// True if we have a logical device of type playback hosted in the system.
private final boolean mHasPlaybackDevice;
@@ -1186,4 +1292,234 @@ public final class HdmiControlManager {
}
};
}
+
+ /**
+ * Get a set of user-modifiable settings.
+ *
+ * @return a set of user-modifiable settings.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @CecSettingName
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public List<String> getUserCecSettings() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getUserCecSettings();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get a set of allowed values for a settings.
+ *
+ * @param name name of the setting
+ * @return a set of allowed values for a settings. {@code null} on failure.
+ * @throws IllegalArgumentException when setting {@code name} does not exist.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public List<String> getAllowedCecSettingValues(@NonNull @CecSettingName String name) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getAllowedCecSettingValues(name);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'hdmi_cec_enabled' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setHdmiCecEnabled(@NonNull @HdmiCecControl String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'hdmi_cec_enabled' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @HdmiCecControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getHdmiCecEnabled() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'send_standby_on_sleep' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setSendStandbyOnSleep(@NonNull @StandbyBehavior String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'send_standby_on_sleep' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @StandbyBehavior
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getSendStandbyOnSleep() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'power_state_change_on_active_source_lost' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setPowerStateChangeOnActiveSourceLost(
+ @NonNull @ActiveSourceLostBehavior String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(
+ CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'power_state_change_on_active_source_lost' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @ActiveSourceLostBehavior
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getPowerStateChangeOnActiveSourceLost() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(
+ CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'system_audio_mode_muting' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'system_audio_mode_muting' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemAudioModeMuting
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getSystemAudioModeMuting() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 02896351ea3a..22d4640be493 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -20,6 +20,7 @@ import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.TestApi;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -293,6 +294,26 @@ public final class HdmiControlServiceWrapper {
IHdmiCecVolumeControlFeatureListener listener) {
HdmiControlServiceWrapper.this.removeHdmiCecVolumeControlFeatureListener(listener);
}
+
+ @Override
+ public List<String> getUserCecSettings() {
+ return HdmiControlServiceWrapper.this.getUserCecSettings();
+ }
+
+ @Override
+ public List<String> getAllowedCecSettingValues(String name) {
+ return HdmiControlServiceWrapper.this.getAllowedCecSettingValues(name);
+ }
+
+ @Override
+ public String getCecSettingValue(String name) {
+ return HdmiControlServiceWrapper.this.getCecSettingValue(name);
+ }
+
+ @Override
+ public void setCecSettingValue(String name, String value) {
+ HdmiControlServiceWrapper.this.setCecSettingValue(name, value);
+ }
};
@BinderThread
@@ -466,4 +487,22 @@ public final class HdmiControlServiceWrapper {
/** @hide */
public void removeHdmiCecVolumeControlFeatureListener(
IHdmiCecVolumeControlFeatureListener listener) {}
+
+ /** @hide */
+ public List<String> getUserCecSettings() {
+ return new ArrayList<>();
+ }
+
+ /** @hide */
+ public List<String> getAllowedCecSettingValues(String name) {
+ return new ArrayList<>();
+ }
+
+ /** @hide */
+ public String getCecSettingValue(String name) {
+ return "";
+ }
+
+ /** @hide */
+ public void setCecSettingValue(String name, String value) {}
}
diff --git a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
index 55b07268d201..3fd20f12381e 100644
--- a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
@@ -463,7 +463,7 @@ public class HdmiDeviceInfo implements Parcelable {
@NonNull
@Override
public String toString() {
- StringBuffer s = new StringBuffer();
+ StringBuilder s = new StringBuilder();
switch (mHdmiDeviceType) {
case HDMI_DEVICE_TYPE_CEC:
s.append("CEC: ");
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index e97e120109bf..e4b311a64c31 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -169,7 +169,7 @@ public final class HdmiPortInfo implements Parcelable {
@NonNull
@Override
public String toString() {
- StringBuffer s = new StringBuffer();
+ StringBuilder s = new StringBuilder();
s.append("port_id: ").append(mId).append(", ");
s.append("type: ").append((mType == PORT_INPUT) ? "HDMI_IN" : "HDMI_OUT").append(", ");
s.append("address: ").append(String.format("0x%04x", mAddress)).append(", ");
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 4c724ef62ea9..6df164bb71dc 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -87,4 +87,8 @@ interface IHdmiControlService {
boolean isHdmiCecVolumeControlEnabled();
void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
void setSystemAudioModeOnForAudioOnlySource();
+ List<String> getUserCecSettings();
+ List<String> getAllowedCecSettingValues(String name);
+ String getCecSettingValue(String name);
+ void setCecSettingValue(String name, String value);
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 88525895d9ec..b550c7d4ba81 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -4049,7 +4049,8 @@ public abstract class BatteryStats implements Parcelable {
if (cpuFreqs != null) {
sb.setLength(0);
for (int i = 0; i < cpuFreqs.length; ++i) {
- sb.append((i == 0 ? "" : ",") + cpuFreqs[i]);
+ if (i != 0) sb.append(',');
+ sb.append(cpuFreqs[i]);
}
dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
}
@@ -4368,12 +4369,13 @@ public abstract class BatteryStats implements Parcelable {
if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
sb.setLength(0);
for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
- sb.append((i == 0 ? "" : ",") + cpuFreqTimeMs[i]);
+ if (i != 0) sb.append(',');
+ sb.append(cpuFreqTimeMs[i]);
}
final long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
if (screenOffCpuFreqTimeMs != null) {
for (int i = 0; i < screenOffCpuFreqTimeMs.length; ++i) {
- sb.append("," + screenOffCpuFreqTimeMs[i]);
+ sb.append(',').append(screenOffCpuFreqTimeMs[i]);
}
} else {
for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
@@ -4389,13 +4391,14 @@ public abstract class BatteryStats implements Parcelable {
if (timesMs != null && timesMs.length == cpuFreqs.length) {
sb.setLength(0);
for (int i = 0; i < timesMs.length; ++i) {
- sb.append((i == 0 ? "" : ",") + timesMs[i]);
+ if (i != 0) sb.append(',');
+ sb.append(timesMs[i]);
}
final long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes(
which, procState);
if (screenOffTimesMs != null) {
for (int i = 0; i < screenOffTimesMs.length; ++i) {
- sb.append("," + screenOffTimesMs[i]);
+ sb.append(',').append(screenOffTimesMs[i]);
}
} else {
for (int i = 0; i < timesMs.length; ++i) {
@@ -5427,7 +5430,7 @@ public abstract class BatteryStats implements Parcelable {
sb.setLength(0);
sb.append(" CPU freqs:");
for (int i = 0; i < cpuFreqs.length; ++i) {
- sb.append(" " + cpuFreqs[i]);
+ sb.append(' ').append(cpuFreqs[i]);
}
pw.println(sb.toString());
pw.println();
@@ -6036,7 +6039,7 @@ public abstract class BatteryStats implements Parcelable {
sb.setLength(0);
sb.append(" Total cpu time per freq:");
for (int i = 0; i < cpuFreqTimes.length; ++i) {
- sb.append(" " + cpuFreqTimes[i]);
+ sb.append(' ').append(cpuFreqTimes[i]);
}
pw.println(sb.toString());
}
@@ -6045,7 +6048,7 @@ public abstract class BatteryStats implements Parcelable {
sb.setLength(0);
sb.append(" Total screen-off cpu time per freq:");
for (int i = 0; i < screenOffCpuFreqTimes.length; ++i) {
- sb.append(" " + screenOffCpuFreqTimes[i]);
+ sb.append(' ').append(screenOffCpuFreqTimes[i]);
}
pw.println(sb.toString());
}
@@ -6054,8 +6057,8 @@ public abstract class BatteryStats implements Parcelable {
final long[] cpuTimes = u.getCpuFreqTimes(which, procState);
if (cpuTimes != null) {
sb.setLength(0);
- sb.append(" Cpu times per freq at state "
- + Uid.PROCESS_STATE_NAMES[procState] + ":");
+ sb.append(" Cpu times per freq at state ")
+ .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
for (int i = 0; i < cpuTimes.length; ++i) {
sb.append(" " + cpuTimes[i]);
}
@@ -6065,8 +6068,8 @@ public abstract class BatteryStats implements Parcelable {
final long[] screenOffCpuTimes = u.getScreenOffCpuFreqTimes(which, procState);
if (screenOffCpuTimes != null) {
sb.setLength(0);
- sb.append(" Screen-off cpu times per freq at state "
- + Uid.PROCESS_STATE_NAMES[procState] + ":");
+ sb.append(" Screen-off cpu times per freq at state ")
+ .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
for (int i = 0; i < screenOffCpuTimes.length; ++i) {
sb.append(" " + screenOffCpuTimes[i]);
}
diff --git a/core/java/android/os/CombinedVibrationEffect.aidl b/core/java/android/os/CombinedVibrationEffect.aidl
new file mode 100644
index 000000000000..330733c2643f
--- /dev/null
+++ b/core/java/android/os/CombinedVibrationEffect.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+parcelable CombinedVibrationEffect;
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
new file mode 100644
index 000000000000..77bfa577babd
--- /dev/null
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A CombinedVibrationEffect describes a haptic effect to be performed by one or more {@link
+ * Vibrator Vibrators}.
+ *
+ * These effects may be any number of things, from single shot vibrations to complex waveforms.
+ *
+ * @hide
+ * @see VibrationEffect
+ */
+public abstract class CombinedVibrationEffect implements Parcelable {
+ private static final int PARCEL_TOKEN_MONO = 1;
+
+ /** @hide to prevent subclassing from outside of the framework */
+ public CombinedVibrationEffect() {
+ }
+
+ /**
+ * Create a synced vibration effect.
+ *
+ * A synced vibration effect should be performed by multiple vibrators at the same time.
+ *
+ * @param effect The {@link VibrationEffect} to perform
+ * @return The desired combined effect.
+ */
+ @NonNull
+ public static CombinedVibrationEffect createSynced(@NonNull VibrationEffect effect) {
+ CombinedVibrationEffect combined = new Mono(effect);
+ combined.validate();
+ return combined;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ public abstract void validate();
+
+ /**
+ * Represents a single {@link VibrationEffect} that should be executed in all vibrators in sync.
+ *
+ * @hide
+ */
+ public static final class Mono extends CombinedVibrationEffect {
+ private final VibrationEffect mEffect;
+
+ public Mono(Parcel in) {
+ mEffect = VibrationEffect.CREATOR.createFromParcel(in);
+ }
+
+ public Mono(@NonNull VibrationEffect effect) {
+ mEffect = effect;
+ }
+
+ public VibrationEffect getEffect() {
+ return mEffect;
+ }
+
+ /** @hide */
+ @Override
+ public void validate() {
+ mEffect.validate();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof CombinedVibrationEffect.Mono)) {
+ return false;
+ }
+ CombinedVibrationEffect.Mono other = (CombinedVibrationEffect.Mono) o;
+ return other.mEffect.equals(other.mEffect);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEffect);
+ }
+
+ @Override
+ public String toString() {
+ return "Mono{mEffect=" + mEffect + '}';
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(PARCEL_TOKEN_MONO);
+ mEffect.writeToParcel(out, flags);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<Mono> CREATOR =
+ new Parcelable.Creator<Mono>() {
+ @Override
+ public Mono createFromParcel(@NonNull Parcel in) {
+ // Skip the type token
+ in.readInt();
+ return new Mono(in);
+ }
+
+ @Override
+ @NonNull
+ public Mono[] newArray(int size) {
+ return new Mono[size];
+ }
+ };
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<CombinedVibrationEffect> CREATOR =
+ new Parcelable.Creator<CombinedVibrationEffect>() {
+ @Override
+ public CombinedVibrationEffect createFromParcel(Parcel in) {
+ int token = in.readInt();
+ if (token == PARCEL_TOKEN_MONO) {
+ return new CombinedVibrationEffect.Mono(in);
+ } else {
+ throw new IllegalStateException(
+ "Unexpected combined vibration event type token in parcel.");
+ }
+ }
+
+ @Override
+ public CombinedVibrationEffect[] newArray(int size) {
+ return new CombinedVibrationEffect[size];
+ }
+ };
+}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index a2e53e29193c..4fed93204f59 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2468,7 +2468,7 @@ public final class Debug
@UnsupportedAppUsage
public static String getCallers(final int depth) {
final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
for (int i = 0; i < depth; i++) {
sb.append(getCaller(callStack, i)).append(" ");
}
@@ -2483,7 +2483,7 @@ public final class Debug
*/
public static String getCallers(final int start, int depth) {
final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
depth += start;
for (int i = start; i < depth; i++) {
sb.append(getCaller(callStack, i)).append(" ");
@@ -2501,7 +2501,7 @@ public final class Debug
*/
public static String getCallers(final int depth, String linePrefix) {
final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
for (int i = 0; i < depth; i++) {
sb.append(linePrefix).append(getCaller(callStack, i)).append("\n");
}
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index ca303d973235..25bffbc9e8d5 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
+import android.util.SparseArray;
import java.io.File;
import java.lang.annotation.Retention;
@@ -101,7 +102,9 @@ public abstract class FileObserver {
private static final String LOG_TAG = "FileObserver";
private static class ObserverThread extends Thread {
+ /** Temporarily retained; appears to be missing UnsupportedAppUsage annotation */
private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
+ private SparseArray<WeakReference> mRealObservers = new SparseArray<>();
private int m_fd;
public ObserverThread() {
@@ -127,10 +130,10 @@ public abstract class FileObserver {
final WeakReference<FileObserver> fileObserverWeakReference =
new WeakReference<>(observer);
- synchronized (m_observers) {
+ synchronized (mRealObservers) {
for (int wfd : wfds) {
if (wfd >= 0) {
- m_observers.put(wfd, fileObserverWeakReference);
+ mRealObservers.put(wfd, fileObserverWeakReference);
}
}
}
@@ -147,12 +150,12 @@ public abstract class FileObserver {
// look up our observer, fixing up the map if necessary...
FileObserver observer = null;
- synchronized (m_observers) {
- WeakReference weak = m_observers.get(wfd);
+ synchronized (mRealObservers) {
+ WeakReference weak = mRealObservers.get(wfd);
if (weak != null) { // can happen with lots of events from a dead wfd
observer = (FileObserver) weak.get();
if (observer == null) {
- m_observers.remove(wfd);
+ mRealObservers.remove(wfd);
}
}
}
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index e821e3194d21..08d201977c49 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -16,9 +16,13 @@
package android.os;
+import android.os.CombinedVibrationEffect;
import android.os.VibrationAttributes;
/** {@hide} */
interface IVibratorManagerService {
int[] getVibratorIds();
+ void vibrate(int uid, String opPkg, in CombinedVibrationEffect effect,
+ in VibrationAttributes attributes, String reason, IBinder token);
+ void cancelVibrate(IBinder token);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 13d5f6a9c9d7..765ef48308ae 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2010,13 +2010,13 @@ public final class Parcel {
* A map used by {@link #readSquashed} to cache parcelables. It's a map from
* an absolute position in a Parcel to the parcelable stored at the position.
*/
- private ArrayMap<Integer, Parcelable> mReadSquashableParcelables;
+ private SparseArray<Parcelable> mReadSquashableParcelables;
private void ensureReadSquashableParcelables() {
if (mReadSquashableParcelables != null) {
return;
}
- mReadSquashableParcelables = new ArrayMap<>();
+ mReadSquashableParcelables = new SparseArray<>();
}
/**
@@ -2112,9 +2112,13 @@ public final class Parcel {
final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos);
if (p == null) {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mReadSquashableParcelables.size(); i++) {
+ sb.append(mReadSquashableParcelables.keyAt(i)).append(' ');
+ }
Slog.wtfStack(TAG, "Map doesn't contain offset "
+ firstAbsolutePos
- + " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet()));
+ + " : contains=" + sb.toString());
}
return (T) p;
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 000b23f5d5ca..e736e30b51e5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1999,7 +1999,7 @@ public final class PowerManager {
Preconditions.checkNotNull(listener, "listener cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkArgument(!mListenerMap.containsKey(listener),
- "Listener already registered: " + listener);
+ "Listener already registered: %s", listener);
IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
@Override
public void onStatusChange(int status) {
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 6c5b04a649e2..0fba8950cf15 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -60,6 +60,7 @@ import android.util.Log;
import android.util.Printer;
import android.util.Singleton;
import android.util.Slog;
+import android.util.SparseLongArray;
import android.view.IWindowManager;
import com.android.internal.annotations.GuardedBy;
@@ -1525,7 +1526,9 @@ public final class StrictMode {
// Map from violation stacktrace hashcode -> uptimeMillis of
// last violation. No locking needed, as this is only
// accessed by the same thread.
+ /** Temporarily retained; appears to be missing UnsupportedAppUsage annotation */
private ArrayMap<Integer, Long> mLastViolationTime;
+ private SparseLongArray mRealLastViolationTime;
public AndroidBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
mThreadPolicyMask = threadPolicyMask;
@@ -1759,17 +1762,17 @@ public final class StrictMode {
long lastViolationTime = 0;
long now = SystemClock.uptimeMillis();
if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
- if (mLastViolationTime != null) {
- Long vtime = mLastViolationTime.get(crashFingerprint);
+ if (mRealLastViolationTime != null) {
+ Long vtime = mRealLastViolationTime.get(crashFingerprint);
if (vtime != null) {
lastViolationTime = vtime;
}
- clampViolationTimeMap(mLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
+ clampViolationTimeMap(mRealLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS)));
} else {
- mLastViolationTime = new ArrayMap<>(1);
+ mRealLastViolationTime = new SparseLongArray(1);
}
- mLastViolationTime.put(crashFingerprint, now);
+ mRealLastViolationTime.put(crashFingerprint, now);
}
long timeSinceLastViolationMillis =
lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);
@@ -2165,16 +2168,17 @@ public final class StrictMode {
}
final int uid = android.os.Process.myUid();
- String msg = "Detected cleartext network traffic from UID " + uid;
+ final StringBuilder msg = new StringBuilder("Detected cleartext network traffic from UID ")
+ .append(uid);
if (rawAddr != null) {
try {
- msg += " to " + InetAddress.getByAddress(rawAddr);
+ msg.append(" to ").append(InetAddress.getByAddress(rawAddr));
} catch (UnknownHostException ignored) {
}
}
- msg += HexDump.dumpHexString(firstPacket).trim() + " ";
+ msg.append(HexDump.dumpHexString(firstPacket).trim()).append(' ');
final boolean forceDeath = (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0;
- onVmPolicyViolation(new CleartextNetworkViolation(msg), forceDeath);
+ onVmPolicyViolation(new CleartextNetworkViolation(msg.toString()), forceDeath);
}
/** @hide */
@@ -2231,18 +2235,19 @@ public final class StrictMode {
// Map from VM violation fingerprint to uptime millis.
@UnsupportedAppUsage
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();
+ private static final SparseLongArray sRealLastVmViolationTime = new SparseLongArray();
/**
* Clamp the given map by removing elements with timestamp older than the given retainSince.
*/
- private static void clampViolationTimeMap(final @NonNull Map<Integer, Long> violationTime,
+ private static void clampViolationTimeMap(final @NonNull SparseLongArray violationTime,
final long retainSince) {
- final Iterator<Map.Entry<Integer, Long>> iterator = violationTime.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<Integer, Long> e = iterator.next();
- if (e.getValue() < retainSince) {
+ for (int i = 0; i < violationTime.size(); ) {
+ if (violationTime.valueAt(i) < retainSince) {
// Remove stale entries
- iterator.remove();
+ violationTime.removeAt(i);
+ } else {
+ i++;
}
}
// Ideally we'd cap the total size of the map, though it'll involve quickselect of topK,
@@ -2273,15 +2278,15 @@ public final class StrictMode {
long lastViolationTime;
long timeSinceLastViolationMillis = Long.MAX_VALUE;
if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
- synchronized (sLastVmViolationTime) {
- if (sLastVmViolationTime.containsKey(fingerprint)) {
- lastViolationTime = sLastVmViolationTime.get(fingerprint);
+ synchronized (sRealLastVmViolationTime) {
+ if (sRealLastVmViolationTime.indexOfKey(fingerprint) >= 0) {
+ lastViolationTime = sRealLastVmViolationTime.get(fingerprint);
timeSinceLastViolationMillis = now - lastViolationTime;
}
if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
- sLastVmViolationTime.put(fingerprint, now);
+ sRealLastVmViolationTime.put(fingerprint, now);
}
- clampViolationTimeMap(sLastVmViolationTime,
+ clampViolationTimeMap(sRealLastVmViolationTime,
now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS));
}
}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 487e46886914..21ad38b0e371 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -983,6 +983,8 @@ public abstract class VibrationEffect implements Parcelable {
Composition.checkPrimitive(effect.id);
Preconditions.checkArgumentInRange(
effect.scale, 0.0f, 1.0f, "scale");
+ Preconditions.checkArgumentNonNegative(effect.delay,
+ "Primitive delay must be zero or positive");
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index c319e8580f76..e4220dd00a5b 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -16,6 +16,8 @@
package android.permission;
+import static android.os.Build.VERSION_CODES.S;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
@@ -29,6 +31,8 @@ import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.IActivityManager;
import android.app.PropertyInvalidatedCache;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -68,6 +72,17 @@ public final class PermissionManager {
public static final String KILL_APP_REASON_GIDS_CHANGED =
"permission grant or revoke changed gids";
+ /**
+ * Refuse to install package if groups of permissions are bad
+ * - Permission groups should only be shared between apps sharing a certificate
+ * - If a permission belongs to a group that group should be defined
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = S)
+ public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400;
+
private final @NonNull Context mContext;
private final IPackageManager mPackageManager;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0e3708e04dd9..59934acdc2f0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10434,14 +10434,6 @@ public final class Settings {
"webview_data_reduction_proxy_key";
/**
- * Whether or not the WebView fallback mechanism should be enabled.
- * 0=disabled, 1=enabled.
- * @hide
- */
- public static final String WEBVIEW_FALLBACK_LOGIC_ENABLED =
- "webview_fallback_logic_enabled";
-
- /**
* Name of the package used as WebView provider (if unset the provider is instead determined
* by the system).
* @hide
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index e0b1c2f91ed2..d15514dfb899 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -117,7 +117,7 @@ public final class BatchUpdates implements Parcelable {
public Builder transformChild(int id, @NonNull Transformation transformation) {
throwIfDestroyed();
Preconditions.checkArgument((transformation instanceof InternalTransformation),
- "not provided by Android System: " + transformation);
+ "not provided by Android System: %s", transformation);
if (mTransformations == null) {
mTransformations = new ArrayList<>();
}
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index e274460cdf03..6df01545c09f 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -180,7 +180,7 @@ public final class CustomDescription implements Parcelable {
public Builder addChild(int id, @NonNull Transformation transformation) {
throwIfDestroyed();
Preconditions.checkArgument((transformation instanceof InternalTransformation),
- "not provided by Android System: " + transformation);
+ "not provided by Android System: %s", transformation);
if (mTransformations == null) {
mTransformations = new ArrayList<>();
}
@@ -275,7 +275,7 @@ public final class CustomDescription implements Parcelable {
public Builder batchUpdate(@NonNull Validator condition, @NonNull BatchUpdates updates) {
throwIfDestroyed();
Preconditions.checkArgument((condition instanceof InternalValidator),
- "not provided by Android System: " + condition);
+ "not provided by Android System: %s", condition);
Preconditions.checkNotNull(updates);
if (mUpdates == null) {
mUpdates = new ArrayList<>();
@@ -329,7 +329,7 @@ public final class CustomDescription implements Parcelable {
public Builder addOnClickAction(int id, @NonNull OnClickAction action) {
throwIfDestroyed();
Preconditions.checkArgument((action instanceof InternalOnClickAction),
- "not provided by Android System: " + action);
+ "not provided by Android System: %s", action);
if (mActions == null) {
mActions = new SparseArray<InternalOnClickAction>();
}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index e640eecc03c0..619bfa225cf1 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -687,7 +687,7 @@ public final class SaveInfo implements Parcelable {
public @NonNull Builder setValidator(@NonNull Validator validator) {
throwIfDestroyed();
Preconditions.checkArgument((validator instanceof InternalValidator),
- "not provided by Android System: " + validator);
+ "not provided by Android System: %s", validator);
mValidator = (InternalValidator) validator;
return this;
}
@@ -734,7 +734,7 @@ public final class SaveInfo implements Parcelable {
throwIfDestroyed();
Preconditions.checkArgument(!ArrayUtils.isEmpty(ids), "ids cannot be empty or null");
Preconditions.checkArgument((sanitizer instanceof InternalSanitizer),
- "not provided by Android System: " + sanitizer);
+ "not provided by Android System: %s", sanitizer);
if (mSanitizers == null) {
mSanitizers = new ArrayMap<>();
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 7814f70b612b..eaffc92c1d6b 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -344,11 +344,11 @@ public final class UserData implements FieldClassificationUserData, Parcelable {
if (!mUniqueCategoryIds.contains(categoryId)) {
// New category - check size
Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(),
- "already added " + mUniqueCategoryIds.size() + " unique category ids");
+ "already added %d unique category ids", mUniqueCategoryIds.size());
}
Preconditions.checkState(mValues.size() < getMaxUserDataSize(),
- "already added " + mValues.size() + " elements");
+ "already added %d elements", mValues.size());
addMapping(value, categoryId);
return this;
diff --git a/core/java/android/service/autofill/Validators.java b/core/java/android/service/autofill/Validators.java
index 0f1ba9891a99..0a2115848bd5 100644
--- a/core/java/android/service/autofill/Validators.java
+++ b/core/java/android/service/autofill/Validators.java
@@ -67,7 +67,7 @@ public final class Validators {
@NonNull
public static Validator not(@NonNull Validator validator) {
Preconditions.checkArgument(validator instanceof InternalValidator,
- "validator not provided by Android System: " + validator);
+ "validator not provided by Android System: %s", validator);
return new NegationValidator((InternalValidator) validator);
}
@@ -78,7 +78,7 @@ public final class Validators {
for (int i = 0; i < validators.length; i++) {
Preconditions.checkArgument((validators[i] instanceof InternalValidator),
- "element " + i + " not provided by Android System: " + validators[i]);
+ "element %d not provided by Android System: %s", i, validators[i]);
internals[i] = (InternalValidator) validators[i];
}
return internals;
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 08d990581390..579a8bfc9d99 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -16,7 +16,7 @@
package android.service.notification;
-import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.text.TextUtils.formatSimple;
import android.annotation.NonNull;
import android.app.Notification;
@@ -31,8 +31,6 @@ import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.nano.MetricsProto;
@@ -258,7 +256,7 @@ public class StatusBarNotification implements Parcelable {
@Override
public String toString() {
- return String.format(
+ return formatSimple(
"StatusBarNotification(pkg=%s user=%s id=%d tag=%s key=%s: %s)",
this.pkg, this.user, this.id, this.tag,
this.key, this.notification);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 6a70a856e09a..4249e5c20d3b 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -20,6 +20,7 @@ import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MSCALE_Y;
import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
+import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import android.annotation.FloatRange;
import android.annotation.Nullable;
@@ -33,6 +34,7 @@ import android.app.WallpaperManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -878,7 +880,6 @@ public abstract class WallpaperService extends Service {
if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame,
- mWinFrames.contentInsets, mWinFrames.stableInsets,
mWinFrames.displayCutout, inputChannel, mInsetsState,
mTempControls) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
@@ -914,20 +915,22 @@ public abstract class WallpaperService extends Service {
int w = mWinFrames.frame.width();
int h = mWinFrames.frame.height();
+ final DisplayCutout rawCutout = mWinFrames.displayCutout.get();
+ final Configuration config = getResources().getConfiguration();
+ final Rect visibleFrame = new Rect(mWinFrames.frame);
+ visibleFrame.intersect(mInsetsState.getDisplayFrame());
+ WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame,
+ null /* ignoringVisibilityState */, config.isScreenRound(),
+ false /* alwaysConsumeSystemBars */, rawCutout, mLayout.softInputMode,
+ mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
+ config.windowConfiguration.getWindowingMode(), null /* typeSideMap */);
+
if (!fixedSize) {
final Rect padding = mIWallpaperEngine.mDisplayPadding;
w += padding.left + padding.right;
h += padding.top + padding.bottom;
- mWinFrames.contentInsets.left += padding.left;
- mWinFrames.contentInsets.top += padding.top;
- mWinFrames.contentInsets.right += padding.right;
- mWinFrames.contentInsets.bottom += padding.bottom;
- mWinFrames.stableInsets.left += padding.left;
- mWinFrames.stableInsets.top += padding.top;
- mWinFrames.stableInsets.right += padding.right;
- mWinFrames.stableInsets.bottom += padding.bottom;
- mWinFrames.displayCutout.set(mWinFrames.displayCutout.get().inset(
- -padding.left, -padding.top, -padding.right, -padding.bottom));
+ windowInsets = windowInsets.insetUnchecked(
+ -padding.left, -padding.top, -padding.right, -padding.bottom);
} else {
w = myWidth;
h = myHeight;
@@ -946,9 +949,12 @@ public abstract class WallpaperService extends Service {
Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight);
}
- final DisplayCutout displayCutout = mWinFrames.displayCutout.get();
- insetsChanged |= !mDispatchedContentInsets.equals(mWinFrames.contentInsets);
- insetsChanged |= !mDispatchedStableInsets.equals(mWinFrames.stableInsets);
+ final Rect contentInsets = windowInsets.getSystemWindowInsets().toRect();
+ final Rect stableInsets = windowInsets.getStableInsets().toRect();
+ final DisplayCutout displayCutout = windowInsets.getDisplayCutout() != null
+ ? windowInsets.getDisplayCutout() : rawCutout;
+ insetsChanged |= !mDispatchedContentInsets.equals(contentInsets);
+ insetsChanged |= !mDispatchedStableInsets.equals(stableInsets);
insetsChanged |= !mDispatchedDisplayCutout.equals(displayCutout);
mSurfaceHolder.setSurfaceFrameSize(w, h);
@@ -1008,18 +1014,13 @@ public abstract class WallpaperService extends Service {
}
if (insetsChanged) {
- mDispatchedContentInsets.set(mWinFrames.contentInsets);
- mDispatchedStableInsets.set(mWinFrames.stableInsets);
+ mDispatchedContentInsets.set(contentInsets);
+ mDispatchedStableInsets.set(stableInsets);
mDispatchedDisplayCutout = displayCutout;
- mFinalStableInsets.set(mDispatchedStableInsets);
- WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- mFinalStableInsets,
- getResources().getConfiguration().isScreenRound(), false,
- mDispatchedDisplayCutout);
if (DEBUG) {
- Log.v(TAG, "dispatching insets=" + insets);
+ Log.v(TAG, "dispatching insets=" + windowInsets);
}
- onApplyWindowInsets(insets);
+ onApplyWindowInsets(windowInsets);
}
if (redrawNeeded) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 4471056e23dd..6318c4757bce 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -931,7 +931,8 @@ public class TextLine {
float totalWidth = 0;
final int numDecorations = decorations == null ? 0 : decorations.size();
- if (needWidth || (c != null && (wp.bgColor != 0 || numDecorations != 0 || runIsRtl))) {
+ if (needWidth || ((c != null || consumer != null) && (wp.bgColor != 0
+ || numDecorations != 0 || runIsRtl))) {
totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
}
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
index dd2570401b3e..02fd7b4470f0 100644
--- a/core/java/android/text/TextShaper.java
+++ b/core/java/android/text/TextShaper.java
@@ -198,6 +198,10 @@ public class TextShaper {
/**
* Shape multi-styled text.
*
+ * In the LTR context, the shape result will go from left to right, thus you may want to draw
+ * glyphs from left most position of the canvas. In the RTL context, the shape result will go
+ * from right to left, thus you may want to draw glyphs from right most position of the canvas.
+ *
* @param text a styled text.
* @param start a start index of shaping target in the text.
* @param count a length of shaping target in the text.
@@ -215,7 +219,7 @@ public class TextShaper {
try {
tl.set(paint, text, start, start + count,
mp.getParagraphDir(),
- mp.getDirections(start, start + count),
+ mp.getDirections(0, count),
false /* tabstop is not supported */,
null,
-1, -1 // ellipsis is not supported.
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 72b35b9253eb..d0fd2b393633 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -2095,6 +2095,9 @@ public class TextUtils {
* <li>{@code %s} for {@code String}
* <li>{@code %x} for hex representation of {@code int} or {@code long}
* <li>{@code %%} for literal {@code %}
+ * <li>{@code %04d} style grammar to specify the argument width, such as
+ * {@code %04d} to prefix an {@code int} with zeros or {@code %10b} to
+ * prefix a {@code boolean} with spaces
* </ul>
*
* @throws IllegalArgumentException if the format string or arguments don't
@@ -2106,8 +2109,23 @@ public class TextUtils {
int j = 0;
for (int i = 0; i < sb.length(); ) {
if (sb.charAt(i) == '%') {
+ char code = sb.charAt(i + 1);
+
+ // Decode any argument width request
+ char prefixChar = '\0';
+ int prefixLen = 0;
+ int consume = 2;
+ while ('0' <= code && code <= '9') {
+ if (prefixChar == '\0') {
+ prefixChar = (code == '0') ? '0' : ' ';
+ }
+ prefixLen *= 10;
+ prefixLen += Character.digit(code, 10);
+ consume += 1;
+ code = sb.charAt(i + consume - 1);
+ }
+
final String repl;
- final char code = sb.charAt(i + 1);
switch (code) {
case 'b': {
if (j == args.length) {
@@ -2155,8 +2173,15 @@ public class TextUtils {
throw new IllegalArgumentException("Unsupported format code " + code);
}
}
- sb.replace(i, i + 2, repl);
- i += repl.length();
+
+ sb.replace(i, i + consume, repl);
+
+ // Apply any argument width request
+ final int prefixInsert = (prefixChar == '0' && repl.charAt(0) == '-') ? 1 : 0;
+ for (int k = repl.length(); k < prefixLen; k++) {
+ sb.insert(i + prefixInsert, prefixChar);
+ }
+ i += Math.max(repl.length(), prefixLen);
} else {
i++;
}
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 4a0bec1300b7..c2e3a80a5c51 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -29,6 +29,7 @@ import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
+import android.text.TextUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -697,7 +698,7 @@ public class DateFormat {
}
private static String zeroPad(int inValue, int inMinDigits) {
- return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue);
+ return TextUtils.formatSimple("%0" + inMinDigits + "d", inValue);
}
/**
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 610cf2c7c784..ae565d1c3317 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -89,7 +89,7 @@ public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
* Set the line height of the paragraph to <code>height</code> physical pixels.
*/
public Standard(@Px @IntRange(from = 1) int height) {
- Preconditions.checkArgument(height > 0, "Height:" + height + "must be positive");
+ Preconditions.checkArgument(height > 0, "Height: %d must be positive", height);
mHeight = height;
}
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index fda5e0d008e6..bf9a8384fe2c 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -60,10 +60,9 @@ public final class LocalLog {
}
final String logLine;
if (mUseLocalTimestamps) {
- logLine = String.format("%s - %s", LocalDateTime.now(), msg);
+ logLine = LocalDateTime.now() + " - " + msg;
} else {
- logLine = String.format(
- "%s / %s - %s", SystemClock.elapsedRealtime(), Instant.now(), msg);
+ logLine = SystemClock.elapsedRealtime() + " / " + Instant.now() + " - " + msg;
}
append(logLine);
}
diff --git a/core/java/android/util/proto/EncodedBuffer.java b/core/java/android/util/proto/EncodedBuffer.java
index 56a0bfa2adb1..2a8f405ffd7f 100644
--- a/core/java/android/util/proto/EncodedBuffer.java
+++ b/core/java/android/util/proto/EncodedBuffer.java
@@ -648,7 +648,7 @@ public final class EncodedBuffer {
* Print the internal buffer chunks.
*/
private static int dumpByteString(String tag, String prefix, int start, byte[] buf) {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
final int length = buf.length;
final int lineLen = 16;
int i;
@@ -656,7 +656,7 @@ public final class EncodedBuffer {
if (i % lineLen == 0) {
if (i != 0) {
Log.d(tag, sb.toString());
- sb = new StringBuffer();
+ sb = new StringBuilder();
}
sb.append(prefix);
sb.append('[');
diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
index aa70d07ff787..9789b10a0a61 100644
--- a/core/java/android/util/proto/ProtoInputStream.java
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -16,10 +16,11 @@
package android.util.proto;
+import android.util.LongArray;
+
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
/**
* Class to read to a protobuf stream.
@@ -98,7 +99,7 @@ public final class ProtoInputStream extends ProtoStream {
/**
* Keeps track of the currently read nested Objects, for end object checking and debug
*/
- private ArrayList<Long> mExpectedObjectTokenStack = null;
+ private LongArray mExpectedObjectTokenStack = null;
/**
* Current nesting depth of start calls.
@@ -498,7 +499,7 @@ public final class ProtoInputStream extends ProtoStream {
int messageSize = (int) readVarint();
if (mExpectedObjectTokenStack == null) {
- mExpectedObjectTokenStack = new ArrayList<>();
+ mExpectedObjectTokenStack = new LongArray();
}
if (++mDepth == mExpectedObjectTokenStack.size()) {
// Create a token to keep track of nested Object and extend the object stack
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 3c5d336b840d..f572eb91ef34 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -729,7 +729,6 @@ interface IWindowManager
* @return {@code true} if system bars are always comsumed.
*/
boolean getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
- out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper outDisplayCutout, out InsetsState outInsetsState);
/**
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7f36169ada50..9febc9f09ef3 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -46,18 +46,16 @@ import java.util.List;
interface IWindowSession {
int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
- out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
+ out Rect outFrame, out DisplayCutout.ParcelableWrapper displayCutout,
+ out InputChannel outInputChannel, out InsetsState insetsState,
+ out InsetsSourceControl[] activeControls);
+ int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
+ in int viewVisibility, in int layerStackId, in int userId,
+ in InsetsState requestedVisibility, out Rect outFrame,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
- int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
- in int viewVisibility, in int layerStackId, in int userId,
- in InsetsState requestedVisibility,
- out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
- out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
- out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
- in int viewVisibility, in int layerStackId, out Rect outContentInsets,
- out Rect outStableInsets, out InsetsState insetsState);
+ in int viewVisibility, in int layerStackId, out InsetsState insetsState);
@UnsupportedAppUsage
void remove(IWindow window);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 878583be87c9..06ddf3c69f8e 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -38,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import android.annotation.Nullable;
+import android.content.res.CompatibilityInfo;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -90,6 +91,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
private final WindowInsetsAnimation mAnimation;
/** @see WindowInsetsAnimationController#hasZeroInsetsIme */
private final boolean mHasZeroInsetsIme;
+ private final CompatibilityInfo.Translator mTranslator;
private Insets mCurrentInsets;
private Insets mPendingInsets;
private float mPendingFraction;
@@ -107,7 +109,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
- @AnimationType int animationType) {
+ @AnimationType int animationType, CompatibilityInfo.Translator translator) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -131,6 +133,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
durationMs);
mAnimation.setAlpha(getCurrentAlpha());
mAnimationType = animationType;
+ mTranslator = translator;
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets));
@@ -396,21 +399,23 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
private void addTranslationToMatrix(@InternalInsetsSide int side, int inset, Matrix m,
Rect frame) {
+ final float surfaceOffset = mTranslator != null
+ ? mTranslator.translateLengthInAppWindowToScreen(inset) : inset;
switch (side) {
case ISIDE_LEFT:
- m.postTranslate(-inset, 0);
+ m.postTranslate(-surfaceOffset, 0);
frame.offset(-inset, 0);
break;
case ISIDE_TOP:
- m.postTranslate(0, -inset);
+ m.postTranslate(0, -surfaceOffset);
frame.offset(0, -inset);
break;
case ISIDE_RIGHT:
- m.postTranslate(inset, 0);
+ m.postTranslate(surfaceOffset, 0);
frame.offset(inset, 0);
break;
case ISIDE_BOTTOM:
- m.postTranslate(0, inset);
+ m.postTranslate(0, surfaceOffset);
frame.offset(0, inset);
break;
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 1307052a25cc..4a5fa0f971ae 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -20,6 +20,7 @@ import static android.view.InsetsController.DEBUG;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
import android.annotation.UiThread;
+import android.content.res.CompatibilityInfo;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Trace;
@@ -102,11 +103,12 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
- @AnimationType int animationType, Handler mainThreadHandler) {
+ @AnimationType int animationType, CompatibilityInfo.Translator translator,
+ Handler mainThreadHandler) {
mMainThreadHandler = mainThreadHandler;
mOuterCallbacks = controller;
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
- types, mCallbacks, durationMs, interpolator, animationType);
+ types, mCallbacks, durationMs, interpolator, animationType, translator);
InsetsAnimationThread.getHandler().post(() -> {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5037d9e5bfe8..b5bf08443a6c 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -33,6 +33,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.CompatibilityInfo;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.CancellationSignal;
@@ -176,6 +177,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
*/
@Nullable
IBinder getWindowToken();
+
+ /**
+ * @return Translator associated with the host, if it has one.
+ */
+ @Nullable
+ default CompatibilityInfo.Translator getTranslator() {
+ return null;
+ }
}
private static final String TAG = "InsetsController";
@@ -994,10 +1003,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final InsetsAnimationControlRunner runner = useInsetsAnimationThread
? new InsetsAnimationThreadControlRunner(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator,
- animationType, mHost.getHandler())
+ animationType, mHost.getTranslator(), mHost.getHandler())
: new InsetsAnimationControlImpl(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator,
- animationType);
+ animationType, mHost.getTranslator());
mRunningAnimations.add(new RunningAnimation(runner, animationType));
if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
+ useInsetsAnimationThread);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index b9f1f6a43992..ac29f2eb5c0b 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -427,6 +427,25 @@ public class InsetsState implements Parcelable {
}
}
+ /**
+ * Scales the frame and the visible frame (if there is one) of each source.
+ *
+ * @param scale the scale to be applied
+ */
+ public void scale(float scale) {
+ mDisplayFrame.scale(scale);
+ for (int i = 0; i < SIZE; i++) {
+ final InsetsSource source = mSources[i];
+ if (source != null) {
+ source.getFrame().scale(scale);
+ final Rect visibleFrame = source.getVisibleFrame();
+ if (visibleFrame != null) {
+ visibleFrame.scale(scale);
+ }
+ }
+ }
+ }
+
public void set(InsetsState other) {
set(other, false /* copySources */);
}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 546e26a85d6e..355b314a0b8f 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,7 +17,6 @@
package android.view;
import android.annotation.Nullable;
-import android.app.Notification;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
@@ -42,7 +41,6 @@ import java.util.ArrayList;
*/
@RemoteViews.RemoteView
public class NotificationHeaderView extends ViewGroup {
- public static final int NO_COLOR = Notification.COLOR_INVALID;
private final int mChildMinWidth;
private final int mContentEndMargin;
private final int mGravity;
@@ -56,7 +54,6 @@ public class NotificationHeaderView extends ViewGroup {
private CachingIconView mIcon;
private View mProfileBadge;
private View mFeedbackIcon;
- private boolean mExpanded;
private boolean mShowExpandButtonAtEnd;
private boolean mShowWorkBadgeAtEnd;
private int mHeaderTextMarginEnd;
@@ -295,35 +292,6 @@ public class NotificationHeaderView extends ViewGroup {
updateTouchListener();
}
- public int getOriginalIconColor() {
- return mIcon.getOriginalIconColor();
- }
-
- public int getOriginalNotificationColor() {
- return mExpandButton.getOriginalNotificationColor();
- }
-
- @RemotableViewMethod
- public void setExpanded(boolean expanded) {
- mExpanded = expanded;
- updateExpandButton();
- }
-
- private void updateExpandButton() {
- int drawableId;
- int contentDescriptionId;
- if (mExpanded) {
- drawableId = R.drawable.ic_collapse_notification;
- contentDescriptionId = R.string.expand_button_content_description_expanded;
- } else {
- drawableId = R.drawable.ic_expand_notification;
- contentDescriptionId = R.string.expand_button_content_description_collapsed;
- }
- mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
- mExpandButton.setColorFilter(getOriginalNotificationColor());
- mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
- }
-
public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
setClipToPadding(!showWorkBadgeAtEnd);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 432d9279c48d..14748f03e934 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1207,7 +1207,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// Therefore, we must explicitly recreate the {@link Surface} in these
// cases.
if (mUseBlastAdapter) {
- mSurface.transferFrom(mBlastBufferQueue.createSurface());
+ mSurface.transferFrom(mBlastBufferQueue.createSurfaceWithHandle());
} else {
mSurface.createFrom(mSurfaceControl);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cf5ca56eb188..667f0b9511d0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6150,6 +6150,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* was set.
*/
@NonNull
+ @SuppressWarnings("AndroidFrameworkEfficientCollections")
public Map<Integer, Integer> getAttributeSourceResourceMap() {
HashMap<Integer, Integer> map = new HashMap<>();
if (!sDebugViewAttributes || mAttributeSourceResId == null) {
@@ -14861,15 +14862,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public void getWindowVisibleDisplayFrame(Rect outRect) {
if (mAttachInfo != null) {
- mAttachInfo.mViewRootImpl.getDisplayFrame(outRect);
- // XXX This is really broken, and probably all needs to be done
- // in the window manager, and we need to know more about whether
- // we want the area behind or in front of the IME.
- final Rect insets = mAttachInfo.mVisibleInsets;
- outRect.left += insets.left;
- outRect.top += insets.top;
- outRect.right -= insets.right;
- outRect.bottom -= insets.bottom;
+ mAttachInfo.mViewRootImpl.getWindowVisibleDisplayFrame(outRect);
return;
}
// The view is not attached to a display so we don't have a context.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index fd7c2d896b09..eb6c49549100 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -50,6 +50,7 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pools;
import android.util.Pools.SynchronizedPool;
@@ -611,7 +612,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private int mNestedScrollAxes;
// Used to manage the list of transient views, added by addTransientView()
- private List<Integer> mTransientIndices = null;
+ private IntArray mTransientIndices = null;
private List<View> mTransientViews = null;
/**
@@ -4853,7 +4854,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
if (mTransientIndices == null) {
- mTransientIndices = new ArrayList<Integer>();
+ mTransientIndices = new IntArray();
mTransientViews = new ArrayList<View>();
}
final int oldSize = mTransientIndices.size();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f0203011b4f2..9bc07702feee 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -562,6 +562,9 @@ public final class ViewRootImpl implements ViewParent,
= new ViewTreeObserver.InternalInsetsInfo();
private WindowInsets mLastWindowInsets;
+ private final Rect mSystemInsetsCache = new Rect();
+ private final Rect mVisibleInsetsCache = new Rect();
+ private final Rect mStableInsetsCache = new Rect();
// Insets types hidden by legacy window flags or system UI flags.
private @InsetsType int mTypesHiddenByFlags = 0;
@@ -1025,9 +1028,12 @@ public final class ViewRootImpl implements ViewParent,
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibility(), mTmpFrames.frame,
- mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+ mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
+ }
setFrame(mTmpFrames.frame);
} catch (RemoteException e) {
mAdded = false;
@@ -1044,9 +1050,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
- }
mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
mAttachInfo.mAlwaysConsumeSystemBars =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
@@ -2313,12 +2316,10 @@ public final class ViewRootImpl implements ViewParent,
(mWindowAttributes.systemUiVisibility
| mWindowAttributes.subtreeSystemUiVisibility));
- Rect visibleInsets = mInsetsController.calculateVisibleInsets(
- mWindowAttributes.softInputMode);
-
- mAttachInfo.mVisibleInsets.set(visibleInsets);
- mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
- mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
+ mSystemInsetsCache.set(mLastWindowInsets.getSystemWindowInsets().toRect());
+ mStableInsetsCache.set(mLastWindowInsets.getStableInsets().toRect());
+ mVisibleInsetsCache.set(mInsetsController.calculateVisibleInsets(
+ mWindowAttributes.softInputMode));
}
return mLastWindowInsets;
}
@@ -2826,8 +2827,7 @@ public final class ViewRootImpl implements ViewParent,
&& mWinFrame.height() == mPendingBackDropFrame.height();
// TODO: Need cutout?
startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame,
- mLastWindowInsets.getSystemWindowInsets().toRect(),
- mLastWindowInsets.getStableInsets().toRect(), mResizeMode);
+ mSystemInsetsCache, mStableInsetsCache, mResizeMode);
} else {
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
@@ -3234,9 +3234,6 @@ public final class ViewRootImpl implements ViewParent,
final boolean windowMoved = mAttachInfo.mWindowLeft != frame.left
|| mAttachInfo.mWindowTop != frame.top;
if (windowMoved) {
- if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWinFrame(frame);
- }
mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;
}
@@ -4450,8 +4447,8 @@ public final class ViewRootImpl implements ViewParent,
}
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
- final Rect ci = getWindowInsets(false).getSystemWindowInsetsAsRect();
- final Rect vi = mAttachInfo.mVisibleInsets;
+ final Rect ci = mSystemInsetsCache;
+ final Rect vi = mVisibleInsetsCache;
int scrollY = 0;
boolean handled = false;
@@ -7516,7 +7513,8 @@ public final class ViewRootImpl implements ViewParent,
}
if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWinFrame(mTmpFrames.frame);
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+ mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
}
setFrame(mTmpFrames.frame);
mInsetsController.onStateChanged(mTempInsets);
@@ -7538,6 +7536,22 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Gets the current display size in which the window is being laid out, accounting for screen
+ * decorations around it.
+ */
+ void getWindowVisibleDisplayFrame(Rect outFrame) {
+ outFrame.set(mTmpFrames.displayFrame);
+ // XXX This is really broken, and probably all needs to be done
+ // in the window manager, and we need to know more about whether
+ // we want the area behind or in front of the IME.
+ final Rect insets = mVisibleInsetsCache;
+ outFrame.left += insets.left;
+ outFrame.top += insets.top;
+ outFrame.right -= insets.right;
+ outFrame.bottom -= insets.bottom;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -7856,13 +7870,8 @@ public final class ViewRootImpl implements ViewParent,
MergedConfiguration mergedConfiguration, boolean forceLayout,
boolean alwaysConsumeSystemBars, int displayId) {
final Rect frame = frames.frame;
- final Rect contentInsets = frames.contentInsets;
- final Rect visibleInsets = frames.visibleInsets;
- final Rect stableInsets = frames.stableInsets;
final Rect backDropFrame = frames.backdropFrame;
if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
- + " contentInsets=" + contentInsets.toShortString()
- + " visibleInsets=" + visibleInsets.toShortString()
+ " reportDraw=" + reportDraw
+ " backDropFrame=" + backDropFrame);
@@ -7873,7 +7882,7 @@ public final class ViewRootImpl implements ViewParent,
synchronized (mWindowCallbacks) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen,
- visibleInsets, stableInsets);
+ mVisibleInsetsCache, mStableInsetsCache);
}
}
}
@@ -7881,8 +7890,6 @@ public final class ViewRootImpl implements ViewParent,
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(frame);
- mTranslator.translateRectInScreenToAppWindow(contentInsets);
- mTranslator.translateRectInScreenToAppWindow(visibleInsets);
}
SomeArgs args = SomeArgs.obtain();
final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
@@ -7900,6 +7907,9 @@ public final class ViewRootImpl implements ViewParent,
if (Binder.getCallingPid() == android.os.Process.myPid()) {
insetsState = new InsetsState(insetsState, true /* copySource */);
}
+ if (mTranslator != null) {
+ mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
+ }
mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
}
@@ -7913,6 +7923,9 @@ public final class ViewRootImpl implements ViewParent,
}
}
}
+ if (mTranslator != null) {
+ mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
+ }
SomeArgs args = SomeArgs.obtain();
args.arg1 = insetsState;
args.arg2 = activeControls;
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 8f58df466ee3..514fb29029d0 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CO
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
import android.annotation.NonNull;
+import android.content.res.CompatibilityInfo;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -251,4 +252,12 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
}
return view.getWindowToken();
}
+
+ @Override
+ public CompatibilityInfo.Translator getTranslator() {
+ if (mViewRoot != null) {
+ return mViewRoot.mTranslator;
+ }
+ return null;
+ }
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 94c518483429..8b0cf3bb86dc 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -924,6 +924,15 @@ public final class WindowInsets {
Preconditions.checkArgumentNonnegative(right);
Preconditions.checkArgumentNonnegative(bottom);
+ return insetUnchecked(left, top, right, bottom);
+ }
+
+ /**
+ * @see #inset(int, int, int, int)
+ * @hide
+ */
+ @NonNull
+ public WindowInsets insetUnchecked(int left, int top, int right, int bottom) {
return new WindowInsets(
mSystemWindowInsetsConsumed
? null
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ba3dee42c0b2..9336872f29d8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -272,13 +272,6 @@ public interface WindowManager extends ViewManager {
int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27;
/**
- * A display which can only contain one task is being shown because the first activity is
- * started or it's being turned on.
- * @hide
- */
- int TRANSIT_SHOW_SINGLE_TASK_DISPLAY = 28;
-
- /**
* @hide
*/
@IntDef(prefix = { "TRANSIT_" }, value = {
@@ -303,8 +296,7 @@ public interface WindowManager extends ViewManager {
TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
TRANSIT_CRASHING_ACTIVITY_CLOSE,
- TRANSIT_TASK_CHANGE_WINDOWING_MODE,
- TRANSIT_SHOW_SINGLE_TASK_DISPLAY
+ TRANSIT_TASK_CHANGE_WINDOWING_MODE
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionType {}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 59e022645544..7dfae002b554 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -262,14 +262,11 @@ public final class WindowManagerImpl implements WindowManager {
private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) {
try {
- final Rect systemWindowInsets = new Rect();
- final Rect stableInsets = new Rect();
final DisplayCutout.ParcelableWrapper displayCutout =
new DisplayCutout.ParcelableWrapper();
final InsetsState insetsState = new InsetsState();
final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
- .getWindowInsets(attrs, mContext.getDisplayId(), systemWindowInsets,
- stableInsets, displayCutout, insetsState);
+ .getWindowInsets(attrs, mContext.getDisplayId(), displayCutout, insetsState);
final Configuration config = mContext.getResources().getConfiguration();
final boolean isScreenRound = config.isScreenRound();
final int windowingMode = config.windowConfiguration.getWindowingMode();
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 814787347b75..5e5d14f55240 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -131,7 +131,6 @@ public class WindowlessWindowManager implements IWindowSession {
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
@@ -167,18 +166,16 @@ public class WindowlessWindowManager implements IWindowSession {
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
- Rect outFrame, Rect outContentInsets, Rect outStableInsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
- InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
+ Rect outFrame, DisplayCutout.ParcelableWrapper outDisplayCutout,
+ InputChannel outInputChannel, InsetsState outInsetsState,
+ InsetsSourceControl[] outActiveControls) {
return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
- outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
- outInsetsState, outActiveControls);
+ outFrame, outDisplayCutout, outInputChannel, outInsetsState, outActiveControls);
}
@Override
public int addToDisplayWithoutInputChannel(android.view.IWindow window,
android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId,
- android.graphics.Rect outContentInsets, android.graphics.Rect outStableInsets,
android.view.InsetsState insetsState) {
return 0;
}
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index be1a2f221587..e92c30fcbc3b 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -65,7 +65,7 @@ public final class AutofillValue implements Parcelable {
* @throws IllegalStateException if the value is not a text value
*/
@NonNull public CharSequence getTextValue() {
- Preconditions.checkState(isText(), "value must be a text value, not type=" + mType);
+ Preconditions.checkState(isText(), "value must be a text value, not type=%d", mType);
return (CharSequence) mValue;
}
@@ -86,7 +86,7 @@ public final class AutofillValue implements Parcelable {
* @throws IllegalStateException if the value is not a toggle value
*/
public boolean getToggleValue() {
- Preconditions.checkState(isToggle(), "value must be a toggle value, not type=" + mType);
+ Preconditions.checkState(isToggle(), "value must be a toggle value, not type=%d", mType);
return (Boolean) mValue;
}
@@ -107,7 +107,7 @@ public final class AutofillValue implements Parcelable {
* @throws IllegalStateException if the value is not a list value
*/
public int getListValue() {
- Preconditions.checkState(isList(), "value must be a list value, not type=" + mType);
+ Preconditions.checkState(isList(), "value must be a list value, not type=%d", mType);
return (Integer) mValue;
}
@@ -128,7 +128,7 @@ public final class AutofillValue implements Parcelable {
* @throws IllegalStateException if the value is not a date value
*/
public long getDateValue() {
- Preconditions.checkState(isDate(), "value must be a date value, not type=" + mType);
+ Preconditions.checkState(isDate(), "value must be a date value, not type=%d", mType);
return (Long) mValue;
}
@@ -150,7 +150,7 @@ public final class AutofillValue implements Parcelable {
*/
public @NonNull ClipData getRichContentValue() {
Preconditions.checkState(isRichContent(),
- "value must be a rich content value, not type=" + mType);
+ "value must be a rich content value, not type=%d", mType);
return (ClipData) mValue;
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 73636f81369f..e0711132f459 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -19,6 +19,8 @@ package android.view.inputmethod;
import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
import android.annotation.CallSuper;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
@@ -585,6 +587,48 @@ public class BaseInputConnection implements InputConnection {
}
/**
+ * The default implementation returns the given amount of text around the current cursor
+ * position in the buffer.
+ */
+ @Nullable
+ public SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ final Editable content = getEditable();
+ if (content == null) return null;
+
+ int selStart = Selection.getSelectionStart(content);
+ int selEnd = Selection.getSelectionEnd(content);
+
+ // Guard against the case where the cursor has not been positioned yet.
+ if (selStart < 0 || selEnd < 0) {
+ return null;
+ }
+
+ if (selStart > selEnd) {
+ int tmp = selStart;
+ selStart = selEnd;
+ selEnd = tmp;
+ }
+
+ int contentLength = content.length();
+ int startPos = selStart - beforeLength;
+ int endPos = selEnd + afterLength;
+
+ // Guards the start and end pos within range [0, contentLength].
+ startPos = Math.max(0, startPos);
+ endPos = Math.min(contentLength, endPos);
+
+ CharSequence surroundingText;
+ if ((flags & GET_TEXT_WITH_STYLES) != 0) {
+ surroundingText = content.subSequence(startPos, endPos);
+ } else {
+ surroundingText = TextUtils.substring(content, startPos, endPos);
+ }
+ return new SurroundingText(
+ surroundingText, selStart - startPos, selEnd - startPos, startPos);
+ }
+
+ /**
* The default implementation turns this into the enter key.
*/
public boolean performEditorAction(int actionCode) {
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 4337ed5109db..c7acd298cd20 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -16,14 +16,20 @@
package android.view.inputmethod;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
import android.os.Handler;
+import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The InputConnection interface is the communication channel from an
* {@link InputMethod} back to the application that is receiving its
@@ -122,14 +128,20 @@ import android.view.KeyEvent;
* of each other, and the IME may use them however they see fit.</p>
*/
public interface InputConnection {
+ /** @hide */
+ @IntDef(flag = true, prefix = { "GET_TEXT_" }, value = {
+ GET_TEXT_WITH_STYLES,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface GetTextType {}
+
/**
- * Flag for use with {@link #getTextAfterCursor} and
- * {@link #getTextBeforeCursor} to have style information returned
- * along with the text. If not set, {@link #getTextAfterCursor}
- * sends only the raw text, without style or other spans. If set,
- * it may return a complex CharSequence of both text and style
- * spans. <strong>Editor authors</strong>: you should strive to
- * send text with styles if possible, but it is not required.
+ * Flag for use with {@link #getTextAfterCursor}, {@link #getTextBeforeCursor} and
+ * {@link #getSurroundingText} to have style information returned along with the text. If not
+ * set, {@link #getTextAfterCursor} sends only the raw text, without style or other spans. If
+ * set, it may return a complex CharSequence of both text and style spans.
+ * <strong>Editor authors</strong>: you should strive to send text with styles if possible, but
+ * it is not required.
*/
int GET_TEXT_WITH_STYLES = 0x0001;
@@ -264,6 +276,61 @@ public interface InputConnection {
CharSequence getSelectedText(int flags);
/**
+ * Gets the surrounding text around the current cursor, with <var>beforeLength</var> characters
+ * of text before the cursor (start of the selection), <var>afterLength</var> characters of text
+ * after the cursor (end of the selection), and all of the selected text.
+ *
+ * <p>This method may fail either if the input connection has become invalid (such as its
+ * process crashing), or the client is taking too long to respond with the text (it is given a
+ * couple seconds to return), or the protocol is not supported. In any of these cases, null is
+ * returned.
+ *
+ * <p>This method does not affect the text in the editor in any way, nor does it affect the
+ * selection or composing spans.</p>
+ *
+ * <p>If {@link #GET_TEXT_WITH_STYLES} is supplied as flags, the editor should return a
+ * {@link android.text.Spanned} with all the spans set on the text.</p>
+ *
+ * <p><strong>IME authors:</strong> please consider this will trigger an IPC round-trip that
+ * will take some time. Assume this method consumes a lot of time. If you are using this to get
+ * the initial surrounding text around the cursor, you may consider using
+ * {@link EditorInfo#getInitialTextBeforeCursor(int, int)},
+ * {@link EditorInfo#getInitialSelectedText(int)}, and
+ * {@link EditorInfo#getInitialTextAfterCursor(int, int)} to prevent IPC costs.</p>
+ *
+ * @param beforeLength The expected length of the text before the cursor.
+ * @param afterLength The expected length of the text after the cursor.
+ * @param flags Supplies additional options controlling how the text is returned. Defined by the
+ * constants.
+ * @return an {@link android.view.inputmethod.SurroundingText} object describing the surrounding
+ * text and state of selection, or null if the input connection is no longer valid, or the
+ * editor can't comply with the request for some reason, or the application does not implement
+ * this method. The length of the returned text might be less than the sum of
+ * <var>beforeLength</var> and <var>afterLength</var> .
+ */
+ @Nullable
+ default SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
+ @GetTextType int flags) {
+ CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags);
+ if (textBeforeCursor == null) {
+ textBeforeCursor = "";
+ }
+ CharSequence selectedText = getSelectedText(flags);
+ if (selectedText == null) {
+ selectedText = "";
+ }
+ CharSequence textAfterCursor = getTextAfterCursor(afterLength, flags);
+ if (textAfterCursor == null) {
+ textAfterCursor = "";
+ }
+ CharSequence surroundingText =
+ TextUtils.concat(textBeforeCursor, selectedText, textAfterCursor);
+ return new SurroundingText(surroundingText, textBeforeCursor.length(),
+ textBeforeCursor.length() + selectedText.length(), -1);
+ }
+
+ /**
* Retrieve the current capitalization mode in effect at the
* current cursor position in the text. See
* {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}
diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java
index 5f25bf58ce57..7621da7cef1b 100644
--- a/core/java/android/view/inputmethod/InputConnectionInspector.java
+++ b/core/java/android/view/inputmethod/InputConnectionInspector.java
@@ -44,6 +44,7 @@ public final class InputConnectionInspector {
MissingMethodFlags.GET_HANDLER,
MissingMethodFlags.CLOSE_CONNECTION,
MissingMethodFlags.COMMIT_CONTENT,
+ MissingMethodFlags.GET_SURROUNDING_TEXT
})
public @interface MissingMethodFlags {
/**
@@ -86,6 +87,11 @@ public final class InputConnectionInspector {
* {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
*/
int COMMIT_CONTENT = 1 << 7;
+ /**
+ * {@link InputConnection#getSurroundingText(int, int, int)} is available in
+ * {@link android.os.Build.VERSION_CODES#S} and later.
+ */
+ int GET_SURROUNDING_TEXT = 1 << 8;
}
private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
@@ -138,6 +144,9 @@ public final class InputConnectionInspector {
if (!hasCommitContent(clazz)) {
flags |= MissingMethodFlags.COMMIT_CONTENT;
}
+ if (!hasGetSurroundingText(clazz)) {
+ flags |= MissingMethodFlags.GET_SURROUNDING_TEXT;
+ }
sMissingMethodsMap.put(clazz, flags);
return flags;
}
@@ -216,6 +225,16 @@ public final class InputConnectionInspector {
}
}
+ private static boolean hasGetSurroundingText(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("getSurroundingText", int.class, int.class,
+ int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
final StringBuilder sb = new StringBuilder();
boolean isEmpty = true;
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index f671e22b4922..ec7fa60a62ba 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
@@ -101,6 +102,16 @@ public class InputConnectionWrapper implements InputConnection {
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Nullable
+ @Override
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ return mTarget.getSurroundingText(beforeLength, afterLength, flags);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
@Override
public int getCursorCapsMode(int reqModes) {
return mTarget.getCursorCapsMode(reqModes);
diff --git a/core/java/android/view/inputmethod/SurroundingText.aidl b/core/java/android/view/inputmethod/SurroundingText.aidl
new file mode 100644
index 000000000000..7a9898ec6a43
--- /dev/null
+++ b/core/java/android/view/inputmethod/SurroundingText.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable SurroundingText; \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/SurroundingText.java b/core/java/android/view/inputmethod/SurroundingText.java
new file mode 100644
index 000000000000..506f95a4ac7b
--- /dev/null
+++ b/core/java/android/view/inputmethod/SurroundingText.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information about the surrounding text around the cursor for use by an input method.
+ *
+ * <p>This contains information about the text and the selection relative to the text. </p>
+ */
+public final class SurroundingText implements Parcelable {
+ /**
+ * The surrounding text around the cursor.
+ */
+ @NonNull
+ private final CharSequence mText;
+
+ /**
+ * The text offset of the start of the selection in the surrounding text.
+ *
+ * <p>This needs to be the position relative to the {@link #mText} instead of the real position
+ * in the editor.</p>
+ */
+ @IntRange(from = 0)
+ private final int mSelectionStart;
+
+ /**
+ * The text offset of the end of the selection in the surrounding text.
+ *
+ * <p>This needs to be the position relative to the {@link #mText} instead of the real position
+ * in the editor.</p>
+ */
+ @IntRange(from = 0)
+ private final int mSelectionEnd;
+
+ /**
+ * The text offset between the start of the editor's text and the start of the surrounding text.
+ *
+ * <p>-1 indicates the offset information is unknown.</p>
+ */
+ @IntRange(from = -1)
+ private final int mOffset;
+
+ /**
+ * Constructor.
+ *
+ * @param text The surrounding text.
+ * @param selectionStart The text offset of the start of the selection in the surrounding text.
+ * Reversed selection is allowed.
+ * @param selectionEnd The text offset of the end of the selection in the surrounding text.
+ * Reversed selection is allowed.
+ * @param offset The text offset between the start of the editor's text and the start of the
+ * surrounding text. -1 indicates the offset is unknown.
+ */
+ public SurroundingText(@NonNull final CharSequence text,
+ @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd,
+ @IntRange(from = -1) int offset) {
+ mText = text;
+ mSelectionStart = selectionStart;
+ mSelectionEnd = selectionEnd;
+ mOffset = offset;
+ }
+
+ /**
+ * Returns the surrounding text around the cursor.
+ */
+ @NonNull
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns the text offset of the start of the selection in the surrounding text.
+ */
+ @IntRange(from = 0)
+ public int getSelectionStart() {
+ return mSelectionStart;
+ }
+
+ /**
+ * Returns the text offset of the end of the selection in the surrounding text.
+ */
+ @IntRange(from = 0)
+ public int getSelectionEnd() {
+ return mSelectionEnd;
+ }
+
+ /**
+ * Returns text offset between the start of the editor's text and the start of the surrounding
+ * text.
+ *
+ * <p>-1 indicates the offset information is unknown.</p>
+ */
+ @IntRange(from = -1)
+ public int getOffset() {
+ return mOffset;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ TextUtils.writeToParcel(mText, out, flags);
+ out.writeInt(mSelectionStart);
+ out.writeInt(mSelectionEnd);
+ out.writeInt(mOffset);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<SurroundingText> CREATOR =
+ new Parcelable.Creator<SurroundingText>() {
+ @NonNull
+ public SurroundingText createFromParcel(Parcel in) {
+ final CharSequence text =
+ TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ final int selectionHead = in.readInt();
+ final int selectionEnd = in.readInt();
+ final int offset = in.readInt();
+ return new SurroundingText(
+ text == null ? "" : text, selectionHead, selectionEnd, offset);
+ }
+
+ @NonNull
+ public SurroundingText[] newArray(int size) {
+ return new SurroundingText[size];
+ }
+ };
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 97e0689ce64f..45b21c569cea 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -82,6 +82,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.SurroundingText;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.widget.RemoteViews.OnClickHandler;
@@ -5997,6 +5998,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@Override
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ if (mTarget == null) return null;
+ return mTarget.getSurroundingText(beforeLength, afterLength, flags);
+ }
+
+ @Override
public int getCursorCapsMode(int reqModes) {
if (mTarget == null) return InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
return mTarget.getCursorCapsMode(reqModes);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7bb2b7e92a00..3fc0f4efd608 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11051,12 +11051,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
MotionEvent.actionToString(event.getActionMasked()),
event.getX(), event.getY());
}
- if (!isFromPrimePointer(event, false)) {
- return true;
- }
-
final int action = event.getActionMasked();
if (mEditor != null) {
+ if (!isFromPrimePointer(event, false)) {
+ return true;
+ }
+
mEditor.onTouchEvent(event);
if (mEditor.mInsertionPointCursorController != null
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 0523e64f3e7a..5d7025b57f91 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -42,19 +42,11 @@ public class ClientWindowFrames implements Parcelable {
/** The area cut from the display. */
public final @NonNull DisplayCutout.ParcelableWrapper displayCutout;
- // TODO(b/149813814): Remove legacy insets.
- public final Rect contentInsets;
- public final Rect visibleInsets;
- public final Rect stableInsets;
-
public ClientWindowFrames() {
frame = new Rect();
displayFrame = new Rect();
backdropFrame = new Rect();
displayCutout = new DisplayCutout.ParcelableWrapper();
- contentInsets = new Rect();
- visibleInsets = new Rect();
- stableInsets = new Rect();
}
public ClientWindowFrames(ClientWindowFrames other) {
@@ -62,9 +54,6 @@ public class ClientWindowFrames implements Parcelable {
displayFrame = new Rect(other.displayFrame);
backdropFrame = new Rect(other.backdropFrame);
displayCutout = new DisplayCutout.ParcelableWrapper(other.displayCutout.get());
- contentInsets = new Rect(other.contentInsets);
- visibleInsets = new Rect(other.visibleInsets);
- stableInsets = new Rect(other.stableInsets);
}
private ClientWindowFrames(Parcel in) {
@@ -72,9 +61,6 @@ public class ClientWindowFrames implements Parcelable {
displayFrame = Rect.CREATOR.createFromParcel(in);
backdropFrame = Rect.CREATOR.createFromParcel(in);
displayCutout = DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in);
- contentInsets = Rect.CREATOR.createFromParcel(in);
- visibleInsets = Rect.CREATOR.createFromParcel(in);
- stableInsets = Rect.CREATOR.createFromParcel(in);
}
/** Needed for AIDL out parameters. */
@@ -83,9 +69,6 @@ public class ClientWindowFrames implements Parcelable {
displayFrame.set(Rect.CREATOR.createFromParcel(in));
backdropFrame.set(Rect.CREATOR.createFromParcel(in));
displayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
- contentInsets.set(Rect.CREATOR.createFromParcel(in));
- visibleInsets.set(Rect.CREATOR.createFromParcel(in));
- stableInsets.set(Rect.CREATOR.createFromParcel(in));
}
@Override
@@ -94,9 +77,6 @@ public class ClientWindowFrames implements Parcelable {
displayFrame.writeToParcel(dest, flags);
backdropFrame.writeToParcel(dest, flags);
displayCutout.writeToParcel(dest, flags);
- contentInsets.writeToParcel(dest, flags);
- visibleInsets.writeToParcel(dest, flags);
- stableInsets.writeToParcel(dest, flags);
}
@Override
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index b5a11b1a8136..3a84c1f98ce6 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -39,12 +39,8 @@ interface ITaskOrganizerController {
*/
void unregisterTaskOrganizer(ITaskOrganizer organizer);
- /**
- * Creates a persistent root task in WM for a particular windowing-mode.
- * {@link TaskOrganizer#onTaskAppeared} won't be called since we are returning
- * {@link TaskAppearedInfo} here.
- */
- TaskAppearedInfo createRootTask(int displayId, int windowingMode);
+ /** Creates a persistent root task in WM for a particular windowing-mode. */
+ ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode);
/** Deletes a persistent root task in WM */
boolean deleteRootTask(in WindowContainerToken task);
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 5c86e1c124f7..6c739bed35a4 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -29,6 +29,7 @@ import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Interface for ActivityTaskManager/WindowManager to delegate control of tasks.
@@ -37,15 +38,19 @@ import java.util.List;
@TestApi
public class TaskOrganizer extends WindowOrganizer {
- private ITaskOrganizerController mTaskOrganizerController;
+ private final ITaskOrganizerController mTaskOrganizerController;
+ // Callbacks WM Core are posted on this executor if it isn't null, otherwise direct calls are
+ // made on the incoming binder call.
+ private final Executor mExecutor;
public TaskOrganizer() {
- this(null);
+ this(null /*taskOrganizerController*/, null /*executor*/);
}
/** @hide */
@VisibleForTesting
- public TaskOrganizer(ITaskOrganizerController taskOrganizerController) {
+ public TaskOrganizer(ITaskOrganizerController taskOrganizerController, Executor executor) {
+ mExecutor = executor != null ? executor : command -> command.run();
mTaskOrganizerController = taskOrganizerController != null
? taskOrganizerController : getController();
}
@@ -99,7 +104,7 @@ public class TaskOrganizer extends WindowOrganizer {
/** Creates a persistent root task in WM for a particular windowing-mode. */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
@Nullable
- public TaskAppearedInfo createRootTask(int displayId, int windowingMode) {
+ public ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode) {
try {
return mTaskOrganizerController.createRootTask(displayId, windowingMode);
} catch (RemoteException e) {
@@ -183,22 +188,22 @@ public class TaskOrganizer extends WindowOrganizer {
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- TaskOrganizer.this.onTaskAppeared(taskInfo, leash);
+ mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- TaskOrganizer.this.onTaskVanished(taskInfo);
+ mExecutor.execute(() -> TaskOrganizer.this.onTaskVanished(taskInfo));
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
- TaskOrganizer.this.onTaskInfoChanged(info);
+ mExecutor.execute(() -> TaskOrganizer.this.onTaskInfoChanged(info));
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo info) {
- TaskOrganizer.this.onBackPressedOnTaskRoot(info);
+ mExecutor.execute(() -> TaskOrganizer.this.onBackPressedOnTaskRoot(info));
}
};
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index db27d6255de7..be5d55af5e8a 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -63,12 +63,10 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
// For Virtual Displays
private int mDisplayDensityDpi;
- private final boolean mSingleTaskInstance;
private final boolean mUsePublicVirtualDisplay;
private final boolean mUseTrustedDisplay;
private VirtualDisplay mVirtualDisplay;
private Insets mForwardedInsets;
- private DisplayMetrics mTmpDisplayMetrics;
private TaskStackListener mTaskStackListener;
/**
@@ -76,14 +74,10 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
*
* @param context the context
* @param host the host for this embedded task
- * @param singleTaskInstance whether to apply a single-task constraint to this container,
- * only applicable if virtual displays are used
*/
public VirtualDisplayTaskEmbedder(Context context, VirtualDisplayTaskEmbedder.Host host,
- boolean singleTaskInstance, boolean usePublicVirtualDisplay,
- boolean useTrustedDisplay) {
+ boolean usePublicVirtualDisplay, boolean useTrustedDisplay) {
super(context, host);
- mSingleTaskInstance = singleTaskInstance;
mUsePublicVirtualDisplay = usePublicVirtualDisplay;
mUseTrustedDisplay = useTrustedDisplay;
}
@@ -128,10 +122,6 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
WindowManagerGlobal.getWindowSession().reparentDisplayContent(
mHost.getWindow(), mSurfaceControl, displayId);
wm.dontOverrideDisplayInfo(displayId);
- if (mSingleTaskInstance) {
- mContext.getSystemService(ActivityTaskManager.class)
- .setDisplayToSingleTaskInstance(displayId);
- }
setForwardedInsets(mForwardedInsets);
mTaskStackListener = new TaskStackListenerImpl();
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index d8eaeda2b549..52dc7e646d29 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -191,8 +191,9 @@ public class SuspendedAppActivity extends AlertActivity
mOnUnsuspend = intent.getParcelableExtra(EXTRA_UNSUSPEND_INTENT);
if (mSuppliedDialogInfo != null) {
try {
- mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(mSuspendingPackage,
- mUserId);
+ mSuspendingAppResources = createContextAsUser(
+ UserHandle.of(mUserId), /* flags */ 0).getPackageManager()
+ .getResourcesForApplication(mSuspendedPackage);
} catch (PackageManager.NameNotFoundException ne) {
Slog.e(TAG, "Could not find resources for " + mSuspendingPackage, ne);
}
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index 5e886a611913..7a87be35ea98 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -16,6 +16,8 @@
package com.android.internal.compat;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.IntDef;
import android.util.Log;
import android.util.Slog;
@@ -175,7 +177,7 @@ public final class ChangeReporter {
}
private void debugLog(int uid, long changeId, int state) {
- String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
+ String message = formatSimple("Compat change id reported: %d; UID %d; state: %s", changeId,
uid, stateToString(state));
if (mSource == SOURCE_SYSTEM_SERVER) {
Slog.d(TAG, message);
diff --git a/core/java/com/android/internal/inputmethod/CancellationGroup.java b/core/java/com/android/internal/inputmethod/CancellationGroup.java
index 09c9d128553b..a4a220880d46 100644
--- a/core/java/com/android/internal/inputmethod/CancellationGroup.java
+++ b/core/java/com/android/internal/inputmethod/CancellationGroup.java
@@ -263,6 +263,16 @@ public final class CancellationGroup {
super(factory);
}
}
+
+ /**
+ * Completable object of {@link android.view.inputmethod.SurroundingText}.
+ */
+ public static final class SurroundingText
+ extends Values<android.view.inputmethod.SurroundingText> {
+ private SurroundingText(@NonNull CancellationGroup factory) {
+ super(factory);
+ }
+ }
}
/**
@@ -292,6 +302,16 @@ public final class CancellationGroup {
return new Completable.ExtractedText(this);
}
+ /**
+ * @return an instance of {@link Completable.SurroundingText} that is associated with this
+ * {@link CancellationGroup}.
+ */
+ @AnyThread
+ public Completable.SurroundingText createCompletableSurroundingText() {
+ return new Completable.SurroundingText(this);
+ }
+
+
@AnyThread
private boolean registerLatch(@NonNull CountDownLatch latch) {
synchronized (mLock) {
diff --git a/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl b/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
new file mode 100644
index 000000000000..6c4f3d58ed02
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 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.inputmethod;
+
+import android.view.inputmethod.SurroundingText;
+
+oneway interface ISurroundingTextResultCallback {
+ void onResult(in SurroundingText result);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 44a8a83b519f..5eba898fde8a 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -129,4 +129,32 @@ public final class ResultCallbacks {
}
};
}
+
+ /**
+ * Creates {@link ISurroundingTextResultCallback.Stub} that is to set
+ * {@link CancellationGroup.Completable.SurroundingText} when receiving the result.
+ *
+ * @param value {@link CancellationGroup.Completable.SurroundingText} to be set when receiving
+ * the result.
+ * @return {@link ISurroundingTextResultCallback.Stub} that can be passed as a binder IPC
+ * parameter.
+ */
+ @AnyThread
+ public static ISurroundingTextResultCallback.Stub of(
+ @NonNull CancellationGroup.Completable.SurroundingText value) {
+ final AtomicReference<WeakReference<CancellationGroup.Completable.SurroundingText>>
+ atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+ return new ISurroundingTextResultCallback.Stub() {
+ @BinderThread
+ @Override
+ public void onResult(android.view.inputmethod.SurroundingText result) {
+ final CancellationGroup.Completable.SurroundingText value = unwrap(atomicRef);
+ if (value == null) {
+ return;
+ }
+ value.onComplete(result);
+ }
+ };
+ }
}
diff --git a/core/java/com/android/internal/os/AppIdToPackageMap.java b/core/java/com/android/internal/os/AppIdToPackageMap.java
index 65aa989bbb38..98cced89a174 100644
--- a/core/java/com/android/internal/os/AppIdToPackageMap.java
+++ b/core/java/com/android/internal/os/AppIdToPackageMap.java
@@ -16,25 +16,23 @@
package com.android.internal.os;
-
import android.app.AppGlobals;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/** Maps AppIds to their package names. */
public final class AppIdToPackageMap {
- private final Map<Integer, String> mAppIdToPackageMap;
+ private final SparseArray<String> mAppIdToPackageMap;
@VisibleForTesting
- public AppIdToPackageMap(Map<Integer, String> appIdToPackageMap) {
+ public AppIdToPackageMap(SparseArray<String> appIdToPackageMap) {
mAppIdToPackageMap = appIdToPackageMap;
}
@@ -50,10 +48,10 @@ public final class AppIdToPackageMap {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- final Map<Integer, String> map = new HashMap<>();
+ final SparseArray<String> map = new SparseArray<>();
for (PackageInfo pkg : packages) {
final int uid = pkg.applicationInfo.uid;
- if (pkg.sharedUserId != null && map.containsKey(uid)) {
+ if (pkg.sharedUserId != null && map.indexOfKey(uid) >= 0) {
// Use sharedUserId string as package name if there are collisions
map.put(uid, "shared:" + pkg.sharedUserId);
} else {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5948e7eab4ee..c762939faac2 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -385,7 +385,6 @@ public class ZygoteInit {
"/system/framework/android.hidl.manager-V1.0-java.jar", null /*packageName*/,
null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/);
- hidlManager.addDependency(hidlBase);
SharedLibraryInfo androidTestBase = new SharedLibraryInfo(
"/system/framework/android.test.base.jar", null /*packageName*/,
diff --git a/core/java/com/android/internal/util/LocalLog.java b/core/java/com/android/internal/util/LocalLog.java
index 39166917d521..057dc8fd477c 100644
--- a/core/java/com/android/internal/util/LocalLog.java
+++ b/core/java/com/android/internal/util/LocalLog.java
@@ -16,12 +16,12 @@
package com.android.internal.util;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import java.util.ArrayList;
+
/**
* Helper class for logging serious issues, which also keeps a small
* snapshot of the logged events that can be printed later, such as part
@@ -47,20 +47,21 @@ public class LocalLog {
}
}
- public boolean dump(PrintWriter pw, String header, String prefix) {
+ public boolean dump(IndentingPrintWriter pw, String header) {
synchronized (mLines) {
if (mLines.size() <= 0) {
return false;
}
if (header != null) {
pw.println(header);
+ pw.increaseIndent();
}
for (int i=0; i<mLines.size(); i++) {
- if (prefix != null) {
- pw.print(prefix);
- }
pw.println(mLines.get(i));
}
+ if (header != null) {
+ pw.decreaseIndent();
+ }
return true;
}
}
diff --git a/core/java/com/android/internal/util/NotificationMessagingUtil.java b/core/java/com/android/internal/util/NotificationMessagingUtil.java
index 28994fd52126..c59647d264f6 100644
--- a/core/java/com/android/internal/util/NotificationMessagingUtil.java
+++ b/core/java/com/android/internal/util/NotificationMessagingUtil.java
@@ -26,7 +26,7 @@ import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
+import android.util.SparseArray;
import java.util.Collection;
import java.util.Objects;
@@ -39,7 +39,7 @@ public class NotificationMessagingUtil {
private static final String DEFAULT_SMS_APP_SETTING = Settings.Secure.SMS_DEFAULT_APPLICATION;
private final Context mContext;
- private ArrayMap<Integer, String> mDefaultSmsApp = new ArrayMap<>();
+ private SparseArray<String> mDefaultSmsApp = new SparseArray<>();
public NotificationMessagingUtil(Context context) {
mContext = context;
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index e80e5454f40e..4d441cd98a96 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -262,6 +262,25 @@ public class Preconditions {
}
/**
+ * Ensures the truth of an expression involving whether the calling identity is authorized to
+ * call the calling method.
+ *
+ * @param expression a boolean expression
+ * @param messageTemplate a printf-style message template to use if the check fails; will
+ * be converted to a string using {@link String#format(String, Object...)}
+ * @param messageArgs arguments for {@code messageTemplate}
+ * @throws SecurityException if {@code expression} is false
+ */
+ public static void checkCallAuthorization(
+ final boolean expression,
+ final @NonNull String messageTemplate,
+ final Object... messageArgs) {
+ if (!expression) {
+ throw new SecurityException(String.format(messageTemplate, messageArgs));
+ }
+ }
+
+ /**
* Ensures the truth of an expression involving whether the calling user is authorized to
* call the calling method.
*
@@ -694,7 +713,7 @@ public class Preconditions {
*/
public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
String valueName) {
- checkNotNull(value, valueName + " must not be null");
+ checkNotNull(value, "%s must not be null", valueName);
for (int i = 0; i < value.length; ++i) {
float v = value[i];
@@ -730,7 +749,7 @@ public class Preconditions {
*/
public static int[] checkArrayElementsInRange(int[] value, int lower, int upper,
String valueName) {
- checkNotNull(value, valueName + " must not be null");
+ checkNotNull(value, "%s must not be null", valueName);
for (int i = 0; i < value.length; ++i) {
int v = value[i];
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index c1be33a215b8..bd6b950623eb 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -1416,19 +1416,19 @@ public class XmlUtils {
if (tagName.equals("null")) {
res = null;
} else if (tagName.equals("string")) {
- String value = "";
+ final StringBuilder value = new StringBuilder();
int eventType;
while ((eventType = parser.next()) != parser.END_DOCUMENT) {
if (eventType == parser.END_TAG) {
if (parser.getName().equals("string")) {
name[0] = valueName;
//System.out.println("Returning value for " + valueName + ": " + value);
- return value;
+ return value.toString();
}
throw new XmlPullParserException(
"Unexpected end tag in <string>: " + parser.getName());
} else if (eventType == parser.TEXT) {
- value += parser.getText();
+ value.append(parser.getText());
} else if (eventType == parser.START_TAG) {
throw new XmlPullParserException(
"Unexpected start tag in <string>: " + parser.getName());
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 9257c6d19148..cd5502c9f270 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -35,11 +35,13 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.ICharSequenceResultCallback;
import com.android.internal.inputmethod.IExtractedTextResultCallback;
import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.ISurroundingTextResultCallback;
import com.android.internal.os.SomeArgs;
public abstract class IInputConnectionWrapper extends IInputContext.Stub {
@@ -70,6 +72,8 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
private static final int DO_CLOSE_CONNECTION = 150;
private static final int DO_COMMIT_CONTENT = 160;
+ private static final int DO_GET_SURROUNDING_TEXT = 41;
+
@GuardedBy("mLock")
@Nullable
@@ -127,6 +131,21 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
}
+ /**
+ * Dispatches the request for retrieving surrounding text.
+ *
+ * <p>See {@link InputConnection#getSurroundingText(int, int, int)}.
+ */
+ public void getSurroundingText(int beforeLength, int afterLength, int flags,
+ ISurroundingTextResultCallback callback) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = beforeLength;
+ args.arg2 = afterLength;
+ args.arg3 = flags;
+ args.arg4 = callback;
+ dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
+ }
+
public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
dispatchMessage(
mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
@@ -293,6 +312,33 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
}
return;
}
+ case DO_GET_SURROUNDING_TEXT: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ int beforeLength = (int) args.arg1;
+ int afterLength = (int) args.arg2;
+ int flags = (int) args.arg3;
+ final ISurroundingTextResultCallback callback =
+ (ISurroundingTextResultCallback) args.arg4;
+ final InputConnection ic = getInputConnection();
+ final SurroundingText result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getSurroundingText on inactive InputConnection");
+ result = null;
+ } else {
+ result = ic.getSurroundingText(beforeLength, afterLength, flags);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getSurroundingText()."
+ + " result=" + result, e);
+ }
+ } finally {
+ args.recycle();
+ }
+ return;
+ }
case DO_GET_CURSOR_CAPS_MODE: {
final IIntResultCallback callback = (IIntResultCallback) msg.obj;
final InputConnection ic = getInputConnection();
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 86f1293c014f..074908acdf2a 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -26,6 +26,7 @@ import android.view.inputmethod.InputContentInfo;
import com.android.internal.inputmethod.ICharSequenceResultCallback;
import com.android.internal.inputmethod.IExtractedTextResultCallback;
import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.ISurroundingTextResultCallback;
/**
* Interface from an input method to the application, allowing it to perform
@@ -79,4 +80,7 @@ import com.android.internal.inputmethod.IIntResultCallback;
void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
IIntResultCallback callback);
+
+ void getSurroundingText(int beforeLength, int afterLength, int flags,
+ ISurroundingTextResultCallback callback);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 0bf52345bc7e..f086dd79758b 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -33,6 +33,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.ResultCallbacks;
@@ -157,6 +158,38 @@ public class InputConnectionWrapper implements InputConnection {
return getResultOrNull(value, "getSelectedText()");
}
+ /**
+ * Get {@link SurroundingText} around the current cursor, with <var>beforeLength</var>
+ * characters of text before the cursor, <var>afterLength</var> characters of text after the
+ * cursor, and all of the selected text.
+ * @param beforeLength The expected length of the text before the cursor
+ * @param afterLength The expected length of the text after the cursor
+ * @param flags Supplies additional options controlling how the text is returned. May be either
+ * 0 or {@link #GET_TEXT_WITH_STYLES}.
+ * @return the surrounding text around the cursor position; the length of the returned text
+ * might be less than requested. It could also be {@code null} when the editor or system could
+ * not support this protocol.
+ */
+ @AnyThread
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ if (mCancellationGroup.isCanceled()) {
+ return null;
+ }
+ if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
+ // This method is not implemented.
+ return null;
+ }
+ final CancellationGroup.Completable.SurroundingText value =
+ mCancellationGroup.createCompletableSurroundingText();
+ try {
+ mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
+ ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ return null;
+ }
+ return getResultOrNull(value, "getSurroundingText()");
+ }
+
@AnyThread
public int getCursorCapsMode(int reqModes) {
if (mCancellationGroup.isCanceled()) {
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 84cde1b84e14..0bf323f8f493 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -16,12 +16,15 @@
package com.android.internal.widget;
+import static com.android.internal.widget.ColoredIconHelper.applyGrayTint;
+
import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -48,6 +51,7 @@ public class CachingIconView extends ImageView {
private Consumer<Integer> mOnVisibilityChangedListener;
private Consumer<Boolean> mOnForceHiddenChangedListener;
private int mIconColor;
+ private int mBackgroundColor;
private boolean mWillBeForceHidden;
@UnsupportedAppUsage
@@ -230,9 +234,55 @@ public class CachingIconView extends ImageView {
return mForceHidden;
}
+ /**
+ * Provides the notification's background color to the icon. This is only used when the icon
+ * is "inverted". This should be called before calling {@link #setOriginalIconColor(int)}.
+ */
+ @RemotableViewMethod
+ public void setBackgroundColor(int color) {
+ mBackgroundColor = color;
+ }
+
+ /**
+ * Sets the icon color. If COLOR_INVALID is set, the icon's color filter will
+ * not be altered. If there is a background drawable, this method uses the value from
+ * {@link #setBackgroundColor(int)} which must have been already called.
+ */
@RemotableViewMethod
public void setOriginalIconColor(int color) {
mIconColor = color;
+ Drawable background = getBackground();
+ Drawable icon = getDrawable();
+ boolean hasColor = color != ColoredIconHelper.COLOR_INVALID;
+ if (background == null) {
+ // This is the pre-S style -- colored icon with no background.
+ if (hasColor) {
+ icon.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ }
+ } else {
+ // When there is a background drawable, color it with the foreground color and
+ // colorize the icon itself with the background color, creating an inverted effect.
+ if (hasColor) {
+ background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+ } else {
+ background.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+ }
+ }
+ }
+
+ /**
+ * Set the icon's color filter: to gray if true, otherwise colored.
+ * If this icon has no original color, this has no effect.
+ */
+ public void setGrayedOut(boolean grayedOut) {
+ // If there is a background drawable, then it has the foreground color and the image
+ // drawable has the background color, creating an inverted efffect.
+ Drawable drawable = getBackground();
+ if (drawable == null) {
+ drawable = getDrawable();
+ }
+ applyGrayTint(mContext, drawable, grayedOut, mIconColor);
}
public int getOriginalIconColor() {
diff --git a/core/java/com/android/internal/widget/ColoredIconHelper.java b/core/java/com/android/internal/widget/ColoredIconHelper.java
new file mode 100644
index 000000000000..97e5e86d9e60
--- /dev/null
+++ b/core/java/com/android/internal/widget/ColoredIconHelper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.annotation.ColorInt;
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.util.ContrastColorUtil;
+
+/** Helpers for colored icons */
+final class ColoredIconHelper {
+
+ @ColorInt
+ static final int COLOR_INVALID = Notification.COLOR_INVALID;
+
+ private ColoredIconHelper() {
+ }
+
+ /**
+ * Apply a gray tint or the original color to a drawable, accounting for the night mode in
+ * selecting the gray.
+ */
+ static void applyGrayTint(Context ctx, Drawable drawable, boolean apply, int originalColor) {
+ if (originalColor == COLOR_INVALID) {
+ return;
+ }
+ if (apply) {
+ // lets gray it out
+ Configuration config = ctx.getResources().getConfiguration();
+ boolean inNightMode = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+ int grey = ContrastColorUtil.resolveColor(ctx, Notification.COLOR_DEFAULT, inNightMode);
+ drawable.mutate().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
+ } else {
+ // lets reset it
+ drawable.mutate().setColorFilter(originalColor, PorterDuff.Mode.SRC_ATOP);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index a49980696e6b..986412d2ce5d 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -16,6 +16,8 @@
package com.android.internal.widget;
+import static com.android.internal.widget.ColoredIconHelper.applyGrayTint;
+
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
@@ -26,12 +28,15 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.RemoteViews;
+import com.android.internal.R;
+
/**
* An expand button in a notification
*/
@RemoteViews.RemoteView
public class NotificationExpandButton extends ImageView {
+ private boolean mExpanded;
private int mOriginalNotificationColor;
public NotificationExpandButton(Context context) {
@@ -67,6 +72,14 @@ public class NotificationExpandButton extends ImageView {
return mOriginalNotificationColor;
}
+ /**
+ * Set the button's color filter: to gray if true, otherwise colored.
+ * If this button has no original color, this has no effect.
+ */
+ public void setGrayedOut(boolean shouldApply) {
+ applyGrayTint(mContext, getDrawable(), shouldApply, mOriginalNotificationColor);
+ }
+
private void extendRectToMinTouchSize(Rect rect) {
int touchTargetSize = (int) (getResources().getDisplayMetrics().density * 48);
rect.left = rect.centerX() - touchTargetSize / 2;
@@ -80,4 +93,28 @@ public class NotificationExpandButton extends ImageView {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(Button.class.getName());
}
+
+ /**
+ * Update the button's drawable, content description, and color for the given expanded state.
+ */
+ @RemotableViewMethod
+ public void setExpanded(boolean expanded) {
+ mExpanded = expanded;
+ updateExpandButton();
+ }
+
+ private void updateExpandButton() {
+ int drawableId;
+ int contentDescriptionId;
+ if (mExpanded) {
+ drawableId = R.drawable.ic_collapse_notification;
+ contentDescriptionId = R.string.expand_button_content_description_expanded;
+ } else {
+ drawableId = R.drawable.ic_expand_notification;
+ contentDescriptionId = R.string.expand_button_content_description_collapsed;
+ }
+ setImageDrawable(getContext().getDrawable(drawableId));
+ setColorFilter(mOriginalNotificationColor);
+ setContentDescription(mContext.getText(contentDescriptionId));
+ }
}
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 0fc0451c6d4b..3e87cb5003f6 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -53,9 +53,11 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
queue->decStrong((void*)nativeCreate);
}
-static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr) {
+static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
+ jboolean includeSurfaceControlHandle) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
- return android_view_Surface_createFromSurface(env, queue->getSurface());
+ return android_view_Surface_createFromSurface(env,
+ queue->getSurface(includeSurfaceControlHandle));
}
static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
@@ -77,7 +79,7 @@ static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"nativeCreate", "(Ljava/lang/String;JJJZ)J", (void*)nativeCreate},
- {"nativeGetSurface", "(J)Landroid/view/Surface;", (void*)nativeGetSurface},
+ {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
{"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate},
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index e4d138d92621..d65b498404fa 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -640,7 +640,7 @@ android_glCompressedTexImage2D__IIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -684,7 +684,7 @@ android_glCompressedTexSubImage2D__IIIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -929,7 +929,7 @@ android_glDrawElements__IIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -2801,7 +2801,8 @@ android_glReadPixels__IIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3241,7 +3242,7 @@ android_glTexImage2D__IIIIIIIILjava_nio_Buffer_2
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3301,7 +3302,7 @@ android_glTexSubImage2D__IIIIIIIILjava_nio_Buffer_2
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 1069a1d3acb1..9724e6c2a5dd 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -464,7 +464,7 @@ android_glBufferData__IILjava_nio_Buffer_2I
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -509,7 +509,7 @@ android_glBufferSubData__IIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 86d7ecdce44d..1ffa4ec67ae1 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -893,7 +893,8 @@ android_glEGLImageTargetTexture2DOES__ILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, image, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)image - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -930,7 +931,8 @@ android_glEGLImageTargetRenderbufferStorageOES__ILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, image, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)image - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index 49baa51f2342..d832558aa368 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -599,7 +599,7 @@ android_glBufferData__IILjava_nio_Buffer_2I
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -644,7 +644,7 @@ android_glBufferSubData__IIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -758,7 +758,7 @@ android_glCompressedTexImage2D__IIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -802,7 +802,7 @@ android_glCompressedTexSubImage2D__IIIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -1379,7 +1379,7 @@ android_glDrawElements__IIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4273,7 +4273,8 @@ android_glReadPixels__IIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4380,7 +4381,7 @@ android_glShaderBinary__I_3IIILjava_nio_Buffer_2I
exit:
if (_array) {
- releasePointer(_env, _array, binary, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset), JNI_FALSE);
}
if (shaders_base) {
_env->ReleaseIntArrayElements(shaders_ref, (jint*)shaders_base,
@@ -4445,7 +4446,8 @@ android_glShaderBinary__ILjava_nio_IntBuffer_2ILjava_nio_Buffer_2I
exit:
if (_binaryArray) {
- releasePointer(_env, _binaryArray, binary, JNI_FALSE);
+ releasePointer(_env, _binaryArray, (void *)((char *)binary - _binaryBufferOffset),
+ JNI_FALSE);
}
if (_shadersArray) {
_env->ReleaseIntArrayElements(_shadersArray, (jint*)shaders, JNI_ABORT);
@@ -4568,7 +4570,7 @@ android_glTexImage2D__IIIIIIIILjava_nio_Buffer_2
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4816,7 +4818,7 @@ android_glTexSubImage2D__IIIIIIIILjava_nio_Buffer_2
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 32a2a24c2d2d..719c6b32fec6 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -463,7 +463,7 @@ android_glDrawRangeElements__IIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -516,7 +516,7 @@ android_glTexImage3D__IIIIIIIIILjava_nio_Buffer_2
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -580,7 +580,7 @@ android_glTexSubImage3D__IIIIIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -660,7 +660,7 @@ android_glCompressedTexImage3D__IIIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -723,7 +723,7 @@ android_glCompressedTexSubImage3D__IIIIIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -5445,7 +5445,8 @@ android_glGetProgramBinary__II_3II_3IILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, binary, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (binaryFormat_base) {
_env->ReleaseIntArrayElements(binaryFormat_ref, (jint*)binaryFormat_base,
@@ -5519,7 +5520,8 @@ android_glGetProgramBinary__IILjava_nio_IntBuffer_2Ljava_nio_IntBuffer_2Ljava_ni
exit:
if (_binaryArray) {
- releasePointer(_env, _binaryArray, binary, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _binaryArray, (void *)((char *)binary - _binaryBufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_binaryFormatArray) {
_env->ReleaseIntArrayElements(_binaryFormatArray, (jint*)binaryFormat, _exception ? JNI_ABORT : 0);
@@ -5564,7 +5566,7 @@ android_glProgramBinary__IILjava_nio_Buffer_2I
exit:
if (_array) {
- releasePointer(_env, _array, binary, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 07a794d0ef19..7ed754850ea3 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -863,7 +863,7 @@ android_glDrawElementsBaseVertex__IIILjava_nio_Buffer_2I
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -911,7 +911,7 @@ android_glDrawRangeElementsBaseVertex__IIIIILjava_nio_Buffer_2I
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -1048,7 +1048,8 @@ android_glReadnPixels__IIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 5c87f1973bed..3a1ccd9de79e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -313,7 +313,7 @@ static jlong nativeGetFromBlastBufferQueue(JNIEnv* env, jclass clazz, jlong nati
return nativeObject;
}
- sp<Surface> surface = queue->getSurface();
+ sp<Surface> surface = queue->getSurface(true /* includeSurfaceControlHandle */);
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner);
}
@@ -349,7 +349,8 @@ static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
sp<Surface> sur;
if (surfaceShim.graphicBufferProducer != nullptr) {
// we have a new IGraphicBufferProducer, create a new Surface for it
- sur = new Surface(surfaceShim.graphicBufferProducer, true);
+ sur = new Surface(surfaceShim.graphicBufferProducer, true,
+ surfaceShim.surfaceControlHandle);
// and keep a reference before passing to java
sur->incStrong(&sRefBaseOwner);
}
@@ -373,6 +374,7 @@ static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
android::view::Surface surfaceShim;
if (self != nullptr) {
surfaceShim.graphicBufferProducer = self->getIGraphicBufferProducer();
+ surfaceShim.surfaceControlHandle = self->getSurfaceControlHandle();
}
// Calling code in Surface.java has already written the name of the Surface
// to the Parcel
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a61903dcb7c8..62f844eb59a8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -318,8 +318,13 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
}
}
- status_t err = client->createSurfaceChecked(
- String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
+ sp<IBinder> parentHandle;
+ if (parent != nullptr) {
+ parentHandle = parent->getHandle();
+ }
+
+ status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,
+ flags, parentHandle, std::move(metadata));
if (err == NAME_NOT_FOUND) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index ffc1ddc03355..21de72397384 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -424,7 +424,7 @@ android_glCompressedTexImage2D__IIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -468,7 +468,7 @@ android_glCompressedTexSubImage2D__IIIIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -713,7 +713,7 @@ android_glDrawElements__IIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3488,7 +3488,8 @@ android_glReadPixels__IIIIIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3972,7 +3973,7 @@ android_glTexImage2D__IIIIIIIILjava_nio_Buffer_2
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4032,7 +4033,7 @@ android_glTexSubImage2D__IIIIIIIILjava_nio_Buffer_2
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4299,7 +4300,7 @@ android_glBufferData__IILjava_nio_Buffer_2I
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4344,7 +4345,7 @@ android_glBufferSubData__IIILjava_nio_Buffer_2
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 9fed1b95f6c3..fe65bda365af 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -511,9 +511,14 @@ message IncidentProto {
(section).args = "sensorservice --proto"
];
- optional com.android.server.powerstats.PowerStatsServiceProto powerstats = 3054 [
+ optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [
(section).type = SECTION_DUMPSYS,
- (section).args = "power_stats --proto"
+ (section).args = "power_stats --proto meter"
+ ];
+
+ optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "power_stats --proto model"
];
// Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index c80524402335..9a7ed7cbe98f 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -20,44 +20,99 @@ package com.android.server.powerstats;
option java_multiple_files = true;
-message IncidentReportProto {
+/**
+ * IncidentReportMeterProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
+ */
+message IncidentReportMeterProto {
+ /** Section number matches that in incident.proto */
+ optional PowerStatsServiceMeterProto incident_report = 3054;
+}
+
+/**
+ * IncidentReportModelProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
+ */
+message IncidentReportModelProto {
/** Section number matches that in incident.proto */
- optional PowerStatsServiceProto incident_report = 3054;
+ optional PowerStatsServiceModelProto incident_report = 3055;
}
-message PowerStatsServiceProto {
- repeated RailInfoProto rail_info = 1;
- repeated EnergyDataProto energy_data = 2;
+/**
+ * EnergyConsumer (model) data is exposed by the PowerStats HAL. This data
+ * represents modeled energy consumption estimates and is provided per
+ * subsystem. The default subsystems are defined in EnergyConsumerId.aidl.
+ * Energy model estimates will be logged to incident reports in addition to
+ * the raw energy meter data.
+ */
+message PowerStatsServiceModelProto {
+ repeated EnergyConsumerIdProto energy_consumer_id = 1;
+ repeated EnergyConsumerResultProto energy_consumer_result = 2;
}
/**
- * Rail information:
- * Reports information related to the rails being monitored.
+ * EnergyMeasurement (meter) data is exposed by the PowerStats HAL. This data
+ * represents measurements taken directly from on-device energy meters.
+ * This raw energy meter data will be logged to incident reports.
*/
-message RailInfoProto {
- /** Index corresponding to the rail */
- optional int32 index = 1;
+message PowerStatsServiceMeterProto {
+ repeated ChannelInfoProto channel_info = 1;
+ repeated EnergyMeasurementProto energy_measurement = 2;
+}
- /** Name of the rail (opaque to the framework) */
- optional string rail_name = 2;
+/**
+ * Energy consumer ID:
+ * A list of default subsystems for which energy consumption estimates
+ * may be provided (hardware dependent).
+ */
+message EnergyConsumerIdProto {
+ /** Unique index identifying the energy consumer. */
+ optional int32 energy_consumer_id = 1;
+}
- /** Name of the subsystem to which this rail belongs (opaque to the framework) */
- optional string subsys_name = 3;
+/**
+ * Energy consumer result:
+ * An estimate of energy consumption since boot for the subsystem identified
+ * by the unique energy_consumer_id.
+ */
+message EnergyConsumerResultProto {
+ /** Unique index identifying the energy consumer. */
+ optional int32 energy_consumer_id = 1;
+
+ /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+ optional int64 timestamp_ms = 2;
+
+ /** Accumulated energy since device boot in microwatt-seconds (uWs) */
+ optional int64 energy_uws = 3;
+}
+
+/**
+ * Channel information:
+ * Reports information related to the energy meter channels being monitored.
+ */
+message ChannelInfoProto {
+ /**
+ * Index corresponding to the energy meter channel. This index matches
+ * the index returned in ChannelInfo.
+ */
+ optional int32 channel_id = 1;
- /** Hardware sampling rate */
- optional int32 sampling_rate = 4;
+ /** Name of the energy meter channel */
+ optional string channel_name = 2;
}
/**
- * Rail level energy measurements:
- * Reports accumulated energy since boot on each rail.
+ * Energy measurements:
+ * Reports accumulated energy since boot for each energy meter.
*/
-message EnergyDataProto {
+message EnergyMeasurementProto {
/**
- * Index corresponding to the rail. This index matches
- * the index returned in RailInfo
+ * Index corresponding to the energy meter channel. This index matches
+ * the index returned in ChannelInfo.
*/
- optional int32 index = 1;
+ optional int32 channel_id = 1;
/** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
optional int64 timestamp_ms = 2;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 0affcea99706..ce52a352e10b 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -195,7 +195,7 @@ message DisplayContentProto {
repeated WindowTokenProto overlay_windows = 20 [deprecated=true];
optional DisplayAreaProto root_display_area = 21;
- optional bool single_task_instance = 22;
+ optional bool single_task_instance = 22 [deprecated=true];
optional int32 focused_root_task_id = 23;
optional .com.android.server.wm.IdentifierProto resumed_activity = 24;
repeated TaskProto tasks = 25 [deprecated=true];
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index d289e00d3467..55671091687c 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -129,6 +129,16 @@ message PackageProto {
optional bool is_loading = 2;
}
+ // TODO (b/170263003) refactor to permissiongr
+ // Runtime permission state that are granted for users.
+ message UserPermissionsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ // User id.
+ optional int32 id = 1;
+ // Pre-granted Android permissions.
+ repeated string granted_permissions = 2;
+ }
+
// Name of package. e.g. "com.android.providers.telephony".
optional string name = 1;
// UID for this package as assigned by Android OS.
@@ -152,4 +162,6 @@ message PackageProto {
optional InstallSourceProto install_source = 10;
// Whether the package is startable or is still loading
optional StatesProto states = 11;
+ // Granted runtime permissions for users.
+ repeated UserPermissionsProto user_permissions = 12;
}
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index d2c02769aa78..2546b5133c7e 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -232,13 +232,13 @@ enum SmsSendResultEnum {
// Data profile of the data call. From
// frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java
enum DataProfileEnum {
- DATA_PROFILE_INVALID = -1;
DATA_PROFILE_DEFAULT = 0;
DATA_PROFILE_TETHERED = 1;
DATA_PROFILE_IMS = 2;
DATA_PROFILE_FOTA = 3;
DATA_PROFILE_CBS = 4;
DATA_PROFILE_OEM_BASE = 1000;
+ DATA_PROFILE_INVALID = -1;
}
// Reason of data call deactivation. From
diff --git a/core/res/res/values-mcc310-mnc560-as/strings.xml b/core/res/res/values-mcc310-mnc560-as/strings.xml
deleted file mode 100644
index 13f3657f7057..000000000000
--- a/core/res/res/values-mcc310-mnc560-as/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 2006, 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="3526528316378889524">"ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="4618730283812066268">"ছিমৰ অনুমতি নাই MM#3"</string>
- <string name="mmcc_illegal_me" msgid="8522039751358990401">"ফ\'নৰ অনুমতি নাই MM#6"</string>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b4b634ab7b59..8d5129465b0e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1857,6 +1857,8 @@
<string name="config_defaultCallScreening" translatable="false"></string>
<!-- The name of the package that will hold the system gallery role. -->
<string name="config_systemGallery" translatable="false">com.android.gallery3d</string>
+ <!-- The names of the packages that will hold the automotive projection role. -->
+ <string name="config_systemAutomotiveProjection" translatable="false"></string>
<!-- The name of the package that will hold the system cluster service role. -->
<string name="config_systemAutomotiveCluster" translatable="false"></string>
<!-- The name of the package that will hold the system video call role. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 45bdff99fb9f..39989dd24a23 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3074,6 +3074,8 @@
<public name="config_systemAutomotiveCluster" />
<!-- @hide @SystemApi @TestApi -->
<public name="config_systemVideoCall" />
+ <!-- @hide @SystemApi @TestApi -->
+ <public name="config_systemAutomotiveProjection" />
</public-group>
<public-group type="id" first-id="0x01020055">
diff --git a/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml b/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
index 9a1f0ff7efb0..6a0c4215ce52 100644
--- a/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
+++ b/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
@@ -17,17 +17,14 @@
package="com.android.frameworks.coretests.install_decl_perm">
<permission android:name="com.android.frameworks.coretests.NORMAL"
- android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="normal"
android:label="test normal perm" />
<permission android:name="com.android.frameworks.coretests.DANGEROUS"
- android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="dangerous"
android:label="test dangerous perm" />
<permission android:name="com.android.frameworks.coretests.SIGNATURE"
- android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="signature"
android:label="test signature perm" />
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
new file mode 100644
index 000000000000..faa67a8bbd62
--- /dev/null
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public class CombinedVibrationEffectTest {
+
+ @Test
+ public void testValidateMono() {
+ CombinedVibrationEffect.createSynced(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> CombinedVibrationEffect.createSynced(new VibrationEffect.OneShot(-1, -1)));
+ }
+
+ @Test
+ public void testSerializationMono() {
+ CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+
+ Parcel parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CombinedVibrationEffect restored = CombinedVibrationEffect.CREATOR.createFromParcel(parcel);
+ assertEquals(original, restored);
+ }
+}
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index e6852929bbc2..c357414c8913 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -28,6 +28,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
import android.content.ContentInterface;
import android.content.ContentResolver;
@@ -100,6 +101,73 @@ public class VibrationEffectTest {
}
@Test
+ public void testValidateOneShot() {
+ VibrationEffect.createOneShot(1, 255).validate();
+ VibrationEffect.createOneShot(1, VibrationEffect.DEFAULT_AMPLITUDE).validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(-1, 255).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(0, 255).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(1, -2).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(1, 256).validate());
+ }
+
+ @Test
+ public void testValidatePrebaked() {
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK).validate();
+ VibrationEffect.createPredefined(VibrationEffect.RINGTONES[1]).validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createPredefined(-1).validate());
+ }
+
+ @Test
+ public void testValidateWaveform() {
+ VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1).validate();
+ VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0).validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(new long[0], new int[0], -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(TEST_TIMINGS, new int[0], -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(
+ new long[]{0, 0, 0}, TEST_AMPLITUDES, -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(
+ TEST_TIMINGS, new int[]{-1, -1, -2}, -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(
+ TEST_TIMINGS, TEST_AMPLITUDES, TEST_TIMINGS.length).validate());
+ }
+
+ @Test
+ public void testValidateComposed() {
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
+ .compose()
+ .validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.startComposition().addPrimitive(-1).compose().validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, -1, 10)
+ .compose()
+ .validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, -1)
+ .compose()
+ .validate());
+ }
+
+ @Test
public void testScalePrebaked_ignoresScaleAndReturnsSameEffect() {
VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]);
assertSame(initial, initial.scale(0.5f));
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 4e49118f438c..a0fc34923a4d 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -822,6 +822,25 @@ public class TextUtilsTest {
}
@Test
+ public void testFormatSimple_Width() {
+ assertEquals("42", formatSimple("%1d", 42));
+ assertEquals("42", formatSimple("%2d", 42));
+ assertEquals(" 42", formatSimple("%3d", 42));
+ assertEquals(" 42", formatSimple("%4d", 42));
+ assertEquals(" 42 42", formatSimple("%4d%4d", 42, 42));
+ assertEquals(" -42", formatSimple("%4d", -42));
+ assertEquals(" 42", formatSimple("%10d", 42));
+
+ assertEquals("42", formatSimple("%01d", 42));
+ assertEquals("42", formatSimple("%02d", 42));
+ assertEquals("042", formatSimple("%03d", 42));
+ assertEquals("0042", formatSimple("%04d", 42));
+ assertEquals("00420042", formatSimple("%04d%04d", 42, 42));
+ assertEquals("-042", formatSimple("%04d", -42));
+ assertEquals("0000000042", formatSimple("%010d", 42));
+ }
+
+ @Test
public void testFormatSimple_Empty() {
assertEquals("", formatSimple(""));
}
@@ -833,6 +852,15 @@ public class TextUtilsTest {
}
@Test
+ public void testFormatSimple_Advanced() {
+ assertEquals("000000000000002a.ext",
+ formatSimple("%016x.%s", 42, "ext"));
+ assertEquals("crtcl=0x002a:intrsv=Y:grnk=0x0018:gsmry=A:example:rnk=0x0000",
+ formatSimple("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
+ 42, 'Y', 24, 'A', "example", 0));
+ }
+
+ @Test
public void testFormatSimple_Mismatch() {
try {
formatSimple("%s");
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index b98ce308ae3b..873627eae696 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -109,7 +109,7 @@ public class InsetsAnimationControlImplTest {
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */, new LinearInterpolator(),
- 0 /* animationType */);
+ 0 /* animationType */, null /* translator */);
mController.mReadyDispatched = true;
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
new file mode 100644
index 000000000000..dfbc39c76489
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SurroundingTextTest {
+
+ @Test
+ public void testSurroundingTextBasicCreation() {
+ SurroundingText surroundingText1 = new SurroundingText("test", 0, 0, 0);
+ assertThat(surroundingText1.getText(), is("test"));
+ assertThat(surroundingText1.getSelectionStart(), is(0));
+ assertThat(surroundingText1.getSelectionEnd(), is(0));
+ assertThat(surroundingText1.getOffset(), is(0));
+
+ SurroundingText surroundingText2 = new SurroundingText("", -1, -1, -1);
+ assertThat(surroundingText2.getText(), is(""));
+ assertThat(surroundingText2.getSelectionStart(), is(-1));
+ assertThat(surroundingText2.getSelectionEnd(), is(-1));
+ assertThat(surroundingText2.getOffset(), is(-1));
+
+ SurroundingText surroundingText3 = new SurroundingText("hello", 0, 5, 0);
+ assertThat(surroundingText3.getText(), is("hello"));
+ assertThat(surroundingText3.getSelectionStart(), is(0));
+ assertThat(surroundingText3.getSelectionEnd(), is(5));
+ assertThat(surroundingText3.getOffset(), is(0));
+ }
+
+ @Test
+ public void testSurroundingTextWriteToParcel() {
+ SurroundingText surroundingText = new SurroundingText("text", 0, 1, 2);
+ Parcel parcel = Parcel.obtain();
+ surroundingText.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ SurroundingText surroundingTextFromParcel =
+ SurroundingText.CREATOR.createFromParcel(parcel);
+ assertThat(surroundingText.getText(), is("text"));
+ assertThat(surroundingText.getSelectionStart(), is(0));
+ assertThat(surroundingText.getSelectionEnd(), is(1));
+ assertThat(surroundingText.getOffset(), is(2));
+ assertThat(surroundingTextFromParcel.getText(), is("text"));
+ assertThat(surroundingTextFromParcel.getSelectionStart(), is(0));
+ assertThat(surroundingTextFromParcel.getSelectionEnd(), is(1));
+ assertThat(surroundingTextFromParcel.getOffset(), is(2));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 0eb34a993dec..3117935fb3ed 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -497,9 +497,9 @@ public class BinderCallsStatsTest {
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
PrintWriter pw = new PrintWriter(new StringWriter());
- bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), Process.INVALID_UID, true);
+ bcs.dump(pw, new AppIdToPackageMap(new SparseArray<>()), Process.INVALID_UID, true);
- bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), WORKSOURCE_UID, true);
+ bcs.dump(pw, new AppIdToPackageMap(new SparseArray<>()), WORKSOURCE_UID, true);
}
@Test
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index a4f206586625..cc68bb6333cb 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -28,6 +28,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -373,6 +374,25 @@ public class HdmiAudioSystemClientTest {
public void removeHdmiCecVolumeControlFeatureListener(
IHdmiCecVolumeControlFeatureListener listener) {
}
+
+ @Override
+ public List<String> getUserCecSettings() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List<String> getAllowedCecSettingValues(String name) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public String getCecSettingValue(String name) {
+ return "";
+ }
+
+ @Override
+ public void setCecSettingValue(String name, String value) {
+ }
}
}
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
index fa51d55ee8a1..37f64b5b8228 100644
--- a/data/etc/car/com.android.car.provision.xml
+++ b/data/etc/car/com.android.car.provision.xml
@@ -16,6 +16,10 @@
-->
<permissions>
<privapp-permissions package="com.android.car.provision">
+ <permission name="android.car.permission.CAR_POWERTRAIN"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.QUERY_ALL_PACKAGES"/>
+ <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.WRITE_SETTINGS"/>
</privapp-permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 977703dcd956..301b49181947 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -248,8 +248,7 @@
<library name="android.hidl.base-V1.0-java"
file="/system/framework/android.hidl.base-V1.0-java.jar" />
<library name="android.hidl.manager-V1.0-java"
- file="/system/framework/android.hidl.manager-V1.0-java.jar"
- dependency="android.hidl.base-V1.0-java" />
+ file="/system/framework/android.hidl.manager-V1.0-java.jar" />
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b19c8bfe1a6d..98c33705255a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -121,6 +121,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1973119651": {
+ "message": "SyncGroup %d: Adding to group: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-1963461591": {
"message": "Removing %s from %s",
"level": "VERBOSE",
@@ -157,14 +163,20 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1918702467": {
+ "message": "onSyncFinishedDrawing %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"-1915280162": {
"message": "Attempted to add wallpaper window with bad token %s. Aborting.",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1910833551": {
- "message": "SyncSet{%x:%d} Start for %s",
+ "-1905191109": {
+ "message": "SyncGroup %d: Finished!",
"level": "VERBOSE",
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
@@ -607,12 +619,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1387080937": {
- "message": "SyncSet{%x:%d} Child ready, now ready=%b and waiting on %d transactions",
- "level": "VERBOSE",
- "group": "WM_DEBUG_SYNC_ENGINE",
- "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
- },
"-1376035390": {
"message": "No task found",
"level": "DEBUG",
@@ -643,12 +649,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1340783230": {
- "message": "SyncSet{%x:%d} Added %s. now waiting on %d transactions",
- "level": "VERBOSE",
- "group": "WM_DEBUG_SYNC_ENGINE",
- "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
- },
"-1340540100": {
"message": "Creating SnapshotStartingData",
"level": "VERBOSE",
@@ -1177,12 +1177,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DragState.java"
},
- "-678300709": {
- "message": "SyncSet{%x:%d} Trying to add %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_SYNC_ENGINE",
- "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
- },
"-677449371": {
"message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b",
"level": "DEBUG",
@@ -1591,6 +1585,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-230587670": {
+ "message": "SyncGroup %d: Unfinished container: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-198463978": {
"message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
"level": "VERBOSE",
@@ -2089,6 +2089,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "371173718": {
+ "message": "finishSync cancel=%b for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"371641947": {
"message": "Window Manager Crash %s",
"level": "WTF",
@@ -2233,6 +2239,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "550717438": {
+ "message": "SyncGroup %d: Started for listener: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"556758086": {
"message": "Applying new update lock state '%s' for %s",
"level": "DEBUG",
@@ -2269,12 +2281,6 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
- "590184240": {
- "message": "- NOT adding to sync: visible=%b hasListener=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_SYNC_ENGINE",
- "at": "com\/android\/server\/wm\/WindowContainer.java"
- },
"594260577": {
"message": "createWallpaperAnimations()",
"level": "DEBUG",
@@ -2599,6 +2605,18 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
+ "959486822": {
+ "message": "setSyncGroup #%d on %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
+ "966569777": {
+ "message": "SyncGroup %d: onSurfacePlacement checking %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"969323241": {
"message": "Sending new config to %s, config: %s",
"level": "VERBOSE",
@@ -2623,12 +2641,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "1000601037": {
- "message": "SyncSet{%x:%d} Set ready",
- "level": "VERBOSE",
- "group": "WM_DEBUG_SYNC_ENGINE",
- "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
- },
"1001509841": {
"message": "Auto-PIP allowed, entering PIP mode directly: %s",
"level": "DEBUG",
@@ -3163,6 +3175,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1689989893": {
+ "message": "SyncGroup %d: Set ready",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"1696210756": {
"message": "Launch on display check: allow launch on public display",
"level": "DEBUG",
@@ -3391,12 +3409,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "2001924866": {
- "message": "SyncSet{%x:%d} Finished. Reporting %d containers to %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_SYNC_ENGINE",
- "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
- },
"2016061474": {
"message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
"level": "VERBOSE",
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
index d5243164abdc..c29a095ecc1b 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
@@ -43,10 +43,10 @@ import com.sun.source.tree.Tree;
*/
@AutoService(BugChecker.class)
@BugPattern(
- name = "AndroidFrameworkParcelablePerformance",
+ name = "AndroidFrameworkEfficientParcelable",
summary = "Verifies Parcelable performance best-practices",
severity = WARNING)
-public final class ParcelablePerformanceChecker extends BugChecker
+public final class EfficientParcelableChecker extends BugChecker
implements MethodInvocationTreeMatcher {
private static final Matcher<Tree> INSIDE_WRITE_TO_PARCEL = allOf(
enclosingClass(isSubtypeOf("android.os.Parcelable")),
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
index 3fbd51deec65..3a0fbd33933f 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
@@ -18,7 +18,11 @@ package com.google.errorprone.bugpatterns.android;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.hasModifier;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static com.google.errorprone.matchers.Matchers.kindIs;
import static com.google.errorprone.matchers.Matchers.methodInvocation;
import static com.google.errorprone.matchers.Matchers.not;
@@ -28,19 +32,28 @@ import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.CompoundAssignmentTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.predicates.TypePredicate;
import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Type;
import java.util.List;
+import java.util.Objects;
+
+import javax.lang.model.element.Modifier;
/**
* Android offers several efficient alternatives to some upstream {@link String}
@@ -52,7 +65,7 @@ import java.util.List;
summary = "Verifies efficient Strings best-practices",
severity = WARNING)
public final class EfficientStringsChecker extends BugChecker
- implements MethodInvocationTreeMatcher {
+ implements MethodInvocationTreeMatcher, NewClassTreeMatcher, CompoundAssignmentTreeMatcher {
private static final Matcher<ExpressionTree> FORMAT_CALL = methodInvocation(
staticMethod().onClass("java.lang.String").named("format"));
@@ -60,17 +73,37 @@ public final class EfficientStringsChecker extends BugChecker
staticMethod().onClass(withSimpleName("Preconditions")).withAnyName());
private static final Matcher<ExpressionTree> OBJECTS_CALL = methodInvocation(
staticMethod().onClass("java.util.Objects").named("requireNonNull"));
+ private static final Matcher<ExpressionTree> APPEND_CALL = methodInvocation(
+ instanceMethod().onExactClass("java.lang.StringBuilder")
+ .withSignature("append(java.lang.String)"));
+
+ /**
+ * Identify any dynamic values that will likely cause us to allocate a
+ * transparent StringBuilder.
+ */
+ private static final Matcher<ExpressionTree> DYNAMIC_VALUE = anyOf(
+ allOf(kindIs(Kind.MEMBER_SELECT),
+ not(allOf(hasModifier(Modifier.STATIC), hasModifier(Modifier.FINAL)))),
+ allOf(kindIs(Kind.IDENTIFIER),
+ not(allOf(hasModifier(Modifier.STATIC), hasModifier(Modifier.FINAL)))),
+ kindIs(Kind.METHOD_INVOCATION));
+
+ /**
+ * Identify an expression that is either a direct "+" binary operator, or
+ * that contains a "+" binary operator nested deep inside.
+ */
+ private static final Matcher<Tree> PLUS = anyOf(kindIs(Kind.PLUS),
+ contains(BinaryTree.class, kindIs(Kind.PLUS)));
/**
- * Match an expression which combines both string literals any other dynamic
- * values, since these allocate a transparent StringBuilder.
- * <p>
- * This won't match a single isolated string literal, or a chain consisting
- * of only string literals, since those don't require dynamic construction.
+ * Identify an expression that is using a "+" binary operator to combine
+ * dynamic values, which will likely end up allocating a transparent
+ * {@link StringBuilder}.
*/
- private static final Matcher<ExpressionTree> CONTAINS_DYNAMIC_STRING = allOf(
- contains(ExpressionTree.class, kindIs(Kind.STRING_LITERAL)),
- contains(ExpressionTree.class, not(kindIs(Kind.STRING_LITERAL))));
+ private static final Matcher<Tree> PLUS_DYNAMIC_VALUE = allOf(
+ PLUS, contains(ExpressionTree.class, DYNAMIC_VALUE));
+
+ private static final Matcher<Tree> IS_STRING_BUFFER = isSubtypeOf("java.lang.StringBuffer");
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
@@ -98,9 +131,9 @@ public final class EfficientStringsChecker extends BugChecker
} else if (PRECONDITIONS_CALL.matches(tree, state)
|| OBJECTS_CALL.matches(tree, state)) {
final List<? extends ExpressionTree> args = tree.getArguments();
- for (int i = 1 ; i < args.size(); i++) {
- final ExpressionTree arg = args.get(i);
- if (CONTAINS_DYNAMIC_STRING.matches(arg, state)) {
+ if (args.size() > 1) {
+ final ExpressionTree arg = args.get(1);
+ if (PLUS_DYNAMIC_VALUE.matches(arg, state)) {
return buildDescription(arg)
.setMessage("Building dynamic messages is discouraged, since they "
+ "always allocate a transparent StringBuilder, even in "
@@ -108,6 +141,37 @@ public final class EfficientStringsChecker extends BugChecker
.build();
}
}
+ } else if (APPEND_CALL.matches(tree, state)) {
+ final ExpressionTree arg = tree.getArguments().get(0);
+ if (PLUS_DYNAMIC_VALUE.matches(arg, state)) {
+ return buildDescription(arg)
+ .setMessage("Call append() directly for each argument instead of "
+ + "allocating a transparent StringBuilder")
+ .build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+
+ @Override
+ public Description matchNewClass(NewClassTree tree, VisitorState state) {
+ if (IS_STRING_BUFFER.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Strongly encouraged to replace with StringBuilder "
+ + "which avoids synchronization overhead")
+ .build();
+ }
+ return Description.NO_MATCH;
+ }
+
+ @Override
+ public Description matchCompoundAssignment(CompoundAssignmentTree tree, VisitorState state) {
+ if (tree.getKind() == Kind.PLUS_ASSIGNMENT && "java.lang.String"
+ .equals(String.valueOf(ASTHelpers.getType(tree.getVariable())))) {
+ return buildDescription(tree)
+ .setMessage("Strongly encouraged to replace with StringBuilder "
+ + "which avoids transparent StringBuilder allocations")
+ .build();
}
return Description.NO_MATCH;
}
@@ -116,8 +180,10 @@ public final class EfficientStringsChecker extends BugChecker
for (int i = 0; i < format.length(); i++) {
char c = format.charAt(i);
if (c == '%') {
- i++;
- c = format.charAt(i);
+ c = format.charAt(++i);
+ while ('0' <= c && c <= '9') {
+ c = format.charAt(++i);
+ }
switch (c) {
case 'b':
case 'c':
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientParcelableCheckerTest.java
index 75c76e32f00e..a40414bc1cef 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientParcelableCheckerTest.java
@@ -24,13 +24,13 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
-public class ParcelablePerformanceCheckerTest {
+public class EfficientParcelableCheckerTest {
private CompilationTestHelper compilationHelper;
@Before
public void setUp() {
compilationHelper = CompilationTestHelper.newInstance(
- ParcelablePerformanceChecker.class, getClass());
+ EfficientParcelableChecker.class, getClass());
}
@Test
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
index a755564d52dd..48e4ad11f9bf 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
@@ -41,9 +41,13 @@ public class EfficientStringsCheckerTest {
assertTrue(EfficientStringsChecker.isSimple(""));
assertTrue(EfficientStringsChecker.isSimple("%s"));
assertTrue(EfficientStringsChecker.isSimple("String %s%s and %%%% number %d%d together"));
+ assertTrue(EfficientStringsChecker.isSimple("%04d"));
+ assertTrue(EfficientStringsChecker.isSimple("%02x:%02x:%02x"));
+ assertTrue(EfficientStringsChecker.isSimple("%10d"));
- assertFalse(EfficientStringsChecker.isSimple("%04d"));
- assertFalse(EfficientStringsChecker.isSimple("%02x:%02x:%02x"));
+ assertFalse(EfficientStringsChecker.isSimple("%0.4f"));
+ assertFalse(EfficientStringsChecker.isSimple("%t"));
+ assertFalse(EfficientStringsChecker.isSimple("%1$s"));
}
@Test
@@ -58,6 +62,7 @@ public class EfficientStringsCheckerTest {
" String.format(\"foo %s bar\", str);",
" // BUG: Diagnostic contains:",
" String.format(\"foo %d bar\", 42);",
+ " // BUG: Diagnostic contains:",
" String.format(\"foo %04d bar\", 42);",
" }",
" public void exampleLocale(String str) {",
@@ -66,6 +71,7 @@ public class EfficientStringsCheckerTest {
" String.format(Locale.US, \"foo %s bar\", str);",
" // BUG: Diagnostic contains:",
" String.format(Locale.US, \"foo %d bar\", 42);",
+ " // BUG: Diagnostic contains:",
" String.format(Locale.US, \"foo %04d bar\", 42);",
" }",
"}")
@@ -116,4 +122,126 @@ public class EfficientStringsCheckerTest {
"}")
.doTest();
}
+
+ @Test
+ public void testPreconditions_Complex() {
+ compilationHelper
+ .addSourceFile("/android/util/Preconditions.java")
+ .addSourceLines("Example.java",
+ "import android.util.Preconditions;",
+ "public class Example {",
+ " String[] classArray = new String[] { null };",
+ " String classVar;",
+ " static final String CONST_VAR = \"baz\";",
+ " public String classMethod() { return \"baz\"; }",
+ " public static final String CONST_METHOD() { return \"baz\"; }",
+ " public void checkNotNull(Example example, Object val) {",
+ " String methodVar = \"baz\";",
+ " Preconditions.checkNotNull(val, \"foo\");",
+ " Preconditions.checkNotNull(val, (\"foo\"));",
+ " Preconditions.checkNotNull(val, classArray[0]);",
+ " Preconditions.checkNotNull(val, classVar);",
+ " Preconditions.checkNotNull(val, CONST_VAR);",
+ " Preconditions.checkNotNull(val, example.classVar);",
+ " Preconditions.checkNotNull(val, Example.CONST_VAR);",
+ " Preconditions.checkNotNull(val, methodVar);",
+ " Preconditions.checkNotNull(val, classMethod());",
+ " Preconditions.checkNotNull(val, CONST_METHOD());",
+ " Preconditions.checkNotNull(val, \"foo\" + \"bar\");",
+ " Preconditions.checkNotNull(val, (\"foo\" + \"bar\"));",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkNotNull(val, \"foo\" + classArray[0]);",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkNotNull(val, \"foo\" + classVar);",
+ " Preconditions.checkNotNull(val, \"foo\" + CONST_VAR);",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkNotNull(val, \"foo\" + methodVar);",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkNotNull(val, \"foo\" + classMethod());",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkNotNull(val, \"foo\" + CONST_METHOD());",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStringBuffer() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "public class Example {",
+ " public void example() {",
+ " // BUG: Diagnostic contains:",
+ " StringBuffer sb = new StringBuffer();",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStringBuilder() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "public class Example {",
+ " StringBuilder sb = new StringBuilder();",
+ " String[] classArray = new String[] { null };",
+ " String classVar;",
+ " static final String CONST_VAR = \"baz\";",
+ " public String classMethod() { return \"baz\"; }",
+ " public static final String CONST_METHOD() { return \"baz\"; }",
+ " public void generic(Example example) {",
+ " sb.append(\"foo\");",
+ " sb.append(\"foo\" + \"bar\");",
+ " sb.append(classArray[0]);",
+ " sb.append(example.classArray[0]);",
+ " sb.append(classVar);",
+ " sb.append(CONST_VAR);",
+ " sb.append(example.classVar);",
+ " sb.append(Example.CONST_VAR);",
+ " sb.append(classMethod());",
+ " sb.append(CONST_METHOD());",
+ " }",
+ " public void string(String val) {",
+ " sb.append(\"foo\").append(val);",
+ " sb.append(\"foo\").append(val != null ? \"bar\" : \"baz\");",
+ " // BUG: Diagnostic contains:",
+ " sb.append(\"foo\" + val);",
+ " }",
+ " public void number(int val) {",
+ " sb.append(\"foo\").append(val);",
+ " sb.append(\"foo\").append(val + val);",
+ " sb.append(\"foo\").append(val > 0 ? \"bar\" : \"baz\");",
+ " // BUG: Diagnostic contains:",
+ " sb.append(\"foo\" + val);",
+ " // BUG: Diagnostic contains:",
+ " sb.append(\"foo\" + String.valueOf(val));",
+ " // BUG: Diagnostic contains:",
+ " sb.append(\"foo\" + Integer.toString(val));",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testPlusAssignment() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "public class Example {",
+ " public void string(String val) {",
+ " String s = \"foo\";",
+ " // BUG: Diagnostic contains:",
+ " s += \"bar\";",
+ " // BUG: Diagnostic contains:",
+ " s += val;",
+ " // BUG: Diagnostic contains:",
+ " s += (\"bar\" + \"baz\");",
+ " }",
+ " public void number(int val) {",
+ " int other = 42;",
+ " other += 24;",
+ " other += val;",
+ " }",
+ "}")
+ .doTest();
+ }
}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 8284042c4dca..94bfdc9dbad6 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -29,7 +29,7 @@ public final class BLASTBufferQueue {
private static native long nativeCreate(String name, long surfaceControl, long width,
long height, boolean tripleBufferingEnabled);
private static native void nativeDestroy(long ptr);
- private static native Surface nativeGetSurface(long ptr);
+ private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
private static native void nativeFlushShadowQueue(long ptr);
@@ -49,7 +49,15 @@ public final class BLASTBufferQueue {
* @return a new Surface instance from the IGraphicsBufferProducer of the adapter.
*/
public Surface createSurface() {
- return nativeGetSurface(mNativeObject);
+ return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);
+ }
+
+ /**
+ * @return a new Surface instance from the IGraphicsBufferProducer of the adapter and
+ * the SurfaceControl handle.
+ */
+ public Surface createSurfaceWithHandle() {
+ return nativeGetSurface(mNativeObject, true /* includeSurfaceControlHandle */);
}
public void setNextTransaction(SurfaceControl.Transaction t) {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index a2dd9a8322b6..ecb0ff43a9e2 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -768,7 +768,7 @@ public class Typeface {
public @NonNull CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
Preconditions.checkNotNull(family);
Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(),
- "Custom fallback limit exceeded(" + getMaxCustomFallbackCount() + ")");
+ "Custom fallback limit exceeded(%d)", getMaxCustomFallbackCount());
mFamilies.add(family);
return this;
}
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 90412f417f38..abf0e8a1fe89 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -39,6 +39,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -441,10 +442,11 @@ public final class Icon implements Parcelable {
resPackage = context.getPackageName();
}
if (getResources() == null && !(getResPackage().equals("android"))) {
- final PackageManager pm = context.getPackageManager();
+ final PackageManager pm = context.createContextAsUser(
+ UserHandle.of(userId), /* flags */ 0).getPackageManager();
try {
// assign getResources() as the correct user
- mObj1 = pm.getResourcesForApplicationAsUser(resPackage, userId);
+ mObj1 = pm.getResourcesForApplication(resPackage);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, String.format("Unable to find pkg=%s user=%d",
getResPackage(),
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index b6d8fa19fca8..31c3d09ed5b1 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -80,11 +80,11 @@ public class MeasuredText {
public @FloatRange(from = 0.0) @Px float getWidth(
@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(" + start + ") must be 0 <= start <= " + mChars.length);
+ "start(%d) must be 0 <= start <= %d", start, mChars.length);
Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(" + end + ") must be 0 <= end <= " + mChars.length);
+ "end(%d) must be 0 <= end <= %d", end, mChars.length);
Preconditions.checkArgument(start <= end,
- "start(" + start + ") is larger than end(" + end + ")");
+ "start(%d) is larger than end(%d)", start, end);
return nGetWidth(mNativePtr, start, end);
}
@@ -107,11 +107,11 @@ public class MeasuredText {
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect rect) {
Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(" + start + ") must be 0 <= start <= " + mChars.length);
+ "start(%d) must be 0 <= start <= %d", start, mChars.length);
Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(" + end + ") must be 0 <= end <= " + mChars.length);
+ "end(%d) must be 0 <= end <= %d", end, mChars.length);
Preconditions.checkArgument(start <= end,
- "start(" + start + ") is larger than end(" + end + ")");
+ "start(%d) is larger than end(%d)", start, end);
Preconditions.checkNotNull(rect);
nGetBounds(mNativePtr, mChars, start, end, rect);
}
@@ -123,7 +123,7 @@ public class MeasuredText {
*/
public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) {
Preconditions.checkArgument(0 <= offset && offset < mChars.length,
- "offset(" + offset + ") is larger than text length: " + mChars.length);
+ "offset(%d) is larger than text length %d" + offset, mChars.length);
return nGetCharWidthAt(mNativePtr, offset);
}
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index ecbc45c54d85..c2de0acebca9 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -170,9 +170,8 @@ public final class PositionedGlyphs {
* @hide
*
* @param layoutPtr the address of native layout object.
- * @param paint a paint object
*/
- public PositionedGlyphs(long layoutPtr, @NonNull Paint paint, float xOffset, float yOffset) {
+ public PositionedGlyphs(long layoutPtr, float xOffset, float yOffset) {
mLayoutPtr = layoutPtr;
int glyphCount = nGetGlyphCount(layoutPtr);
mFonts = new ArrayList<>(glyphCount);
diff --git a/graphics/java/android/graphics/text/TextRunShaper.java b/graphics/java/android/graphics/text/TextRunShaper.java
index b73436e36ae0..8459e7bfe191 100644
--- a/graphics/java/android/graphics/text/TextRunShaper.java
+++ b/graphics/java/android/graphics/text/TextRunShaper.java
@@ -72,7 +72,7 @@ public class TextRunShaper {
return new PositionedGlyphs(
nativeShapeTextRun(text, start, count, contextStart, contextCount, isRtl,
paint.getNativeInstance()),
- paint, xOffset, yOffset);
+ xOffset, yOffset);
}
/**
@@ -104,7 +104,7 @@ public class TextRunShaper {
nativeShapeTextRun(
(String) text, start, count, contextStart, contextCount, isRtl,
paint.getNativeInstance()),
- paint, xOffset, yOffset);
+ xOffset, yOffset);
} else {
char[] buf = new char[contextCount];
TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
@@ -112,7 +112,7 @@ public class TextRunShaper {
nativeShapeTextRun(
buf, start - contextStart, count,
0, contextCount, isRtl, paint.getNativeInstance()),
- paint, xOffset, yOffset);
+ xOffset, yOffset);
}
}
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 02bf38504725..3f6ca0fc5246 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -31,12 +31,6 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
- "-1312360667": {
- "message": "createRootTask() displayId=%d winMode=%d listener=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
"-880817403": {
"message": "Task vanished taskId=%d",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 4cb5fd139259..5bd693a9311e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -20,7 +20,9 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCR
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
import android.app.ActivityManager;
-import android.util.ArraySet;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.util.ArrayMap;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -37,7 +39,7 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private final SyncTransactionQueue mSyncQueue;
- private final ArraySet<Integer> mTasks = new ArraySet<>();
+ private final ArrayMap<Integer, SurfaceControl> mTasks = new ArrayMap<>();
FullscreenTaskListener(SyncTransactionQueue syncQueue) {
mSyncQueue = syncQueue;
@@ -46,16 +48,16 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
synchronized (mTasks) {
- if (mTasks.contains(taskInfo.taskId)) {
+ if (mTasks.containsKey(taskInfo.taskId)) {
throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
- mTasks.add(taskInfo.taskId);
+ mTasks.put(taskInfo.taskId, leash);
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state).
- t.setPosition(leash, 0, 0);
+ updateSurfacePosition(t, taskInfo, leash);
t.setWindowCrop(leash, null);
// TODO(shell-transitions): Eventually set everything in transition so there's no
// SF Transaction here.
@@ -71,7 +73,7 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
synchronized (mTasks) {
- if (!mTasks.remove(taskInfo.taskId)) {
+ if (mTasks.remove(taskInfo.taskId) == null) {
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
}
@@ -81,6 +83,23 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
}
@Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ synchronized (mTasks) {
+ if (!mTasks.containsKey(taskInfo.taskId)) {
+ Slog.e(TAG, "Changed Task wasn't appeared or already vanished: #"
+ + taskInfo.taskId);
+ return;
+ }
+ final SurfaceControl leash = mTasks.get(taskInfo.taskId);
+ mSyncQueue.runInSync(t -> {
+ // Reposition the task in case the bounds has been changed (such as Task level
+ // letterboxing).
+ updateSurfacePosition(t, taskInfo, leash);
+ });
+ }
+ }
+
+ @Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
@@ -92,4 +111,13 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
+
+ /** Places the Task surface to the latest position. */
+ private static void updateSurfacePosition(SurfaceControl.Transaction t,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ // TODO(170725334) drop this after ag/12876439
+ final Configuration config = taskInfo.getConfiguration();
+ final Rect bounds = config.windowConfiguration.getBounds();
+ t.setPosition(leash, bounds.left, bounds.top);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 8bd7193843f7..cbc1c8d6d310 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -105,6 +105,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
// TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks
private final Transitions mTransitions;
+ private final Object mLock = new Object();
+
public ShellTaskOrganizer(SyncTransactionQueue syncQueue, TransactionPool transactionPool,
ShellExecutor mainExecutor, ShellExecutor animExecutor) {
this(null, syncQueue, transactionPool, mainExecutor, animExecutor);
@@ -114,7 +116,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
SyncTransactionQueue syncQueue, TransactionPool transactionPool,
ShellExecutor mainExecutor, ShellExecutor animExecutor) {
- super(taskOrganizerController);
+ super(taskOrganizerController, mainExecutor);
addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
@@ -122,68 +124,62 @@ public class ShellTaskOrganizer extends TaskOrganizer {
@Override
public List<TaskAppearedInfo> registerOrganizer() {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Registering organizer");
- final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
- for (int i = 0; i < taskInfos.size(); i++) {
- final TaskAppearedInfo info = taskInfos.get(i);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
- info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
- onTaskAppeared(info);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Registering organizer");
+ final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ final TaskAppearedInfo info = taskInfos.get(i);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
+ info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
+ onTaskAppeared(info);
+ }
+ return taskInfos;
}
- return taskInfos;
- }
-
- public TaskAppearedInfo createRootTask(
- int displayId, int windowingMode, TaskListener listener) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
- "createRootTask() displayId=%d winMode=%d listener=%s",
- displayId, windowingMode, listener.toString());
- final TaskAppearedInfo info = super.createRootTask(displayId, windowingMode);
-
- // Add the listener and send the task appeared signal
- mTaskListeners.put(info.getTaskInfo().taskId, listener);
- onTaskAppeared(info);
- return info;
}
/**
* Adds a listener for a specific task id.
*/
public void addListenerForTaskId(TaskListener listener, int taskId) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
- if (mTaskListeners.get(taskId) != null) {
- throw new IllegalArgumentException("Listener for taskId=" + taskId + " already exists");
- }
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
+ if (mTaskListeners.get(taskId) != null) {
+ throw new IllegalArgumentException(
+ "Listener for taskId=" + taskId + " already exists");
+ }
- final TaskAppearedInfo info = mTasks.get(taskId);
- if (info == null) {
- throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
- }
+ final TaskAppearedInfo info = mTasks.get(taskId);
+ if (info == null) {
+ throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
+ }
- final TaskListener oldListener = getTaskListener(info.getTaskInfo());
- mTaskListeners.put(taskId, listener);
- updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
+ final TaskListener oldListener = getTaskListener(info.getTaskInfo());
+ mTaskListeners.put(taskId, listener);
+ updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
+ }
}
/**
* Adds a listener for tasks with given types.
*/
public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
- Arrays.toString(listenerTypes), listener);
- for (int listenerType : listenerTypes) {
- if (mTaskListeners.get(listenerType) != null) {
- throw new IllegalArgumentException("Listener for listenerType=" + listenerType
- + " already exists");
- }
- mTaskListeners.put(listenerType, listener);
-
- // Notify the listener of all existing tasks with the given type.
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = mTasks.valueAt(i);
- final TaskListener taskListener = getTaskListener(data.getTaskInfo());
- if (taskListener != listener) continue;
- listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
+ Arrays.toString(listenerTypes), listener);
+ for (int listenerType : listenerTypes) {
+ if (mTaskListeners.get(listenerType) != null) {
+ throw new IllegalArgumentException("Listener for listenerType=" + listenerType
+ + " already exists");
+ }
+ mTaskListeners.put(listenerType, listener);
+
+ // Notify the listener of all existing tasks with the given type.
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = mTasks.valueAt(i);
+ final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+ if (taskListener != listener) continue;
+ listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
+ }
}
}
}
@@ -192,30 +188,32 @@ public class ShellTaskOrganizer extends TaskOrganizer {
* Removes a registered listener.
*/
public void removeListener(TaskListener listener) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
- final int index = mTaskListeners.indexOfValue(listener);
- if (index == -1) {
- Log.w(TAG, "No registered listener found");
- return;
- }
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
+ final int index = mTaskListeners.indexOfValue(listener);
+ if (index == -1) {
+ Log.w(TAG, "No registered listener found");
+ return;
+ }
- // Collect tasks associated with the listener we are about to remove.
- final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = mTasks.valueAt(i);
- final TaskListener taskListener = getTaskListener(data.getTaskInfo());
- if (taskListener != listener) continue;
- tasks.add(data);
- }
+ // Collect tasks associated with the listener we are about to remove.
+ final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = mTasks.valueAt(i);
+ final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+ if (taskListener != listener) continue;
+ tasks.add(data);
+ }
- // Remove listener
- mTaskListeners.removeAt(index);
+ // Remove listener
+ mTaskListeners.removeAt(index);
- // Associate tasks with new listeners if needed.
- for (int i = tasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = tasks.get(i);
- updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
- null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
+ // Associate tasks with new listeners if needed.
+ for (int i = tasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = tasks.get(i);
+ updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
+ null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
+ }
}
}
@@ -224,12 +222,16 @@ public class ShellTaskOrganizer extends TaskOrganizer {
* appears.
*/
public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) {
- mLaunchCookieToListener.put(cookie, listener);
+ synchronized (mLock) {
+ mLaunchCookieToListener.put(cookie, listener);
+ }
}
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
- onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
+ synchronized (mLock) {
+ onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
+ }
}
private void onTaskAppeared(TaskAppearedInfo info) {
@@ -245,35 +247,41 @@ public class ShellTaskOrganizer extends TaskOrganizer {
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
- final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
- final TaskListener oldListener = getTaskListener(data.getTaskInfo());
- final TaskListener newListener = getTaskListener(taskInfo);
- mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
- final boolean updated = updateTaskListenerIfNeeded(
- taskInfo, data.getLeash(), oldListener, newListener);
- if (!updated && newListener != null) {
- newListener.onTaskInfoChanged(taskInfo);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
+ final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
+ final TaskListener oldListener = getTaskListener(data.getTaskInfo());
+ final TaskListener newListener = getTaskListener(taskInfo);
+ mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
+ final boolean updated = updateTaskListenerIfNeeded(
+ taskInfo, data.getLeash(), oldListener, newListener);
+ if (!updated && newListener != null) {
+ newListener.onTaskInfoChanged(taskInfo);
+ }
}
}
@Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
- final TaskListener listener = getTaskListener(taskInfo);
- if (listener != null) {
- listener.onBackPressedOnTaskRoot(taskInfo);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
+ final TaskListener listener = getTaskListener(taskInfo);
+ if (listener != null) {
+ listener.onBackPressedOnTaskRoot(taskInfo);
+ }
}
}
@Override
public void onTaskVanished(RunningTaskInfo taskInfo) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
- final int taskId = taskInfo.taskId;
- final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
- mTasks.remove(taskId);
- if (listener != null) {
- listener.onTaskVanished(taskInfo);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
+ final int taskId = taskInfo.taskId;
+ final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
+ mTasks.remove(taskId);
+ if (listener != null) {
+ listener.onTaskVanished(taskInfo);
+ }
}
}
@@ -368,32 +376,34 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + TAG);
- pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
- for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
- final int key = mTaskListeners.keyAt(i);
- final TaskListener listener = mTaskListeners.valueAt(i);
- pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
- listener.dump(pw, childPrefix);
- }
+ synchronized (mLock) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
+ for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
+ final int key = mTaskListeners.keyAt(i);
+ final TaskListener listener = mTaskListeners.valueAt(i);
+ pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
+ listener.dump(pw, childPrefix);
+ }
- pw.println();
- pw.println(innerPrefix + mTasks.size() + " Tasks");
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final int key = mTasks.keyAt(i);
- final TaskAppearedInfo info = mTasks.valueAt(i);
- final TaskListener listener = getTaskListener(info.getTaskInfo());
- pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
- }
+ pw.println();
+ pw.println(innerPrefix + mTasks.size() + " Tasks");
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final int key = mTasks.keyAt(i);
+ final TaskAppearedInfo info = mTasks.valueAt(i);
+ final TaskListener listener = getTaskListener(info.getTaskInfo());
+ pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
+ }
- pw.println();
- pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
- for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
- final IBinder key = mLaunchCookieToListener.keyAt(i);
- final TaskListener listener = mLaunchCookieToListener.valueAt(i);
- pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
+ pw.println();
+ pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
+ for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
+ final IBinder key = mLaunchCookieToListener.keyAt(i);
+ final TaskListener listener = mLaunchCookieToListener.valueAt(i);
+ pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index eaf5d7960aa9..24381d937e2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -270,25 +270,6 @@ public class SystemWindows {
mDisplayId = displayId;
}
- @Override
- public int relayout(IWindow window, WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewVisibility, int flags,
- long frameNumber, ClientWindowFrames outFrames,
- MergedConfiguration mergedConfiguration,
- SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
- int res = super.relayout(window, attrs, requestedWidth, requestedHeight,
- viewVisibility, flags, frameNumber, outFrames,
- mergedConfiguration, outSurfaceControl, outInsetsState,
- outActiveControls, outSurfaceSize);
- if (res != 0) {
- return res;
- }
- DisplayLayout dl = mDisplayController.getDisplayLayout(mDisplayId);
- outFrames.stableInsets.set(dl.stableInsets());
- return 0;
- }
-
void updateConfiguration(Configuration configuration) {
setConfiguration(configuration);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
index 6d6cc204538a..08318186a105 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
@@ -60,7 +60,6 @@ public class PipBoundsHandler {
private float mDefaultAspectRatio;
private float mMinAspectRatio;
private float mMaxAspectRatio;
- private float mAspectRatio;
private int mDefaultStackGravity;
private int mDefaultMinSize;
private Point mScreenEdgeInsets;
@@ -80,7 +79,7 @@ public class PipBoundsHandler {
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
// resources as it would clobber mAspectRatio when entering PiP from fullscreen which
// triggers a configuration change and the resources to be reloaded.
- mAspectRatio = mDefaultAspectRatio;
+ mPipBoundsState.setAspectRatio(mDefaultAspectRatio);
}
/**
@@ -133,10 +132,6 @@ public class PipBoundsHandler {
mCurrentMinSize = minEdgeSize;
}
- protected float getAspectRatio() {
- return mAspectRatio;
- }
-
/**
* Sets both shelf visibility and its height if applicable.
* @return {@code true} if the internal shelf state is changed, {@code false} otherwise.
@@ -172,8 +167,8 @@ public class PipBoundsHandler {
if (animatingBounds.isEmpty()) {
animatingBounds.set(defaultBounds);
}
- if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
- transformBoundsToAspectRatio(normalBounds, mAspectRatio,
+ if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
+ transformBoundsToAspectRatio(normalBounds, mPipBoundsState.getAspectRatio(),
false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
}
displayInfo.copyFrom(mDisplayInfo);
@@ -212,27 +207,16 @@ public class PipBoundsHandler {
}
/**
- * Responds to IPinnedStackListener on resetting aspect ratio for the pinned window.
- * It will normally follow up with a
- * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
- */
- public void onAspectRatioChanged(float aspectRatio) {
- mAspectRatio = aspectRatio;
- }
-
- /**
- * See {@link #getDestinationBounds(float, Rect, Size, boolean)}
+ * See {@link #getDestinationBounds(Rect, Size, boolean)}
*/
- public Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
- return getDestinationBounds(aspectRatio, bounds, minimalSize,
- false /* useCurrentMinEdgeSize */);
+ public Rect getDestinationBounds(Rect bounds, Size minimalSize) {
+ return getDestinationBounds(bounds, minimalSize, false /* useCurrentMinEdgeSize */);
}
/**
* @return {@link Rect} of the destination PiP window bounds.
*/
- public Rect getDestinationBounds(float aspectRatio, Rect bounds,
- Size minimalSize, boolean useCurrentMinEdgeSize) {
+ public Rect getDestinationBounds(Rect bounds, Size minimalSize, boolean useCurrentMinEdgeSize) {
boolean isReentryBounds = false;
final Rect destinationBounds;
if (bounds == null) {
@@ -255,11 +239,10 @@ public class PipBoundsHandler {
// Just adjusting bounds (e.g. on aspect ratio changed).
destinationBounds = new Rect(bounds);
}
- if (isValidPictureInPictureAspectRatio(aspectRatio)) {
- transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize,
- isReentryBounds);
+ if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
+ transformBoundsToAspectRatio(destinationBounds, mPipBoundsState.getAspectRatio(),
+ useCurrentMinEdgeSize, isReentryBounds);
}
- mAspectRatio = aspectRatio;
return destinationBounds;
}
@@ -368,8 +351,8 @@ public class PipBoundsHandler {
* @param stackBounds
*/
public void transformBoundsToAspectRatio(Rect stackBounds) {
- transformBoundsToAspectRatio(stackBounds, mAspectRatio, true /* useCurrentMinEdgeSize */,
- true /* useCurrentSize */);
+ transformBoundsToAspectRatio(stackBounds, mPipBoundsState.getAspectRatio(),
+ true /* useCurrentMinEdgeSize */, true /* useCurrentSize */);
}
/**
@@ -514,7 +497,6 @@ public class PipBoundsHandler {
pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
- pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 2625f16fac46..aba2a3a29fe2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -34,6 +34,7 @@ public final class PipBoundsState {
private static final String TAG = PipBoundsState.class.getSimpleName();
private final @NonNull Rect mBounds = new Rect();
+ private float mAspectRatio;
private PipReentryState mPipReentryState;
private ComponentName mLastPipComponentName;
@@ -46,6 +47,14 @@ public final class PipBoundsState {
return new Rect(mBounds);
}
+ public void setAspectRatio(float aspectRatio) {
+ mAspectRatio = aspectRatio;
+ }
+
+ public float getAspectRatio() {
+ return mAspectRatio;
+ }
+
/**
* Save the reentry state to restore to when re-entering PIP mode.
*
@@ -120,6 +129,7 @@ public final class PipBoundsState {
pw.println(prefix + TAG);
pw.println(innerPrefix + "mBounds=" + mBounds);
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
+ pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
if (mPipReentryState == null) {
pw.println(innerPrefix + "mPipReentryState=null");
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index cd5d35bf0c4c..a7bf9aea46bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -341,9 +341,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mShouldIgnoreEnteringPipTransition = true;
mState = State.ENTERING_PIP;
mPipBoundsState.setLastPipComponentName(componentName);
- return mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(pictureInPictureParams),
- null /* bounds */, getMinimalSize(activityInfo));
+ mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(pictureInPictureParams));
+ return mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+ getMinimalSize(activityInfo));
}
/**
@@ -514,9 +514,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(mPictureInPictureParams),
- null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
+ mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(mPictureInPictureParams));
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+ getMinimalSize(mTaskInfo.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
@@ -717,8 +717,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
return;
}
+ // Aspect ratio changed, re-calculate destination bounds.
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(newParams),
mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo),
true /* userCurrentMinEdgeSize */);
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
@@ -735,7 +735,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void onFixedRotationFinished(int displayId) {
if (mShouldDeferEnteringPip && mState.isInPip()) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(mPictureInPictureParams),
null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
// schedule a regular animation to ensure all the callbacks are still being sent
enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
@@ -808,9 +807,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(mPictureInPictureParams),
- null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
+ final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+ getMinimalSize(mTaskInfo.topActivityInfo));
if (newDestinationBounds.equals(currentDestinationBounds)) return;
if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
animator.updateEndValue(newDestinationBounds);
@@ -831,7 +829,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
params.getAspectRatioRational());
mPictureInPictureParams = params;
if (aspectRatioChanged) {
- mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
+ mPipBoundsState.setAspectRatio(params.getAspectRatio());
}
return aspectRatioChanged;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a08983740f9a..edc68e5221f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -197,8 +197,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onAspectRatioChanged(float aspectRatio) {
+ // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params
+ // change.
mHandler.post(() -> {
- mPipBoundsHandler.onAspectRatioChanged(aspectRatio);
+ mPipBoundsState.setAspectRatio(aspectRatio);
mTouchHandler.onAspectRatioChanged();
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
index 22c05fb8acd9..64e3758fd81a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip.phone;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import android.app.IActivityManager;
@@ -180,25 +181,25 @@ public class PipMediaController {
mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_pause_white), pauseDescription, pauseDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
String playDescription = mContext.getString(R.string.pip_play);
mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_play_arrow_white), playDescription, playDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
String nextDescription = mContext.getString(R.string.pip_skip_to_next);
mNextAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_skip_next_white), nextDescription, nextDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NEXT),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
String prevDescription = mContext.getString(R.string.pip_skip_to_prev);
mPrevAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_skip_previous_white), prevDescription, prevDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PREV),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 8eac005425bc..4f2d4e50f76d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -19,6 +19,10 @@ package com.android.wm.shell.pip.tv;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.Intent.ACTION_MEDIA_RESOURCE_GRANTED;
+
+import static com.android.wm.shell.pip.tv.PipNotification.ACTION_CLOSE;
+import static com.android.wm.shell.pip.tv.PipNotification.ACTION_MENU;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -140,17 +144,26 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_MEDIA_RESOURCE_GRANTED.equals(action)) {
- String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
- int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE,
- INVALID_RESOURCE_TYPE);
- if (packageNames != null && packageNames.length > 0
- && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) {
- handleMediaResourceGranted(packageNames);
- }
+ if (DEBUG) {
+ Log.d(TAG, "mBroadcastReceiver, action: " + intent.getAction());
+ }
+ switch (intent.getAction()) {
+ case ACTION_MENU:
+ showPictureInPictureMenu();
+ break;
+ case ACTION_CLOSE:
+ closePip();
+ break;
+ case ACTION_MEDIA_RESOURCE_GRANTED:
+ String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+ int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE,
+ INVALID_RESOURCE_TYPE);
+ if (packageNames != null && packageNames.length > 0
+ && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) {
+ handleMediaResourceGranted(packageNames);
+ }
+ break;
}
-
}
};
private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
@@ -233,8 +246,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mPipTaskOrganizer = pipTaskOrganizer;
mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_CLOSE);
+ intentFilter.addAction(ACTION_MENU);
+ intentFilter.addAction(ACTION_MEDIA_RESOURCE_GRANTED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
// Initialize the last orientation and apply the current configuration
@@ -249,10 +265,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
- }
- // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView
- PipMenuActivity.setPipController(this);
+ // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView
+ PipMenuActivity.setPipController(this);
+ }
}
private void loadConfigurationsAndApply(Configuration newConfig) {
@@ -564,6 +580,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
} catch (RemoteException e) {
Log.e(TAG, "getRootTaskInfo failed", e);
}
+ if (DEBUG) Log.d(TAG, "getPinnedTaskInfo(), taskInfo=" + taskInfo);
return taskInfo;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
index 06d2408c95f4..e185a9604449 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
@@ -35,7 +35,7 @@ import java.util.Collections;
*/
public class PipMenuActivity extends Activity implements PipController.Listener {
private static final String TAG = "PipMenuActivity";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG = PipController.DEBUG;
static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
@@ -51,7 +51,7 @@ public class PipMenuActivity extends Activity implements PipController.Listener
if (DEBUG) Log.d(TAG, "onCreate()");
super.onCreate(bundle);
- if (sPipController == null || sPipController.isPipShown()) {
+ if (sPipController == null) {
finish();
}
setContentView(R.layout.tv_pip_menu);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
index 7433085e6fcb..f5bbd23fa1d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
@@ -20,10 +20,8 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteAction;
-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.PackageManager;
import android.content.pm.ParceledListSlice;
@@ -32,9 +30,7 @@ import android.graphics.Bitmap;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
-import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.Log;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.wm.shell.R;
@@ -49,8 +45,8 @@ public class PipNotification {
private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName();
private static final boolean DEBUG = PipController.DEBUG;
- private static final String ACTION_MENU = "PipNotification.menu";
- private static final String ACTION_CLOSE = "PipNotification.close";
+ static final String ACTION_MENU = "PipNotification.menu";
+ static final String ACTION_CLOSE = "PipNotification.close";
public static final String NOTIFICATION_CHANNEL_TVPIP = "TPP";
@@ -147,23 +143,6 @@ public class PipNotification {
}
};
- private final BroadcastReceiver mEventReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Log.d(TAG, "Received " + intent.getAction() + " from the notification UI");
- }
- switch (intent.getAction()) {
- case ACTION_MENU:
- mPipController.showPictureInPictureMenu();
- break;
- case ACTION_CLOSE:
- mPipController.closePip();
- break;
- }
- }
- };
-
public PipNotification(Context context, PipController pipController) {
mPackageManager = context.getPackageManager();
@@ -182,11 +161,6 @@ public class PipNotification {
pipController.addListener(mPipListener);
pipController.addMediaListener(mPipMediaListener);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_MENU);
- intentFilter.addAction(ACTION_CLOSE);
- context.registerReceiver(mEventReceiver, intentFilter, UserHandle.USER_ALL);
-
onConfigurationChanged(context);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
index 0a1aadc90a62..f763d6d714c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
@@ -33,7 +33,6 @@ import android.util.Log;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
-import android.window.TaskAppearedInfo;
import androidx.annotation.NonNull;
@@ -69,15 +68,10 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener {
void init() throws RemoteException {
synchronized (this) {
try {
- final TaskAppearedInfo primary = mTaskOrganizer.createRootTask(
- Display.DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, this);
- final TaskAppearedInfo secondary = mTaskOrganizer.createRootTask(
- Display.DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, this);
- mPrimary = primary.getTaskInfo();
- mPrimarySurface = primary.getLeash();
- mSecondary = secondary.getTaskInfo();
- mSecondarySurface = secondary.getLeash();
- enableSplitScreenSupportIfNeeded();
+ mPrimary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mSecondary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
} catch (Exception e) {
// teardown to prevent callbacks
mTaskOrganizer.removeListener(this);
@@ -98,29 +92,43 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener {
mSplitScreenController.mTransactionPool.release(t);
}
- private void enableSplitScreenSupportIfNeeded() {
- if (mSplitScreenSupported || mPrimarySurface == null || mSecondarySurface == null) return;
-
- mSplitScreenSupported = true;
-
- // Initialize dim surfaces:
- mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
- .setParent(mPrimarySurface).setColorLayer()
- .setName("Primary Divider Dim")
- .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
- .build();
- mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
- .setParent(mSecondarySurface).setColorLayer()
- .setName("Secondary Divider Dim")
- .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
- .build();
- SurfaceControl.Transaction t = getTransaction();
- t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
- t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
- t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
- t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
- t.apply();
- releaseTransaction(t);
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ synchronized (this) {
+ if (mPrimary == null || mSecondary == null) {
+ Log.w(TAG, "Received onTaskAppeared before creating root tasks " + taskInfo);
+ return;
+ }
+
+ if (taskInfo.token.equals(mPrimary.token)) {
+ mPrimarySurface = leash;
+ } else if (taskInfo.token.equals(mSecondary.token)) {
+ mSecondarySurface = leash;
+ }
+
+ if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) {
+ mSplitScreenSupported = true;
+
+ // Initialize dim surfaces:
+ mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mPrimarySurface).setColorLayer()
+ .setName("Primary Divider Dim")
+ .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
+ .build();
+ mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mSecondarySurface).setColorLayer()
+ .setName("Secondary Divider Dim")
+ .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
+ .build();
+ SurfaceControl.Transaction t = getTransaction();
+ t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
+ t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
+ t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
+ t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
+ t.apply();
+ releaseTransaction(t);
+ }
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 3b66c58414e0..7f8321f3fa3d 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -32,6 +32,10 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ </intent-filter>
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
@@ -41,6 +45,10 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ </intent-filter>
</activity>
</application>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index e0ac8e24756d..39117bb5912b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -123,7 +123,8 @@ public class PipBoundsHandlerTest extends PipTestCase {
(MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
};
for (float aspectRatio : aspectRatios) {
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -139,7 +140,8 @@ public class PipBoundsHandlerTest extends PipTestCase {
MAX_ASPECT_RATIO * 2
};
for (float aspectRatio : invalidAspectRatios) {
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -155,8 +157,9 @@ public class PipBoundsHandlerTest extends PipTestCase {
final Rect currentBounds = new Rect(0, 0, 0, 100);
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
- currentBounds, EMPTY_MINIMAL_SIZE);
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(currentBounds,
+ EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -179,7 +182,8 @@ public class PipBoundsHandlerTest extends PipTestCase {
for (int i = 0; i < aspectRatios.length; i++) {
final float aspectRatio = aspectRatios[i];
final Size minimalSize = minimalSizes[i];
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, minimalSize);
assertTrue("Destination bounds is no smaller than minimal requirement",
(destinationBounds.width() == minimalSize.getWidth()
@@ -200,7 +204,8 @@ public class PipBoundsHandlerTest extends PipTestCase {
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
currentBounds, minSize);
assertTrue("Destination bounds ignores minimal size",
@@ -210,13 +215,14 @@ public class PipBoundsHandlerTest extends PipTestCase {
@Test
public void getDestinationBounds_reentryStateExists_restoreLastSize() {
- final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
reentryBounds.scale(1.25f);
final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertEquals(reentryBounds.width(), destinationBounds.width());
@@ -225,14 +231,15 @@ public class PipBoundsHandlerTest extends PipTestCase {
@Test
public void getDestinationBounds_reentryStateExists_restoreLastPosition() {
- final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
reentryBounds.offset(0, -100);
final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds);
@@ -241,11 +248,12 @@ public class PipBoundsHandlerTest extends PipTestCase {
@Test
public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
- final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.setShelfHeight(true, shelfHeight);
- final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -shelfHeight);
@@ -255,11 +263,12 @@ public class PipBoundsHandlerTest extends PipTestCase {
@Test
public void onImeVisibilityChanged_offsetBounds() {
final int imeHeight = 100;
- final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
- final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -imeHeight);
@@ -268,12 +277,13 @@ public class PipBoundsHandlerTest extends PipTestCase {
@Test
public void getDestinationBounds_noReentryState_useDefaultBounds() {
- final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsState.clearReentryState();
- final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
index 19b017b7dff9..10773f82fa43 100644
--- a/location/java/android/location/Geofence.java
+++ b/location/java/android/location/Geofence.java
@@ -58,7 +58,7 @@ public final class Geofence implements Parcelable {
Geofence(double latitude, double longitude, float radius, long expirationRealtimeMs) {
Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "latitude");
- Preconditions.checkArgument(radius > 0, "invalid radius: " + radius);
+ Preconditions.checkArgument(radius > 0, "invalid radius: %f", radius);
mLatitude = latitude;
mLongitude = longitude;
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index d0706c6b84cb..1adb2b47a884 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -934,7 +934,7 @@ public final class LocationRequest implements Parcelable {
Preconditions.checkArgument(
quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY
|| quality == QUALITY_HIGH_ACCURACY,
- "quality must be a defined QUALITY constant, not " + quality);
+ "quality must be a defined QUALITY constant, not %d", quality);
mQuality = quality;
return this;
}
diff --git a/media/Android.bp b/media/Android.bp
index 828707b70e7b..8b06bb26fcf7 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,7 +1,10 @@
aidl_interface {
name: "audio_common-aidl",
unstable: true,
+ host_supported: true,
+ vendor_available: true,
local_include_dir: "aidl",
+ double_loadable: true,
srcs: [
"aidl/android/media/audio/common/AudioChannelMask.aidl",
"aidl/android/media/audio/common/AudioConfig.aidl",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 195122db146a..8b28cc45fda2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4049,7 +4049,8 @@ public class AudioManager {
// the associated intent will be handled by the component being registered
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(getContext(),
- 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+ 0/*requestCode, ignored*/, mediaButtonIntent,
+ PendingIntent.FLAG_IMMUTABLE);
registerMediaButtonIntent(pi, eventReceiver);
}
@@ -4102,7 +4103,8 @@ public class AudioManager {
// the associated intent will be handled by the component being registered
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(getContext(),
- 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+ 0/*requestCode, ignored*/, mediaButtonIntent,
+ PendingIntent.FLAG_IMMUTABLE);
unregisterMediaButtonIntent(pi);
}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 0a466f424cec..c503721319fb 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -137,12 +137,24 @@ ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const c
return nullptr;
}
+ Surface* surface = static_cast<Surface*>(window);
+ sp<IBinder> parentHandle = surface->getSurfaceControlHandle();
+
uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
- sp<SurfaceControl> surfaceControl =
- client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
- // Format is only relevant for buffer queue layers.
- PIXEL_FORMAT_UNKNOWN /* format */, flags,
- static_cast<Surface*>(window));
+ sp<SurfaceControl> surfaceControl;
+ if (parentHandle) {
+ surfaceControl =
+ client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
+ // Format is only relevant for buffer queue layers.
+ PIXEL_FORMAT_UNKNOWN /* format */, flags, parentHandle);
+ } else {
+ surfaceControl =
+ client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
+ // Format is only relevant for buffer queue layers.
+ PIXEL_FORMAT_UNKNOWN /* format */, flags,
+ static_cast<Surface*>(window));
+ }
+
if (!surfaceControl) {
return nullptr;
}
@@ -164,7 +176,7 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb
client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
// Format is only relevant for buffer queue layers.
PIXEL_FORMAT_UNKNOWN /* format */, flags,
- surfaceControlParent);
+ surfaceControlParent->getHandle());
if (!surfaceControl) {
return nullptr;
}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 225a2e70e998..2ac64a33b771 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -10820,7 +10820,6 @@ package android.content {
field public static final String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
field public static final String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
- field public static final String EXTRA_REMOVED_BY_SYSTEM = "android.intent.extra.REMOVED_BY_SYSTEM";
field public static final String EXTRA_REPLACEMENT_EXTRAS = "android.intent.extra.REPLACEMENT_EXTRAS";
field public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
field public static final String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
@@ -10846,6 +10845,7 @@ package android.content {
field public static final String EXTRA_UID = "android.intent.extra.UID";
field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
field public static final String EXTRA_USER = "android.intent.extra.USER";
+ field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
field public static final int FILL_IN_ACTION = 1; // 0x1
field public static final int FILL_IN_CATEGORIES = 4; // 0x4
field public static final int FILL_IN_CLIP_DATA = 128; // 0x80
@@ -12265,7 +12265,7 @@ package android.content.pm {
field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
- field public static final int GET_INTENT_FILTERS = 32; // 0x20
+ field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
field public static final int GET_META_DATA = 128; // 0x80
field public static final int GET_PERMISSIONS = 4096; // 0x1000
field public static final int GET_PROVIDERS = 8; // 0x8
@@ -45216,6 +45216,7 @@ package android.telephony {
}
public static final class CarrierConfigManager.Ims {
+ field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
field public static final String KEY_PREFIX = "ims.";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
}
@@ -46220,6 +46221,7 @@ package android.telephony {
}
public class SignalStrength implements android.os.Parcelable {
+ ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
method public int describeContents();
method @Deprecated public int getCdmaDbm();
method @Deprecated public int getCdmaEcio();
@@ -46727,6 +46729,9 @@ package android.telephony {
field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+ field public static final int ERI_FLASH = 2; // 0x2
+ field public static final int ERI_OFF = 1; // 0x1
+ field public static final int ERI_ON = 0; // 0x0
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
field public static final String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
@@ -55531,6 +55536,7 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
+ method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method public CharSequence getTextAfterCursor(int, int);
method public CharSequence getTextBeforeCursor(int, int);
method public boolean performContextMenuAction(int);
@@ -55739,6 +55745,17 @@ package android.view.inputmethod {
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
+ public final class SurroundingText implements android.os.Parcelable {
+ ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
+ method public int describeContents();
+ method @IntRange(from=0xffffffff) public int getOffset();
+ method @IntRange(from=0) public int getSelectionEnd();
+ method @IntRange(from=0) public int getSelectionStart();
+ method @NonNull public CharSequence getText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
+ }
+
}
package android.view.inspector {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 26c307141065..5c8c4d5f40d7 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -310,6 +310,7 @@ package android {
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+ field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemVideoCall = 17039401; // 0x1040029
}
@@ -864,6 +865,7 @@ package android.app.admin {
field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
+ field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
@@ -10380,10 +10382,6 @@ package android.telephony {
field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
- public class SignalStrength implements android.os.Parcelable {
- ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
- }
-
public final class SmsCbCmasInfo implements android.os.Parcelable {
ctor public SmsCbCmasInfo(int, int, int, int, int, int);
method public int describeContents();
@@ -10611,6 +10609,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
@@ -11344,6 +11343,10 @@ package android.telephony.ims {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
+ public class ImsManager {
+ method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int);
+ }
+
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -11379,10 +11382,13 @@ package android.telephony.ims {
method public void disableIms(int);
method public void enableIms(int);
method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method public long getImsServiceCapabilities();
method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+ method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
method public void readyForFeatureCreation();
+ field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L
}
public final class ImsSsData implements android.os.Parcelable {
@@ -11628,6 +11634,10 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
+ public class SipDelegateManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+ }
+
}
package android.telephony.ims.feature {
@@ -11877,6 +11887,10 @@ package android.telephony.ims.stub {
method public int updateColr(int);
}
+ public class SipTransportImplBase {
+ ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+ }
+
}
package android.telephony.mbms {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index be778e92787e..2674eaf4b76c 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -339,8 +339,9 @@ public class UninstallerActivity extends Activity {
broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
- broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(this, uninstallId, broadcastIntent,
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL,
diff --git a/packages/PrintSpooler/res/values-mr/strings.xml b/packages/PrintSpooler/res/values-mr/strings.xml
index 4d7e919ad125..81194399c00e 100644
--- a/packages/PrintSpooler/res/values-mr/strings.xml
+++ b/packages/PrintSpooler/res/values-mr/strings.xml
@@ -63,7 +63,7 @@
<string name="printer_info_desc" msgid="7181988788991581654">"या प्रिंटर विषयी अधिक माहिती"</string>
<string name="notification_channel_progress" msgid="872788690775721436">"प्रिंट कार्ये चालवणे"</string>
<string name="notification_channel_failure" msgid="9042250774797916414">"अयशस्वी प्रिंट कार्ये"</string>
- <string name="could_not_create_file" msgid="3425025039427448443">"फाईल तयार करणेे शक्य झाले नाही"</string>
+ <string name="could_not_create_file" msgid="3425025039427448443">"फाइल तयार करणेे शक्य झाले नाही"</string>
<string name="print_services_disabled_toast" msgid="9089060734685174685">"काही प्रिंट सेवा अक्षम केल्या आहेत"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर शोधत आहे"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही प्रिंट सेवा सक्षम केलेल्या नाहीत"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 7c8dc24bdfd7..8c55328bdc67 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -97,7 +97,7 @@
<string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"श्रवण यंत्रांशी कनेक्ट केले आहे"</string>
<string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मीडिया ऑडिओवर कनेक्ट केले"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन ऑडिओ वर कनेक्ट केले"</string>
- <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाईल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाइल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
<string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"नकाशाशी कनेक्ट केले"</string>
<string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"SAP शी कनेक्‍ट केले"</string>
<string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"फाइल स्थानांतर सर्व्हरशी कनेक्ट केले नाही"</string>
@@ -109,7 +109,7 @@
<string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"SIM प्रवेशासाठी वापरा"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"मीडिया ऑडिओसाठी वापरा"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"फोन ऑडिओसाठी वापरा"</string>
- <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाईल स्थानांतरणासाठी वापरा"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल स्थानांतरणासाठी वापरा"</string>
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुट साठी वापरा"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"श्रवण यंत्रांसाठी वापरा"</string>
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"पेअर करा"</string>
@@ -268,8 +268,8 @@
<string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"फोन किंवा हेडसेटला सपोर्ट करत नाही म्हणजे ती निकामी झाली आहे"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"प्रति पॅटर्न ब्लूटूध ऑडिओ बिट"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: बिट प्रति नमुना"</string>
- <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"ब्लूटूथ ऑडिओ चॅनेल मोड"</string>
- <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: चॅनेल मोड"</string>
+ <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"ब्लूटूथ ऑडिओ चॅनल मोड"</string>
+ <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: चॅनल मोड"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"ब्लूटूथ ऑडिओ LDAC कोडेक: प्लेबॅक गुणवत्ता"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"ब्लूटूथ ऑडिओ LDAC\nकोडेक निवड ट्रिगर करा: प्लेबॅक गुणवत्ता"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"स्ट्रीमिंग: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
@@ -370,7 +370,7 @@
<string name="app_process_limit_title" msgid="8361367869453043007">"पार्श्वभूमी प्रक्रिया मर्यादा"</string>
<string name="show_all_anrs" msgid="9160563836616468726">"बॅकग्राउंड ANR दाखवा"</string>
<string name="show_all_anrs_summary" msgid="8562788834431971392">"बॅकग्राउंड अ‍ॅप्ससाठी अ‍ॅप प्रतिसाद देत नाही दाखवते"</string>
- <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना चॅनेल चेतावण्या दाखवा"</string>
+ <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना चॅनल चेतावण्या दाखवा"</string>
<string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"एखादे अ‍ॅप वैध चॅनेलशिवाय सूचना पोस्ट करते तेव्हा स्क्रीनवर चेतावणी देते"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"बाह्यवर ॲप्सना अनुमती देण्याची सक्ती करा"</string>
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"manifest मूल्यांकडे दुर्लक्ष करून, कोणत्याही ॲपला बाह्य स्टोरेजवर लेखन केले जाण्यासाठी पात्र बनविते"</string>
@@ -404,11 +404,11 @@
<string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string>
<string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"वेबदृश्य अंमलबजावणी सेट करा"</string>
<string name="select_webview_provider_toast_text" msgid="8512254949169359848">"ही निवड यापुढे वैध असणार नाही. पुन्हा प्रयत्न करा."</string>
- <string name="convert_to_file_encryption" msgid="2828976934129751818">"फाईल कूटबद्धीकरणावर रूपांतरित करा"</string>
+ <string name="convert_to_file_encryption" msgid="2828976934129751818">"फाइल कूटबद्धीकरणावर रूपांतरित करा"</string>
<string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"रूपांतरित करा..."</string>
- <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"फाईल आधीपासून एंक्रिप्ट होती"</string>
- <string name="title_convert_fbe" msgid="5780013350366495149">"फाईल आधारित कूटबद्धीकरणावर रूपांतरित करणे"</string>
- <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाईल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्‍या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्‍फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरू ठेवण्‍यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string>
+ <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"फाइल आधीपासून एंक्रिप्ट होती"</string>
+ <string name="title_convert_fbe" msgid="5780013350366495149">"फाइल आधारित कूटबद्धीकरणावर रूपांतरित करणे"</string>
+ <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाइल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्‍या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्‍फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरू ठेवण्‍यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string>
<string name="button_convert_fbe" msgid="1159861795137727671">"पुसा आणि रुपांतरित करा..."</string>
<string name="picture_color_mode" msgid="1013807330552931903">"चित्र रंग मोड"</string>
<string name="picture_color_mode_desc" msgid="151780973768136200">"sRGB वापरा"</string>
@@ -525,7 +525,7 @@
<string name="accessor_expires_text" msgid="4625619273236786252">"भाडेपट्टी <xliff:g id="DATE">%s</xliff:g> रोजी एक्स्पायर होईल"</string>
<string name="delete_blob_text" msgid="2819192607255625697">"शेअर केलेला डेटा हटवा"</string>
<string name="delete_blob_confirmation_text" msgid="7807446938920827280">"तुम्हाला नक्की हा शेअर केलेला डेटा हटवायचा आहे का?"</string>
- <string name="user_add_user_item_summary" msgid="5748424612724703400">"वापरकर्त्यांकडे त्यांचे स्वत:चे अ‍ॅप्स आणि सामग्री आहे"</string>
+ <string name="user_add_user_item_summary" msgid="5748424612724703400">"वापरकर्त्यांकडे त्यांचे स्वत:चे अ‍ॅप्स आणि आशय आहे"</string>
<string name="user_add_profile_item_summary" msgid="5418602404308968028">"तुम्ही आपल्या खात्यावरुन अ‍ॅप्स आणि सामग्रीमध्ये प्रवेश करण्यास प्रतिबंध करु शकता"</string>
<string name="user_add_user_item_title" msgid="2394272381086965029">"वापरकर्ता"</string>
<string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबंधित प्रोफाईल"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e7b1482c3452..197655ef5e58 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -451,7 +451,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Inkopplad, kan inte laddas just nu"</string>
+ <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ansluten, kan inte laddas just nu"</string>
<string name="battery_info_status_full" msgid="4443168946046847468">"Fullt"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Strys av administratören"</string>
<string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 90bed12cfd6e..94de48595b0d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1494,9 +1494,6 @@ class SettingsProtoDumpUtil {
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
GlobalSettingsProto.Webview.DATA_REDUCTION_PROXY_KEY);
dumpSetting(s, p,
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
- GlobalSettingsProto.Webview.FALLBACK_LOGIC_ENABLED);
- dumpSetting(s, p,
Settings.Global.WEBVIEW_PROVIDER,
GlobalSettingsProto.Webview.PROVIDER);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4713243687c4..10d2eab167e7 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -526,7 +526,6 @@ public class SettingsBackupTest {
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
Settings.Global.WEBVIEW_MULTIPROCESS,
Settings.Global.WEBVIEW_PROVIDER,
Settings.Global.WFC_IMS_ENABLED,
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index 9595e28eb6e2..a957184cdf6a 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -31,8 +31,8 @@
<string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अ‍ॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अ‍ॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
- <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाईल वाचणे शक्य झाले नाही"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाईल मध्ये बग रीपोर्ट तपशील जोडणे शक्य झाले नाही"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाइल वाचणे शक्य झाले नाही"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाइल मध्ये बग रीपोर्ट तपशील जोडणे शक्य झाले नाही"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"अनामित"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"तपशील"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"स्क्रीनशॉट"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ef8064fe2c6a..407104504132 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -586,6 +586,24 @@
</intent-filter>
</activity>
+ <!-- People Space Widget -->
+ <receiver
+ android:name=".people.widget.PeopleSpaceWidgetProvider"
+ android:label="People Space"
+ android:enabled="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/people_space_widget_info" />
+ </receiver>
+
+ <!-- Widget service -->
+ <service android:name=".people.widget.PeopleSpaceWidgetService"
+ android:permission="android.permission.BIND_REMOTEVIEWS"
+ android:exported="false" />
+
<!-- a gallery of delicious treats -->
<service
android:name=".DessertCaseDream"
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index 8ec20f5689ff..e709278db43a 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -62,17 +62,17 @@ Acquire the dispatcher by using `@Inject` to obtain a `BroadcastDispatcher`. The
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the user of the context (system user in SystemUI).
+ * Pass `null` to use the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@JvmOverloads
-fun registerReceiver(
- BroadcastReceiver,
- IntentFilter,
- Executor? = context.mainExecutor,
- UserHandle = context.user
-) {
+open fun registerReceiver(
+ receiver: BroadcastReceiver,
+ filter: IntentFilter,
+ executor: Executor? = null,
+ user: UserHandle? = null
+)
```
All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Executor`, pass `null` for the `Executor`.
diff --git a/packages/SystemUI/res-keyguard/font/clock.xml b/packages/SystemUI/res-keyguard/font/clock.xml
new file mode 100644
index 000000000000..008b32223ec6
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/font/clock.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2020, 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.
+*/
+-->
+
+<!--
+** AOD/LockScreen Clock font.
+** Should include all numeric glyphs in all supported locales.
+** Recommended: font with variable width to support AOD => LS animations
+-->
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- TODO (b/171376810): switch this font with a variable width clock font for AOSP -->
+ <font android:typeface="monospace" android:font="@*android:string/config_headlineFontFamily" />
+</font-family> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 7e2e36a4907d..f22c729744d3 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -76,7 +76,7 @@
android:letterSpacing="0.02"
android:lineSpacingMultiplier=".8"
android:includeFontPadding="false"
- android:fontFamily="sans-serif"
+ android:fontFamily="@font/clock"
android:typeface="monospace"
android:format12Hour="hh\nmm"
android:format24Hour="HH\nmm"
diff --git a/packages/SystemUI/res/drawable/ic_check_box.xml b/packages/SystemUI/res/drawable/ic_check_box.xml
new file mode 100644
index 000000000000..a8d1a652b35b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2020 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
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/checked"
+ android:state_checked="true"
+ android:drawable="@drawable/ic_check_box_blue_24dp" />
+ <item
+ android:id="@+id/unchecked"
+ android:state_checked="false"
+ android:drawable="@drawable/ic_check_box_outline_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
new file mode 100644
index 000000000000..43cae6983981
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"
+ android:fillColor="#4285F4"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
new file mode 100644
index 000000000000..f6f453af2a26
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"
+ android:fillColor="#757575"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
new file mode 100644
index 000000000000..ae0d56217fd9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index ef7325ea8f38..e0333eb82d51 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<FrameLayout
+<com.android.systemui.screenshot.ScreenshotView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/global_screenshot_frame"
android:layout_width="match_parent"
@@ -62,4 +62,4 @@
android:layout_margin="@dimen/screenshot_dismiss_button_margin"
android:src="@drawable/screenshot_cancel"/>
</FrameLayout>
-</FrameLayout>
+</com.android.systemui.screenshot.ScreenshotView>
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index ac8b7b5812bd..c98c3a0beb65 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -15,97 +15,124 @@
~ limitations under the License.
-->
-<FrameLayout
- android:id="@+id/device_container"
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/device_container"
android:layout_width="match_parent"
- android:layout_height="64dp">
-
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
<FrameLayout
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="16dp">
- <ImageView
- android:id="@+id/title_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"/>
- </FrameLayout>
+ android:layout_width="match_parent"
+ android:layout_height="64dp">
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="68dp"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"/>
+ <FrameLayout
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp">
+ <ImageView
+ android:id="@+id/title_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
- <RelativeLayout
- android:id="@+id/two_line_layout"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginStart="52dp"
- android:layout_marginEnd="69dp"
- android:layout_marginTop="10dp">
<TextView
- android:id="@+id/two_line_title"
+ android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="68dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"/>
- <TextView
- android:id="@+id/subtitle"
+
+ <RelativeLayout
+ android:id="@+id/two_line_layout"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
- android:layout_marginBottom="7dp"
- android:layout_alignParentBottom="true"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="12sp"
- android:fontFamily="roboto-regular"
+ android:layout_height="48dp"
+ android:layout_marginStart="52dp"
+ android:layout_marginEnd="69dp"
+ android:layout_marginTop="10dp">
+ <TextView
+ android:id="@+id/two_line_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="15dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"/>
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="15dp"
+ android:layout_marginBottom="7dp"
+ android:layout_alignParentBottom="true"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="12sp"
+ android:fontFamily="roboto-regular"
+ android:visibility="gone"/>
+ <SeekBar
+ android:id="@+id/volume_seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"/>
+ </RelativeLayout>
+
+ <ProgressBar
+ android:id="@+id/volume_indeterminate_progress"
+ style="@*android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="258dp"
+ android:layout_height="18dp"
+ android:layout_marginStart="68dp"
+ android:layout_marginTop="40dp"
+ android:indeterminate="true"
+ android:indeterminateOnly="true"
android:visibility="gone"/>
- <SeekBar
- android:id="@+id/volume_seekbar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"/>
- </RelativeLayout>
- <ProgressBar
- android:id="@+id/volume_indeterminate_progress"
- style="@*android:style/Widget.Material.ProgressBar.Horizontal"
- android:layout_width="258dp"
- android:layout_height="18dp"
- android:layout_marginStart="68dp"
- android:layout_marginTop="40dp"
- android:indeterminate="true"
- android:indeterminateOnly="true"
- android:visibility="gone"/>
+ <View
+ android:id="@+id/end_divider"
+ android:layout_width="1dp"
+ android:layout_height="36dp"
+ android:layout_marginEnd="68dp"
+ android:layout_gravity="right|center_vertical"
+ android:background="?android:attr/listDivider"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/add_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="right|center_vertical"
+ android:layout_marginEnd="24dp"
+ android:src="@drawable/ic_add"
+ android:tint="?android:attr/colorAccent"
+ android:visibility="gone"/>
+
+ <CheckBox
+ android:id="@+id/check_box"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="right|center_vertical"
+ android:layout_marginEnd="24dp"
+ android:button="@drawable/ic_check_box"
+ android:visibility="gone"/>
+ </FrameLayout>
<View
- android:layout_width="1dp"
- android:layout_height="36dp"
- android:layout_marginEnd="68dp"
- android:layout_gravity="right|center_vertical"
+ android:id="@+id/bottom_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:layout_gravity="bottom"
android:background="?android:attr/listDivider"
android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/end_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="right|center_vertical"
- android:layout_marginEnd="24dp"
- android:visibility="gone"/>
-</FrameLayout> \ No newline at end of file
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_widget.xml b/packages/SystemUI/res/layout/people_space_widget.xml
new file mode 100644
index 000000000000..60200993e3bf
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_widget.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+<ListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_blue_light"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:padding="5dp"
+ android:divider="@null"
+ android:dividerHeight="0dp"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_widget_item.xml b/packages/SystemUI/res/layout/people_space_widget_item.xml
new file mode 100644
index 000000000000..a40bfffaabd7
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_widget_item.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:background="@drawable/people_space_tile_view_card"
+ android:orientation="vertical"
+ android:padding="6dp"
+ android:layout_marginBottom="6dp"
+ android:elevation="4dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start">
+ <ImageView
+ android:id="@+id/package_icon"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:layout_gravity="end" />
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:layout_gravity="start" />
+ <TextView
+ android:id="@+id/name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="18sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start" />
+ <TextView
+ android:id="@+id/status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:paddingVertical="2dp"
+ android:textSize="14sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index e4d751ab100d..220a77318ce6 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer weer skermkiekie neem"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Wysig skermkiekie"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Maak skermkiekie toe"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Skermkiekievoorskou"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laai tans aanbevelings"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Versteek die huidige sessie."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Huidige sessie kan nie versteek word nie."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Maak toe"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 8c22e8f17552..b2a7c16c8ad0 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ቅጽበታዊ ገጽ ዕይታን እንደገና ማንሳት ይሞክሩ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ባለው ውሱን የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"ቅጽበታዊ ገጽ ዕይታን አርትዕ ያድርጉ"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ቅጽበታዊ ገጽ እይታን አሰናብት"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"የቅጽበታዊ ገጽ ዕይታ ቅድመ-ዕይታ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"የአሁኑ ክፍለ ጊዜ መደበቅ አይችልም።"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"አሰናብት"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 2c74a2789b36..2cced237e948 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"جرّب أخذ لقطة الشاشة مرة أخرى"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"يتعذر حفظ لقطة الشاشة لأن مساحة التخزين المتاحة محدودة."</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"يحظر التطبيق أو تحظر مؤسستك التقاط لقطات شاشة"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"تعديل لقطة الشاشة"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"إغلاق لقطة الشاشة"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string>
@@ -1089,8 +1088,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string>
<string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"لا يمكن إخفاء الجلسة الحالية."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"إغلاق"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 94cd1a7d7084..4c43e4ebed8f 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"স্ক্ৰীণশ্বট আকৌ ল\'বলৈ চেষ্টা কৰক"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"সঞ্চয়াগাৰত সীমিত খালী ঠাই থকাৰ বাবে স্ক্ৰীণশ্বট ছেভ কৰিব পৰা নগ\'ল"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এপটোৱে বা আপোনাৰ প্ৰতিষ্ঠানে স্ক্ৰীণশ্বট ল\'বলৈ অনুমতি নিদিয়ে"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"স্ক্ৰীনশ্বট সম্পাদনা কৰক"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্ৰীনশ্বট অগ্ৰাহ্য কৰক"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্ৰীনশ্বটৰ পূৰ্বদৰ্শন"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string>
<string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"বৰ্তমান ছেশ্বনটো লুকুৱাব নোৱাৰি।"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 43aca9a26e11..4cefd8426ed4 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skrinşotu yenidən çəkin"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Skrinşota düzəliş edin"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran şəklini ötürün"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Cari sessiyanı gizlətmək olmur."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"İmtina edin"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 3777dfe66ac6..2bc6c90cefd3 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probajte da ponovo napravite snimak ekrana"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili organizacija ne dozvoljavaju pravljenje snimaka ekrana"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Izmenite snimak ekrana"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -1071,8 +1070,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuelna sesija ne može da se sakrije."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 8a5d42aeb122..6396cbeb1299 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Паспрабуйце зрабіць здымак экрана яшчэ раз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Немагчыма захаваць здымак экрана, бо мала месца ў сховішчы"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Рабіць здымкі экрана не дазваляе праграма ці ваша арганізацыя"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Змяніць здымак экрана"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Адхіліць здымак экрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Перадпрагляд здымка экрана"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Гэты сеанс не можа быць схаваны."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Адхіліць"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 6cdc128bdb32..c4c6a0c3e2d9 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"বেশি জায়গা নেই তাই স্ক্রিনশটটি সেভ করা যাবে না৷"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"স্ক্রিনশট এডিট করুন"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্রিনশট বাতিল করুন"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string>
<string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"বর্তমান সেশন লুকানো যাবে না।"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"খারিজ করুন"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 8f622c8510ea..4ce5496f2186 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo snimiti ekran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ova aplikacija ili vaša organizacija ne dozvoljavaju snimanje ekrana"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Uredite snimak ekrana"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -1071,8 +1070,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Nije moguće sakriti trenutnu sesiju."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index ea7f36ae4b16..2b6dfd577616 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prova de tornar a fer una captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'aplicació o la teva organització no permeten fer captures de pantalla"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Edita la captura de pantalla"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora la captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Previsualització de la captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"La sessió actual no es pot amagar."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index d3e3dbf0e749..f00c279b6b58 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zkuste snímek pořídit znovu"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímek obrazovky kvůli nedostatku místa v úložišti nelze uložit"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Upravit snímek obrazovky"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavřít snímek obrazovky"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Náhled snímku obrazovky"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuální relaci nelze skrýt."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavřít"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 82e8fd1295db..9d365c33fa39 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv at tage et screenshot igen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Screenshottet kan ikke gemmes, fordi der er begrænset lagerplads"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller din organisation tillader ikke, at du tager screenshots"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Rediger dit screenshot"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Luk screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning af screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medie"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Den nuværende session kan ikke skjules."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Luk"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0e3f309ce5d4..55e7d17c84d2 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Versuche noch einmal, den Screenshot zu erstellen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Screenshot bearbeiten"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot schließen"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuelle Sitzung kann nicht verborgen werden."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 645ff081dd72..3bb15ffff775 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Δοκιμάστε να κάνετε ξανά λήψη του στιγμιότυπου οθόνης"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου αποθηκευτικού χώρου"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Η λήψη στιγμιότυπων οθόνης δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Επεξεργασία στιγμιότυπου οθόνης"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Παράβλεψη στιγμιότυπου οθόνης"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Προεπισκόπηση στιγμιότυπου οθόνης"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Δεν είναι δυνατή η απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Παράβλεψη"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 2b5e96f03f96..d30a39529051 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a hacer una captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla debido a que no hay suficiente espacio de almacenamiento"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La app o tu organización no permiten las capturas de pantalla"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de pantalla"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Descartar captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de la captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"No se puede ocultar la sesión actual."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Descartar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index b0377fd899e1..875c310eb133 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a intentar hacer la captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de pantalla"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cerrar captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"La sesión no se puede ocultar."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cerrar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index a22e12bfa11d..f9940a30fcaa 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Proovige ekraanipilt uuesti jäädvustada"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Rakendus või teie organisatsioon ei luba ekraanipilte jäädvustada"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Ekraanipildi muutmine"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Sule ekraanipilt"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekraanipildi eelvaade"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Praegust seanssi ei saa peita."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Loobu"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index f6ca69796d76..cb86a16122c0 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Saiatu berriro pantaila-argazkia ateratzen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ezin da gorde pantaila-argazkia ez delako gelditzen tokirik"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Editatu pantaila-argazkia"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Baztertu pantaila-argazkia"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Ezin da ezkutatu uneko saioa."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Baztertu"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 4c913f68ff4d..fe38ea09888a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوباره نماگرفت بگیرید"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"به دلیل محدود بودن فضای ذخیره‌سازی نمی‌توان نماگرفت را ذخیره کرد"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"برنامه یا سازمان شما اجازه نمی‌دهند نماگرفت بگیرید."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"ویرایش نماگرفت"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"رد کردن نماگرفت"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"پیش‌نمایش نماگرفت"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ضبط‌کننده صفحه‌نمایش"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"درحال بار کردن توصیه‌ها"</string>
<string name="controls_media_title" msgid="1746947284862928133">"رسانه"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"جلسه فعلی پنهان شود."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"نمی‌توان جلسه فعلی را پنهان کرد."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"رد کردن"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 0ab407adf99b..11c7e19be208 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Yritä ottaa kuvakaappaus uudelleen."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kuvakaappauksen tallennus epäonnistui, sillä tallennustilaa ei ole riittävästi"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Sovellus tai organisaatio ei salli kuvakaappauksien tallentamista."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Muokkaa kuvakaappausta"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hylkää kuvakaappaus"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Kuvakaappauksen esikatselu"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Nykyistä käyttökertaa ei voi piilottaa."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ohita"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 1b7a308f31ca..b8ea6ac31c77 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de faire une autre capture d\'écran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Modifier la capture d\'écran"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"La session actuelle ne peut pas être masquée."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 74a68618fb5d..289ce40d2ce7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de nouveau de faire une capture d\'écran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Modifier la capture d\'écran"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Impossible de masquer la session actuelle."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 26cf07a0dca0..e3d3755d4f68 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Volve tentar crear unha captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Non se puido gardar a captura de pantalla porque o espazo de almacenamento é limitado"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A aplicación ou a túa organización non permite realizar capturas de pantalla"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Edita a captura de pantalla"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora a captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa da captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Non se pode ocultar a sesión actual."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index acaa4420cb56..02c72dbbc7a6 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ફરીથી સ્ક્રીનશૉટ લેવાનો પ્રયાસ કરો"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"મર્યાદિત સ્ટોરેજ સ્પેસને કારણે સ્ક્રીનશૉટ સાચવી શકાતો નથી"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"સ્ક્રીનશૉટમાં ફેરફાર કરો"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"સ્ક્રીનશૉટ છોડી દો"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"વર્તમાન સત્ર છુપાવી શકાતું નથી."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"છોડી દો"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index d211dce96944..3f497e750b77 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट दोबारा लेने की कोशिश करें"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मेमोरी कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सका"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रीनशॉट में बदलाव करें"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट खारिज करें"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉट की झलक"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string>
@@ -1067,8 +1066,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"सुझाव लोड हो रहे हैं"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"इस मीडिया सेशन को छिपाएं."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"मौजूदा मीडिया सत्र छिपाया नहीं जा सकता."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"खारिज करें"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index b5671bf7abe0..9958a2c65998 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo napraviti snimku zaslona"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Zaslon nije snimljen zbog ograničenog prostora za pohranu"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili vaša organizacija ne dopuštaju snimanje zaslona"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Uređivanje snimke zaslona"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacivanje snimke zaslona"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimke zaslona"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string>
@@ -1071,8 +1070,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Trenutačnu sesiju nije moguće sakriti."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 291976e1f77c..0159ec617bf2 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Próbálja meg újra elkészíteni a képernyőképet"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nem menthet képernyőképet, mert kevés a tárhely"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Az alkalmazás vagy az Ön szervezete nem engedélyezi képernyőkép készítését"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Képernyőkép szerkesztése"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Képernyőkép elvetése"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Képernyőkép előnézete"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"A jelenlegi munkamenetet nem lehet elrejteni."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Elvetés"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f21952098a57..9aaf2705e657 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Coba ambil screenshot lagi"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Menutup screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Sesi saat ini tidak dapat disembunyikan."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tutup"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 8f1049f7d5a9..bf49e97785c4 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prófaðu að taka skjámynd aftur"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Forritið eða fyrirtækið þitt leyfir ekki skjámyndatöku"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Breyta skjámynd"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Loka skjámynd"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forskoðun skjámyndar"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Ekki er hægt að fela núverandi lotu."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hunsa"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index a273ea8ad7ac..8e665ec31ba0 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Riprova ad acquisire lo screenshot"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Modifica screenshot"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Anteprima screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Impossibile nascondere la sessione corrente."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 5ae10ad72a45..052b721b6eef 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"יש לנסות שוב לבצע צילום מסך"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"לא היה מספיק מקום לשמור את צילום המסך"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"האפליקציה או הארגון שלך אינם מתירים ליצור צילומי מסך"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"עריכת צילום מסך"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"סגירת צילום מסך"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"תצוגה מקדימה של צילום מסך"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string>
<string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"לא ניתן להסתיר את הסשן הנוכחי."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"סגירה"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 5d22c9a0c295..7e64155fae63 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"スクリーンショットを撮り直してください"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"空き容量が足りないため、スクリーンショットを保存できません"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"スクリーンショットの作成はアプリまたは組織で許可されていません"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"スクリーンショットの編集"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"スクリーンショットを閉じます"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"スクリーンショットのプレビュー"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string>
<string name="controls_media_title" msgid="1746947284862928133">"メディア"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"現在のセッションは非表示にできません。"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"閉じる"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"再開"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 68cd2474c5aa..a6376e37741d 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Қайта скриншот жасап көріңіз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Жадтағы шектеулі бос орынға байланысты скриншот сақталмайды"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Қолданба немесе ұйым скриншоттар түсіруге рұқсат етпейді"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Скриншотты өзгерту"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотты жабу"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотты алдын ала қарау"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Қазіргі сеансты жасыру мүмкін емес."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабу"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 55f3f44f7b1f..b8d2fa8a04ba 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"សាកល្បង​ថតរូបថត​អេក្រង់​ម្តងទៀត"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"មិនអាច​រក្សាទុក​រូបថតអេក្រង់​បានទេ ​ដោយសារ​ទំហំផ្ទុក​មានកម្រិតទាប"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ការថត​រូបអេក្រង់​មិនត្រូវ​បាន​អនុញ្ញាត​ដោយ​កម្មវិធី​នេះ ឬ​ស្ថាប័ន​របស់អ្នក"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"កែ​រូបថត​អេក្រង់"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ច្រានចោល​រូបថត​អេក្រង់"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ការមើល​រូបថត​អេក្រង់​សាកល្បង"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថត​អេក្រង់"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុក​ការណែនាំ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គ​បច្ចុប្បន្ន។"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"មិនអាចលាក់​វគ្គបច្ចុប្បន្នបានទេ។"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ច្រាន​ចោល"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 569837bc6612..5c502acc1e50 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ಪುನಃ ತೆಗೆದುಕೊಳ್ಳಲು ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸಂಸ್ಥೆಯು ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ಗಳನ್ನು ತೆಗೆಯುವುದನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ ಅನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ಸ್ಕ್ರೀನ್‍ಶಾಟ್‍ನ ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ವಜಾಗೊಳಿಸಿ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 1fdff749151f..cef515827b7d 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"스크린샷을 다시 찍어 보세요."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"앱이나 조직에서 스크린샷 촬영을 허용하지 않습니다."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"스크린샷 수정"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"스크린샷 닫기"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"스크린샷 미리보기"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string>
<string name="controls_media_title" msgid="1746947284862928133">"미디어"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"현재 세션은 숨길 수 없습니다."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"닫기"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 970d5fab09cb..572c3e7944c8 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Скриншотту кайра тартып көрүңүз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сактагычта бош орун аз болгондуктан, скриншот сакталбай жатат"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Скриншот тартууга колдонмо же ишканаңыз тыюу салган."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Скриншотту түзөтүү"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотту четке кагуу"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотту алдын ала көрүү"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Учудагы сеансты жашырууга болбойт."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабуу"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 9f0f0ae0eb59..1cc1b4e4b6df 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1064,7 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string>
- <string name="controls_media_active_session" msgid="1984383994625845642">"ບໍ່ສາມາດເຊື່ອເຊດຊັນປັດຈຸບັນໄດ້."</string>
+ <string name="controls_media_active_session" msgid="1984383994625845642">"ບໍ່ສາມາດເຊື່ອງເຊດຊັນປັດຈຸບັນໄດ້."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ປິດໄວ້"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 81696c2c88df..152a5323536b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pabandykite padaryti ekrano kopiją dar kartą"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Redaguoti ekrano kopiją"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Praleisti ekrano kopiją"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrano kopijos peržiūra"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medija"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Dabartinio seanso paslėpti negalima."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Atsisakyti"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index d37dd914865c..ae67d0f22997 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Mēģiniet izveidot jaunu ekrānuzņēmumu."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Lietotne vai jūsu organizācija neatļauj veikt ekrānuzņēmumus."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Rediģēt ekrānuzņēmumu"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Nerādīt ekrānuzņēmumu"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrānuzņēmuma priekšskatījums"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
@@ -1071,8 +1070,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Pašreizējo sesiju nevar paslēpt"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Nerādīt"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 28c4ee7fc951..fca688d28f8e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Повторно обидете се да направите слика од екранот"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сликата од екранот не може да се зачува поради ограничена меморија"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликацијата или вашата организација не дозволува снимање слики од екранот"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Изменете ја сликата од екранот"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отфрлете ја сликата од екранот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед на слика од екранот"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Тековната сесија не може да се сокрие."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отфрли"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index f94264103549..2a6ed6e9bf65 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"സ്‌ക്രീൻഷോട്ട് എടുക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"സ്‌റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കാനാകുന്നില്ല"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"സ്ക്രീൻഷോട്ട് എഡിറ്റ് ചെയ്യുക"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"സ്ക്രീൻഷോട്ട് ഡിസ്‌മിസ് ചെയ്യുക"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"സ്‌ക്രീൻഷോട്ട് പ്രിവ്യു"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string>
<string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്‌ക്കുക."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"നിലവിലെ സെഷൻ മറയ്ക്കാനാകില്ല."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index fc41f6cc0739..4eb2fb1076c3 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Дэлгэцийн зургийг дахин дарж үзнэ үү"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сангийн багтаамж бага байгаа тул дэлгэцээс дарсан зургийг хадгалах боломжгүй байна"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Таны апп, байгууллагад дэлгэцийн зураг авахыг зөвшөөрдөггүй"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Дэлгэцийн агшныг засах"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Дэлгэцийн агшныг хаах"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Дэлгэцийн агшныг урьдчилан үзэх"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Одоогийн харилцан үйлдлийг нуух боломжгүй."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Хаах"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 2b01fef142bd..57c1e07d3802 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मर्यादित स्टोरेज जागेमुळे स्क्रीनशॉट सेव्ह करू शकत नाही"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अ‍ॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रीनशॉट संपादित करा"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट डिसमिस करा"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
@@ -115,10 +114,10 @@
<string name="screenrecord_delete_error" msgid="2870506119743013588">"स्क्रीन रेकॉर्डिंग हटवताना एरर आली"</string>
<string name="screenrecord_permission_error" msgid="7856841237023137686">"परवानग्या मिळवता आल्या नाहीत"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन रेकॉर्डिंग सुरू करताना एरर आली"</string>
- <string name="usb_preference_title" msgid="1439924437558480718">"USB फाईल स्थानांतरण पर्याय"</string>
+ <string name="usb_preference_title" msgid="1439924437558480718">"USB फाइल स्थानांतरण पर्याय"</string>
<string name="use_mtp_button_title" msgid="5036082897886518086">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string>
<string name="use_ptp_button_title" msgid="7676427598943446826">"कॅमेरा म्हणून माउंट करा (PTP)"</string>
- <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac साठी Android फाईल स्थानांतर अ‍ॅप इंस्टॉल करा"</string>
+ <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac साठी Android फाइल स्थानांतर अ‍ॅप इंस्टॉल करा"</string>
<string name="accessibility_back" msgid="6530104400086152611">"मागे"</string>
<string name="accessibility_home" msgid="5430449841237966217">"होम"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"मेनू"</string>
@@ -290,7 +289,7 @@
<string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"स्थान अहवाल बंद केला."</string>
<string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"स्थान अहवाल सुरू केला."</string>
<string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> साठी अलार्म सेट केला."</string>
- <string name="accessibility_quick_settings_close" msgid="2974895537860082341">"पॅनेल बंद करा."</string>
+ <string name="accessibility_quick_settings_close" msgid="2974895537860082341">"पॅनल बंद करा."</string>
<string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"अधिक वेळ."</string>
<string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"कमी वेळ."</string>
<string name="accessibility_quick_settings_flashlight_off" msgid="7606563260714825190">"फ्लॅशलाइट बंद."</string>
@@ -626,10 +625,10 @@
<string name="qs_status_phone_vibrate" msgid="7055409506885541979">"फोन व्हायब्रेटवर आहे"</string>
<string name="qs_status_phone_muted" msgid="3763664791309544103">"फोन म्यूट केला"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. सशब्द करण्यासाठी टॅप करा."</string>
- <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
- <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. नि:शब्द करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
+ <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा म्यूट केल्या जाऊ शकतात."</string>
+ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. नि:शब्द करण्यासाठी टॅप करा."</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. म्यूट करण्यासाठी टॅप करा."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"सध्याचे सेशन लपवता येणार नाही."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"डिसमिस करा"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 5b0f91f5f967..058055f54c95 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Cuba ambil tangkapan skrin sekali lagi"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Edit tangkapan skrin"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ketepikan tangkapan skrin"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Sesi semasa tidak boleh disembunyikan."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tolak"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index b814607226e3..5249efa4d48e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"မျက်နှာပြင်ပုံကို ထပ်ရိုက်ကြည့်ပါ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"ဖန်သားပြင်ဓာတ်ပုံကို တည်းဖြတ်သည်"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ဖန်သားပြင်ဓာတ်ပုံ ပယ်ရန်"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"အကြံပြုချက်များ ဖွင့်နေသည်"</string>
<string name="controls_media_title" msgid="1746947284862928133">"မီဒီယာ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"လက်ရှိ စက်ရှင်ကို ဖျောက်ထားမည်။"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"လက်ရှိစက်ရှင်ကို ဝှက်၍မရနိုင်ပါ။"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ပယ်ရန်"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 5eea7fd4abb1..c3c8903c809c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रिनसट सम्पादन गर्नुहोस्"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रिनसट हटाउनुहोस्"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"हाल चलिरहेको सत्र लुकाउन सकिँदैन।"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"हटाउनुहोस्"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 2126b1a451f7..691bfcbc57f7 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer opnieuw een screenshot te maken"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan screenshot niet opslaan vanwege beperkte opslagruimte"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Het maken van screenshots wordt niet toegestaan door de app of je organisatie"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Screenshot bewerken"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot sluiten"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Voorbeeld van screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"De huidige sessie kan niet worden verborgen."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Sluiten"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index b55fd67ba6a1..de48527f1980 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ପୁଣିଥରେ ସ୍କ୍ରୀନ୍‌ଶଟ୍ ନେବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ସୀମିତ ଷ୍ଟୋରେଜ୍‍ ସ୍ପେସ୍‍ ହେତୁ ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ ହୋଇପାରିବ ନାହିଁ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ଆପ୍‍ କିମ୍ବା ସଂସ୍ଥା ଦ୍ୱାରା ସ୍କ୍ରୀନଶଟ୍‍ ନେବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"ସ୍କ୍ରିନସଟକୁ ଏଡିଟ୍ କରନ୍ତୁ"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ସ୍କ୍ରିନସଟର ପ୍ରିଭ୍ୟୁ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"ବର୍ତ୍ତମାନର ସେସନକୁ ଲୁଚାଯାଇପାରିବ ନାହିଁ।"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ଖାରଜ କରନ୍ତୁ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 9714f34ab0cc..13f45067ce7a 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੁਬਾਰਾ ਲੈ ਕੇ ਦੇਖੋ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ਸੀਮਿਤ ਸਟੋਰੇਜ ਹੋਣ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਖਾਰਜ ਕਰੋ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਪੂਰਵ-ਝਲਕ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ਖਾਰਜ ਕਰੋ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index f5bcdba5cede..6da167dffd34 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Spróbuj jeszcze raz wykonać zrzut ekranu"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nie możesz wykonać zrzutu ekranu, bo nie zezwala na to aplikacja lub Twoja organizacja."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Edytuj zrzut ekranu"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zamknij zrzut ekranu"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Podgląd zrzutu ekranu"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Nie można ukryć bieżącej sesji."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odrzuć"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 9c73e5a1fcfd..24e71fbe1392 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încercați să faceți din nou o captură de ecran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Editați captura de ecran"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Închideți captura de ecran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
@@ -1071,8 +1070,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Sesiunea actuală nu se poate ascunde."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Închideți"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b51a5b2d1644..1ca1c80738c2 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Попробуйте сделать скриншот снова."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не удалось сохранить скриншот: недостаточно места."</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Изменить скриншот"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрыть скриншот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Скрыть текущий сеанс нельзя."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Скрыть"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 5d019e949a17..eb789c1e9181 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"තිර රුව නැවත ගැනීමට උත්සාහ කරන්න"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"තිර රුව සංස්කරණය කරන්න"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"තිර රුව ඉවත ලන්න"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"තිර රූ පෙර දසුන"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"නිර්දේශ පූරණය කරමින්"</string>
<string name="controls_media_title" msgid="1746947284862928133">"මාධ්‍ය"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"වත්මන් සැසිය සඟවන්න."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"වත්මන් සැසිය සැඟවිය නොහැකිය."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ඉවත ලන්න"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 88cc1a2cab60..9ec0771c89f9 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skúste snímku urobiť znova"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímka obrazovky sa nedá uložiť z dôvodu nedostatku miesta v úložisku"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Vytváranie snímok obrazovky je zakázané aplikáciou alebo vašou organizáciou"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Upraviť snímku obrazovky"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavrieť snímku obrazovky"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ukážka snímky obrazovky"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuálnu reláciu nie je možné skryť."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavrieť"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index d1d14b26ebcf..14f6553ddcfc 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Poskusite znova ustvariti posnetek zaslona"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ali vaša organizacija ne dovoljuje posnetkov zaslona"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Urejanje posnetka zaslona"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Opusti posnetek zaslona"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Predogled posnetka zaslona"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Trenutne seje ni mogoče skriti."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Opusti"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 8d79aa423691..87ca508d3893 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Provo ta nxjerrësh përsëri pamjen e ekranit"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Modifiko pamjen e ekranit"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hiq pamjen e ekranit"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Sesioni aktual nuk mund të fshihet."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hiq"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5affcdfb31a0..cd958854f53e 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Пробајте да поново направите снимак екрана"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Чување снимка екрана није успело због ограниченог меморијског простора"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликација или организација не дозвољавају прављење снимака екрана"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Измените снимак екрана"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Одбаците снимак екрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед снимка екрана"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string>
@@ -1071,8 +1070,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медији"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Актуелна сесија не може да се сакрије."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Одбаци"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index a51ed2879cb5..d34c814f2aa7 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Testa att ta en skärmdump igen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisationen tillåter inte att du tar skärmdumpar"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Redigera skärmdump"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Stäng skärmdump"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Förhandsgranskning av skärmdump"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Det går inte att dölja den aktuella sessionen."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Stäng"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 3c1c12ac8ea0..a72ebeff0ddc 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ஸ்கிரீன் ஷாட்டை மீண்டும் எடுக்க முயலவும்"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"போதுமான சேமிப்பிடம் இல்லாததால் ஸ்கிரீன்ஷாட்டைச் சேமிக்க முடியவில்லை"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ஸ்கிரீன் ஷாட்டுகளை எடுப்பதை, ஆப்ஸ் அல்லது உங்கள் நிறுவனம் அனுமதிக்கவில்லை"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"ஸ்கிரீன்ஷாட்டைத் திருத்தும்"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ஸ்கிரீன்ஷாட்டை நிராகரி"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ஸ்கிரீன்ஷாட்டின் மாதிரிக்காட்சி"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string>
<string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"தற்போதைய அமர்வை மறைக்க முடியாது."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"மூடுக"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 2680d22efb97..0d23f25409b4 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"స్క్రీన్‌షాట్ తీయడానికి మళ్లీ ప్రయత్నించండి"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"నిల్వ స్థలం పరిమితంగా ఉన్న కారణంగా స్క్రీన్‌షాట్‌ను సేవ్ చేయడం సాధ్యపడదు"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"స్క్రీన్‌షాట్‌లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"స్క్రీన్‌షాట్‌ను ఎడిట్ చేస్తుంది"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"స్క్రీన్‌షాట్‌ను మూసివేస్తుంది"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్‌షాట్ ప్రివ్యూ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string>
<string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్‌ను దాచు."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"ప్రస్తుత సెషన్‌ను దాచడం సాధ్యం కాదు."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"విస్మరించు"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్‌లు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 2e4682bd2541..094cb765a49b 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ลองบันทึกภาพหน้าจออีกครั้ง"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"บันทึกภาพหน้าจอไม่ได้เนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"แก้ไขภาพหน้าจอ"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ปิดภาพหน้าจอ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมอัดหน้าจอ"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"ซ่อนเซสชันปัจจุบันไม่ได้"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ปิด"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index ae69c4fc3a04..8ef18697cd4d 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Subukang kumuhang muli ng screenshot"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"I-edit ang screenshot"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"I-dismiss ang screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Hindi maitatago ang kasalukuyang session."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"I-dismiss"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 026517671be2..c17c8f710079 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tekrar ekran görüntüsü almayı deneyin"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Uygulama veya kuruluşunuz, ekran görüntüsü alınmasına izin vermiyor."</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Ekran görüntüsünü düzenleyin"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran görüntüsünü kapat"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran görüntüsü önizlemesi"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medya"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Mevcut oturum gizlenemez."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Kapat"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c9fd5032609e..7dcfc3b175ac 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Спробуйте зробити знімок екрана ще раз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Додаток або адміністратор вашої організації не дозволяють робити знімки екрана"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Редагувати знімок екрана"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрити знімок екрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Перегляд знімка екрана"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Відеозапис екрана"</string>
@@ -1077,8 +1076,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Поточний сеанс не можна приховати."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Закрити"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 1bfa85394559..4c0ebaad9275 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوبارہ اسکرین شاٹ لینے کی کوشش کریں"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"اسٹوریج کی محدود جگہ کی وجہ سے اسکرین شاٹ کو محفوظ نہیں کیا جا سکتا"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"اسکرین شاٹ میں ترمیم کریں"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"اسکرین شاٹ برخاست کریں"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string>
<string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"موجودہ سیشن کو چھپایا نہیں جا سکتا۔"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"برخاست کریں"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 332a26daee92..33d4f4ca14b7 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1064,7 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string>
- <string name="controls_media_active_session" msgid="1984383994625845642">"Joriy seans berkilmaydi."</string>
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Joriy seansni berkitish imkonsiz."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Yopish"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 947bd435c777..179f7c265496 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Hãy thử chụp lại màn hình"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ứng dụng hoặc tổ chức của bạn không cho phép chụp ảnh màn hình"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Chỉnh sửa ảnh chụp màn hình"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Đóng ảnh chụp màn hình"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Xem trước ảnh chụp màn hình"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Bạn không thể ẩn phiên hiện tại."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Đóng"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 730fa81a5975..e1f483c78e7f 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"请再次尝试截屏"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由于存储空间有限,无法保存屏幕截图"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"此应用或您所在的单位不允许进行屏幕截图"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"编辑屏幕截图"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"关闭屏幕截图"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"屏幕截图预览"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string>
<string name="controls_media_title" msgid="1746947284862928133">"媒体"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"无法隐藏当前会话。"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"关闭"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 42361079a9bb..f986ef33ca28 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再嘗試拍攝螢幕擷取畫面"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕擷取畫面"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"應用程式或您的機構不允許擷取螢幕畫面"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"編輯螢幕截圖"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string>
<string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"無法隱藏目前的工作階段。"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index c7407ef03730..b442b792e95f 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再次嘗試拍攝螢幕截圖"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕截圖"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"這個應用程式或貴機構不允許擷取螢幕畫面"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"編輯螢幕截圖"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string>
<string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"無法隱藏目前的工作階段。"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index dd7f6f65b86c..764f0b719702 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zama ukuthatha isithombe-skrini futhi"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Hlela isithombe-skrini"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cashisa isithombe-skrini"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ukubuka kuqala isithombe-skrini"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Iseshini yamanje ayikwazi ukufihlwa."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cashisa"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
new file mode 100644
index 000000000000..f08c8c80f961
--- /dev/null
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="72dp"
+ android:minHeight="150dp"
+ android:updatePeriodMillis="60000"
+ android:previewImage="@drawable/cloud"
+ android:resizeMode="horizontal|vertical"
+ android:initialLayout="@layout/people_space_widget">
+</appwidget-provider>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 9d3620f34186..3de0b4b78e06 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -61,18 +61,28 @@ public class ThumbnailData {
snapshotId = 0;
}
- public ThumbnailData(TaskSnapshot snapshot) {
+ private static Bitmap makeThumbnail(TaskSnapshot snapshot) {
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+ Bitmap thumbnail = null;
+ try {
+ if (buffer != null) {
+ thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
+ }
+ } catch (IllegalArgumentException ex) {
// TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
- + buffer);
+ + buffer, ex);
+ }
+ if (thumbnail == null) {
Point taskSize = snapshot.getTaskSize();
thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
thumbnail.eraseColor(Color.BLACK);
- } else {
- thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
}
+ return thumbnail;
+ }
+
+ public ThumbnailData(TaskSnapshot snapshot) {
+ thumbnail = makeThumbnail(snapshot);
insets = new Rect(snapshot.getContentInsets());
orientation = snapshot.getOrientation();
rotation = snapshot.getRotation();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 44372d76bb23..8d010c743d81 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -62,21 +62,6 @@ public abstract class TaskStackChangeListener {
onActivityLaunchOnSecondaryDisplayRerouted();
}
- /**
- * Called when contents are drawn for the first time on a display which can only contain one
- * task.
- *
- * @param displayId the id of the display on which contents are drawn.
- */
- public void onSingleTaskDisplayDrawn(int displayId) { }
-
- /**
- * Called when the last task is removed from a display which can only contain one task.
- *
- * @param displayId the id of the display from which the window is removed.
- */
- public void onSingleTaskDisplayEmpty(int displayId) {}
-
public void onTaskProfileLocked(int taskId, int userId) { }
public void onTaskCreated(int taskId, ComponentName componentName) { }
public void onTaskRemoved(int taskId) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 765cd3de4222..adaee5557a8d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -90,13 +90,11 @@ public class TaskStackChangeListeners extends TaskStackListener {
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
- private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
- private static final int ON_TASK_DISPLAY_CHANGED = 20;
- private static final int ON_TASK_LIST_UPDATED = 21;
- private static final int ON_SINGLE_TASK_DISPLAY_EMPTY = 22;
- private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 23;
- private static final int ON_TASK_DESCRIPTION_CHANGED = 24;
- private static final int ON_ACTIVITY_ROTATION = 25;
+ private static final int ON_TASK_DISPLAY_CHANGED = 19;
+ private static final int ON_TASK_LIST_UPDATED = 20;
+ private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 21;
+ private static final int ON_TASK_DESCRIPTION_CHANGED = 22;
+ private static final int ON_ACTIVITY_ROTATION = 23;
/**
* List of {@link TaskStackChangeListener} registered from {@link #addListener}.
@@ -257,18 +255,6 @@ public class TaskStackChangeListeners extends TaskStackListener {
}
@Override
- public void onSingleTaskDisplayDrawn(int displayId) {
- mHandler.obtainMessage(ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
- 0 /* unused */).sendToTarget();
- }
-
- @Override
- public void onSingleTaskDisplayEmpty(int displayId) {
- mHandler.obtainMessage(ON_SINGLE_TASK_DISPLAY_EMPTY, displayId,
- 0 /* unused */).sendToTarget();
- }
-
- @Override
public void onTaskDisplayChanged(int taskId, int newDisplayId) {
mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
}
@@ -419,18 +405,6 @@ public class TaskStackChangeListeners extends TaskStackListener {
}
break;
}
- case ON_SINGLE_TASK_DISPLAY_DRAWN: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onSingleTaskDisplayDrawn(msg.arg1);
- }
- break;
- }
- case ON_SINGLE_TASK_DISPLAY_EMPTY: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onSingleTaskDisplayEmpty(msg.arg1);
- }
- break;
- }
case ON_TASK_DISPLAY_CHANGED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2);
diff --git a/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
index b59638802756..7cf1bd0b3e79 100644
--- a/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
+++ b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
@@ -61,7 +61,6 @@ public class GradientTextClock extends TextClock {
@Override
public void refreshTime() {
- updatePaint();
super.refreshTime();
}
@@ -77,6 +76,7 @@ public class GradientTextClock extends TextClock {
public void setGradientColors(int[] colors) {
mGradientColors = colors;
+ updatePaint();
}
public void setColorPositions(float[] positions) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 53f847434dcc..89911e01cde9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -81,8 +81,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
abstract void resetState();
@Override
- public void init() {
- super.init();
+ public void initInternal() {
mMessageAreaController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index cf363cc649f0..c6ee15fcf4e3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -15,6 +15,7 @@ import android.transition.TransitionValues;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
+import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -149,6 +150,11 @@ public class KeyguardClockSwitch extends RelativeLayout {
mKeyguardStatusArea.getLayoutParams();
if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+ final int startEndPadding = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 12,
+ getResources().getDisplayMetrics());
+ setPaddingRelative(startEndPadding, 0, startEndPadding, 0);
mSmallClockFrame.setVisibility(GONE);
mNewLockscreenClockFrame.setVisibility(VISIBLE);
mNewLockscreenClockViewController.init();
@@ -157,6 +163,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view);
statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
} else {
+ setPaddingRelative(0, 0, 0, 0);
mSmallClockFrame.setVisibility(VISIBLE);
mNewLockscreenClockFrame.setVisibility(GONE);
@@ -298,6 +305,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
if (mClockPlugin != null) {
mClockPlugin.setDarkAmount(darkAmount);
}
+ mNewLockscreenClockViewController.setDarkAmount(darkAmount);
updateBigClockAlpha();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 1562444f6f87..ea9883023a7c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -92,8 +92,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Attach the controller to the view it relates to.
*/
@Override
- public void init() {
- super.init();
+ public void initInternal() {
mKeyguardSliceViewController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 351369c51364..1d12c1a7abdf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -178,8 +178,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
}
/** Initialize the Controller. */
- public void init() {
- super.init();
+ public void initInternal() {
mKeyguardSecurityContainerController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 3db9db7be00c..94913c80ac5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -190,8 +190,8 @@ public class KeyguardPatternViewController
}
@Override
- public void init() {
- super.init();
+ public void initInternal() {
+ super.initInternal();
mMessageAreaController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1c23605a8516..e9173a3ca3a0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -169,8 +169,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
@Override
- public void init() {
- super.init();
+ public void initInternal() {
mSecurityViewFlipperController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 7705db4e3147..0a57b09a5541 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -70,8 +70,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
@Override
- public void init() {
- super.init();
+ public void initInternal() {
mKeyguardClockSwitchController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
index 4f1963e5b534..3cbae0a18937 100644
--- a/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
@@ -16,6 +16,11 @@
package com.android.keyguard;
+import android.util.MathUtils;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
import com.android.systemui.util.ViewController;
import java.util.Calendar;
@@ -28,6 +33,13 @@ public class TimeBasedColorsClockController extends ViewController<GradientTextC
private final int[] mGradientColors = new int[3];
private final float[] mPositions = new float[3];
+ /**
+ * 0 = fully awake
+ * between 0 and 1 = transitioning between awake and doze
+ * 1 = fully in doze
+ */
+ private float mDarkAmount = 0f;
+
public TimeBasedColorsClockController(GradientTextClock view) {
super(view);
}
@@ -46,14 +58,29 @@ public class TimeBasedColorsClockController extends ViewController<GradientTextC
* Updates the time for this view. Also updates any color changes.
*/
public void refreshTime(long timeInMillis) {
- Calendar now = new GregorianCalendar();
- now.setTimeInMillis(timeInMillis);
- updateColors(now);
- updatePositions(now);
+ updateColors(timeInMillis);
+ updatePositions(timeInMillis);
mView.refreshTime();
}
- private int getTimeIndex(Calendar now) {
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ public void setDarkAmount(float darkAmount) {
+ mDarkAmount = darkAmount;
+
+ // TODO: (b/170228350) currently this relayouts throughout the animation;
+ // eventually this should use new Text APIs to animate the variable font weight
+ refreshTime(System.currentTimeMillis());
+
+ int weight = (int) MathUtils.lerp(200, 400, 1f - darkAmount);
+ mView.setFontVariationSettings("'wght' " + weight);
+ }
+
+ private int getTimeIndex(long timeInMillis) {
+ Calendar now = getCalendar(timeInMillis);
int hour = now.get(Calendar.HOUR_OF_DAY); // 0 - 23
if (hour < mTimes[0]) {
return mTimes.length - 1;
@@ -68,16 +95,22 @@ public class TimeBasedColorsClockController extends ViewController<GradientTextC
return mTimes.length - 1;
}
- private void updateColors(Calendar now) {
- final int index = getTimeIndex(now);
+ private void updateColors(long timeInMillis) {
+ final int index = getTimeIndex(timeInMillis);
+ final int wallpaperTextColor =
+ Utils.getColorAttrDefaultColor(mView.getContext(), R.attr.wallpaperTextColor);
for (int i = 0; i < mGradientColors.length; i++) {
- mGradientColors[i] = COLORS[index][i];
+ // wallpaperTextColor on LS when mDarkAmount = 0f
+ // full color on AOD when mDarkAmount = 1f
+ mGradientColors[i] =
+ ColorUtils.blendARGB(wallpaperTextColor, COLORS[index][i], mDarkAmount);
}
mView.setGradientColors(mGradientColors);
}
- private void updatePositions(Calendar now) {
- final int index = getTimeIndex(now);
+ private void updatePositions(long timeInMillis) {
+ Calendar now = getCalendar(timeInMillis);
+ final int index = getTimeIndex(timeInMillis);
final Calendar startTime = new GregorianCalendar();
startTime.setTimeInMillis(now.getTimeInMillis());
@@ -108,6 +141,12 @@ public class TimeBasedColorsClockController extends ViewController<GradientTextC
mView.setColorPositions(mPositions);
}
+ private Calendar getCalendar(long timeInMillis) {
+ Calendar now = new GregorianCalendar();
+ now.setTimeInMillis(timeInMillis);
+ return now;
+ }
+
private static final int[] SUNRISE = new int[] {0xFF6F75AA, 0xFFAFF0FF, 0xFFFFDEBF};
private static final int[] DAY = new int[] {0xFF9BD8FB, 0xFFD7F5FF, 0xFFFFF278};
private static final int[] NIGHT = new int[] {0xFF333D5E, 0xFFC5A1D6, 0xFF907359};
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index b388a6eb5edd..3348bd1d1e23 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -39,6 +39,7 @@ import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.people.PeopleSpaceActivity;
+import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.util.NotificationChannels;
import java.lang.reflect.Constructor;
@@ -109,6 +110,7 @@ public class SystemUIApplication extends Application implements
}
}
// If flag SHOW_PEOPLE_SPACE is true, enable People Space launcher icon.
+ // TODO(b/170396074): Remove this when we don't need an icon anymore.
try {
int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SHOW_PEOPLE_SPACE);
@@ -121,6 +123,21 @@ public class SystemUIApplication extends Application implements
} catch (Exception e) {
Log.w(TAG, "Error enabling People Space launch icon:", e);
}
+
+ // If SHOW_PEOPLE_SPACE is true, enable People Space widget provider.
+ // TODO(b/170396074): Remove this when we don't need a widget anymore.
+ try {
+ int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.SHOW_PEOPLE_SPACE);
+ context.getPackageManager().setComponentEnabledSetting(
+ new ComponentName(context, PeopleSpaceWidgetProvider.class),
+ showPeopleSpace == 1
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ } catch (Exception e) {
+ Log.w(TAG, "Error enabling People Space widget:", e);
+ }
}
}, bootCompletedFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 83de3243602b..eea168ad16b3 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -112,7 +112,7 @@ open class BroadcastDispatcher constructor (
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the user of the context (system user in SystemUI).
+ * Pass `null` to use the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@@ -120,13 +120,17 @@ open class BroadcastDispatcher constructor (
open fun registerReceiver(
receiver: BroadcastReceiver,
filter: IntentFilter,
- executor: Executor? = context.mainExecutor,
- user: UserHandle = context.user
+ executor: Executor? = null,
+ user: UserHandle? = null
) {
checkFilter(filter)
this.handler
- .obtainMessage(MSG_ADD_RECEIVER,
- ReceiverData(receiver, filter, executor ?: context.mainExecutor, user))
+ .obtainMessage(MSG_ADD_RECEIVER, ReceiverData(
+ receiver,
+ filter,
+ executor ?: context.mainExecutor,
+ user ?: context.user
+ ))
.sendToTarget()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index d1630ebe8dc8..d26f7ab6d78c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -109,34 +109,45 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
super.onBind(device, topMargin, bottomMargin);
final boolean currentlyConnected = isCurrentlyConnected(device);
if (currentlyConnected) {
- mConnectedItem = mFrameLayout;
+ mConnectedItem = mContainerLayout;
+ }
+ mBottomDivider.setVisibility(View.GONE);
+ mCheckBox.setVisibility(View.GONE);
+ if (currentlyConnected && mController.isActiveRemoteDevice(device)) {
+ // Init active device layout
+ mDivider.setVisibility(View.VISIBLE);
+ mDivider.setTransitionAlpha(1);
+ mAddIcon.setVisibility(View.VISIBLE);
+ mAddIcon.setTransitionAlpha(1);
+ mAddIcon.setOnClickListener(v -> onEndItemClick());
+ } else {
+ // Init non-active device layout
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
}
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
- setTwoLineLayout(device, null /* title */, true /* bFocused */,
- false /* showSeekBar*/, true /* showProgressBar */,
- false /* showSubtitle */);
+ setTwoLineLayout(device, true /* bFocused */, false /* showSeekBar*/,
+ true /* showProgressBar */, false /* showSubtitle */);
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
}
} else {
// Set different layout for each device
if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
- setTwoLineLayout(device, null /* title */, false /* bFocused */,
- false /* showSeekBar*/, false /* showProgressBar */,
+ setTwoLineLayout(device, false /* bFocused */,
+ false /* showSeekBar */, false /* showProgressBar */,
true /* showSubtitle */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
- mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
- } else if (!mController.hasAdjustVolumeUserRestriction()
- && currentlyConnected) {
- setTwoLineLayout(device, null /* title */, true /* bFocused */,
- true /* showSeekBar*/, false /* showProgressBar */,
- false /* showSubtitle */);
+ mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+ } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
+ setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
+ false /* showProgressBar */, false /* showSubtitle */);
initSeekbar(device);
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
- mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
+ mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
}
}
}
@@ -145,13 +156,17 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
super.onBind(customizedItem, topMargin, bottomMargin);
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
+ mCheckBox.setVisibility(View.GONE);
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
+ mBottomDivider.setVisibility(View.GONE);
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add);
d.setColorFilter(new PorterDuffColorFilter(
Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
mTitleIcon.setImageDrawable(d);
- mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
+ mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
}
}
@@ -173,5 +188,9 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mController.launchBluetoothPairing();
}
}
+
+ private void onEndItemClick() {
+ mController.launchMediaOutputGroupDialog();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 2d3e77db1ea3..536b7598ce22 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -24,8 +24,9 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.widget.CheckBox;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
@@ -44,19 +45,17 @@ import com.android.systemui.R;
public abstract class MediaOutputBaseAdapter extends
RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> {
- private static final String FONT_SELECTED_TITLE = "sans-serif-medium";
- private static final String FONT_TITLE = "sans-serif";
-
static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
+ static final int CUSTOMIZED_ITEM_GROUP = 2;
final MediaOutputController mController;
- private boolean mIsDragging;
private int mMargin;
private boolean mIsAnimating;
Context mContext;
View mHolderView;
+ boolean mIsDragging;
public MediaOutputBaseAdapter(MediaOutputController controller) {
mController = controller;
@@ -99,27 +98,33 @@ public abstract class MediaOutputBaseAdapter extends
private static final int ANIM_DURATION = 200;
- final FrameLayout mFrameLayout;
+ final LinearLayout mContainerLayout;
final TextView mTitleText;
final TextView mTwoLineTitleText;
final TextView mSubTitleText;
final ImageView mTitleIcon;
- final ImageView mEndIcon;
+ final ImageView mAddIcon;
final ProgressBar mProgressBar;
final SeekBar mSeekBar;
final RelativeLayout mTwoLineLayout;
+ final View mDivider;
+ final View mBottomDivider;
+ final CheckBox mCheckBox;
MediaDeviceBaseViewHolder(View view) {
super(view);
- mFrameLayout = view.requireViewById(R.id.device_container);
+ mContainerLayout = view.requireViewById(R.id.device_container);
mTitleText = view.requireViewById(R.id.title);
mSubTitleText = view.requireViewById(R.id.subtitle);
mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
mTwoLineTitleText = view.requireViewById(R.id.two_line_title);
mTitleIcon = view.requireViewById(R.id.title_icon);
- mEndIcon = view.requireViewById(R.id.end_icon);
mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
mSeekBar = view.requireViewById(R.id.volume_seekbar);
+ mDivider = view.requireViewById(R.id.end_divider);
+ mBottomDivider = view.requireViewById(R.id.bottom_divider);
+ mAddIcon = view.requireViewById(R.id.add_icon);
+ mCheckBox = view.requireViewById(R.id.check_box);
}
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
@@ -132,11 +137,11 @@ public abstract class MediaOutputBaseAdapter extends
}
private void setMargin(boolean topMargin, boolean bottomMargin) {
- ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mContainerLayout
.getLayoutParams();
params.topMargin = topMargin ? mMargin : 0;
params.bottomMargin = bottomMargin ? mMargin : 0;
- mFrameLayout.setLayoutParams(params);
+ mContainerLayout.setLayoutParams(params);
}
void setSingleLineLayout(CharSequence title, boolean bFocused) {
@@ -146,13 +151,26 @@ public abstract class MediaOutputBaseAdapter extends
mTitleText.setTranslationY(0);
mTitleText.setText(title);
if (bFocused) {
- mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+ mTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamilyMedium),
+ Typeface.NORMAL));
} else {
- mTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ mTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
}
}
- void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+ void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
+ boolean showProgressBar, boolean showSubtitle) {
+ setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle);
+ }
+
+ void setTwoLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
+ boolean showProgressBar, boolean showSubtitle) {
+ setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle);
+ }
+
+ private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
@@ -168,18 +186,21 @@ public abstract class MediaOutputBaseAdapter extends
}
if (bFocused) {
- mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE,
+ mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamilyMedium),
Typeface.NORMAL));
} else {
- mTwoLineTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
}
}
void initSeekbar(MediaDevice device) {
mSeekBar.setMax(device.getMaxVolume());
mSeekBar.setMin(0);
- if (mSeekBar.getProgress() != device.getCurrentVolume()) {
- mSeekBar.setProgress(device.getCurrentVolume());
+ final int currentVolume = device.getCurrentVolume();
+ if (mSeekBar.getProgress() != currentVolume) {
+ mSeekBar.setProgress(currentVolume);
}
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
@@ -213,7 +234,9 @@ public abstract class MediaOutputBaseAdapter extends
}
mIsAnimating = true;
// Animation for title text
- toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+ toTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamilyMedium),
+ Typeface.NORMAL));
toTitleText.animate()
.setDuration(ANIM_DURATION)
.translationY(-delta)
@@ -234,7 +257,9 @@ public abstract class MediaOutputBaseAdapter extends
public void onAnimationEnd(Animator animation) {
final TextView fromTitleText = from.requireViewById(
R.id.two_line_title);
- fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ fromTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamily),
+ Typeface.NORMAL));
fromTitleText.animate()
.setDuration(ANIM_DURATION)
.translationY(delta)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index caef536961f1..78939dffb4fb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -119,6 +119,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
// Init device list
mDevicesRecyclerView.setLayoutManager(mLayoutManager);
mDevicesRecyclerView.setAdapter(mAdapter);
+ // Init header icon
+ mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
// Init bottom buttons
mDoneButton.setOnClickListener(v -> dismiss());
mStopButton.setOnClickListener(v -> {
@@ -218,4 +220,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
dismiss();
}
}
+
+ void onHeaderIconClick() {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b1f1bda25961..80928d6da978 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -18,10 +18,7 @@ package com.android.systemui.media.dialog;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadata;
import android.media.MediaRoute2Info;
@@ -49,6 +46,8 @@ import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
import java.util.ArrayList;
@@ -61,7 +60,7 @@ import javax.inject.Inject;
/**
* Controller for media output dialog
*/
-public class MediaOutputController implements LocalMediaManager.DeviceCallback{
+public class MediaOutputController implements LocalMediaManager.DeviceCallback {
private static final String TAG = "MediaOutputController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -71,6 +70,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
private final MediaSessionManager mMediaSessionManager;
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
+ private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
+ private final boolean mAboveStatusbar;
+ private final NotificationEntryManager mNotificationEntryManager;
@VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -82,13 +84,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
@Inject
public MediaOutputController(@NonNull Context context, String packageName,
- MediaSessionManager mediaSessionManager, LocalBluetoothManager
- lbm, ShadeController shadeController, ActivityStarter starter) {
+ boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
+ lbm, ShadeController shadeController, ActivityStarter starter,
+ NotificationEntryManager notificationEntryManager) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
mShadeController = shadeController;
mActivityStarter = starter;
+ mAboveStatusbar = aboveStatusbar;
+ mNotificationEntryManager = notificationEntryManager;
InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
}
@@ -194,7 +199,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
if (DEBUG) {
Log.d(TAG, "Media meta data does not contain icon information");
}
- return getPackageIcon();
+ return getNotificationIcon();
}
IconCompat getDeviceIconCompat(MediaDevice device) {
@@ -210,24 +215,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
return BluetoothUtils.createIconWithDrawable(drawable);
}
- private IconCompat getPackageIcon() {
+ IconCompat getNotificationIcon() {
if (TextUtils.isEmpty(mPackageName)) {
return null;
}
- try {
- final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName);
- if (drawable instanceof BitmapDrawable) {
- return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap());
- }
- final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- return IconCompat.createWithBitmap(bitmap);
- } catch (PackageManager.NameNotFoundException e) {
- if (DEBUG) {
- Log.e(TAG, "Package is not found. Unable to get package icon.");
+ for (NotificationEntry entry
+ : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
+ if (entry.getSbn().getNotification().hasMediaSession()
+ && TextUtils.equals(entry.getSbn().getPackageName(), mPackageName)) {
+ return IconCompat.createFromIcon(entry.getSbn().getNotification().getLargeIcon());
}
}
return null;
@@ -271,6 +267,42 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
mMediaDevices.addAll(targetMediaDevices);
}
+ List<MediaDevice> getGroupMediaDevices() {
+ final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
+ final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
+ if (mGroupMediaDevices.isEmpty()) {
+ mGroupMediaDevices.addAll(selectedDevices);
+ mGroupMediaDevices.addAll(selectableDevices);
+ return mGroupMediaDevices;
+ }
+ // To keep the same list order
+ final Collection<MediaDevice> sourceDevices = new ArrayList<>();
+ final Collection<MediaDevice> targetMediaDevices = new ArrayList<>();
+ sourceDevices.addAll(selectedDevices);
+ sourceDevices.addAll(selectableDevices);
+ for (MediaDevice originalDevice : mGroupMediaDevices) {
+ for (MediaDevice newDevice : sourceDevices) {
+ if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
+ targetMediaDevices.add(newDevice);
+ sourceDevices.remove(newDevice);
+ break;
+ }
+ }
+ }
+ // Add new devices at the end of list if necessary
+ if (!sourceDevices.isEmpty()) {
+ targetMediaDevices.addAll(sourceDevices);
+ }
+ mGroupMediaDevices.clear();
+ mGroupMediaDevices.addAll(targetMediaDevices);
+
+ return mGroupMediaDevices;
+ }
+
+ void resetGroupMediaDevices() {
+ mGroupMediaDevices.clear();
+ }
+
void connectDevice(MediaDevice device) {
ThreadUtils.postOnBackgroundThread(() -> {
mLocalMediaManager.connectDevice(device);
@@ -309,15 +341,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
return mLocalMediaManager.getDeselectableMediaDevice();
}
- boolean isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice) {
- for (MediaDevice device : deviceCollection) {
- if (TextUtils.equals(device.getId(), targetDevice.getId())) {
- return true;
- }
- }
- return false;
- }
-
void adjustSessionVolume(String sessionId, int volume) {
mLocalMediaManager.adjustSessionVolume(sessionId, volume);
}
@@ -407,6 +430,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
}
+ void launchMediaOutputDialog() {
+ mCallback.dismissDialog();
+ new MediaOutputDialog(mContext, mAboveStatusbar, this);
+ }
+
+ void launchMediaOutputGroupDialog() {
+ mCallback.dismissDialog();
+ new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+ }
+
boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
final List<String> features = device.getFeatures();
return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 4cdca4cbcf1e..7d1a7ced7472 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.media.session.MediaSessionManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.phone.ShadeController
import javax.inject.Inject
@@ -31,7 +32,8 @@ class MediaOutputDialogFactory @Inject constructor(
private val mediaSessionManager: MediaSessionManager,
private val lbm: LocalBluetoothManager?,
private val shadeController: ShadeController,
- private val starter: ActivityStarter
+ private val starter: ActivityStarter,
+ private val notificationEntryManager: NotificationEntryManager
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
@@ -40,9 +42,8 @@ class MediaOutputDialogFactory @Inject constructor(
/** Creates a [MediaOutputDialog] for the given package. */
fun create(packageName: String, aboveStatusBar: Boolean) {
mediaOutputDialog?.dismiss()
-
- mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm,
- shadeController, starter).run {
+ mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
+ mediaSessionManager, lbm, shadeController, starter, notificationEntryManager).run {
MediaOutputDialog(context, aboveStatusBar, this) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
new file mode 100644
index 000000000000..ceb4495bd8e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 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.media.dialog;
+
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Adapter for media output dynamic group dialog.
+ */
+public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {
+
+ private static final String TAG = "MediaOutputGroupAdapter";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final List<MediaDevice> mGroupMediaDevices;
+
+ public MediaOutputGroupAdapter(MediaOutputController controller) {
+ super(controller);
+ mGroupMediaDevices = controller.getGroupMediaDevices();
+ }
+
+ @Override
+ public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+ int viewType) {
+ super.onCreateViewHolder(viewGroup, viewType);
+
+ return new GroupViewHolder(mHolderView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
+ // Add "Group"
+ if (position == 0) {
+ viewHolder.onBind(CUSTOMIZED_ITEM_GROUP, true /* topMargin */,
+ false /* bottomMargin */);
+ return;
+ }
+ // Add available devices
+ final int newPosition = position - 1;
+ final int size = mGroupMediaDevices.size();
+ if (newPosition < size) {
+ viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */,
+ newPosition == (size - 1) /* bottomMargin */);
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Incorrect position: " + position);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ // Require extra item for group volume operation
+ return mGroupMediaDevices.size() + 1;
+ }
+
+ @Override
+ CharSequence getItemTitle(MediaDevice device) {
+ return super.getItemTitle(device);
+ }
+
+ class GroupViewHolder extends MediaDeviceBaseViewHolder {
+
+ GroupViewHolder(View view) {
+ super(view);
+ }
+
+ @Override
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
+ super.onBind(device, topMargin, bottomMargin);
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
+ mBottomDivider.setVisibility(View.GONE);
+ mCheckBox.setVisibility(View.VISIBLE);
+ mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ onCheckBoxClicked(isChecked, device);
+ });
+ setTwoLineLayout(device, false /* bFocused */, true /* showSeekBar */,
+ false /* showProgressBar */, false /* showSubtitle*/);
+ initSeekbar(device);
+ final List<MediaDevice> selectedDevices = mController.getSelectedMediaDevice();
+ if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+ mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
+ mCheckBox.setChecked(false);
+ mCheckBox.setEnabled(true);
+ } else if (isDeviceIncluded(selectedDevices, device)) {
+ if (selectedDevices.size() == 1 || !isDeviceIncluded(
+ mController.getDeselectableMediaDevice(), device)) {
+ mCheckBox.setButtonDrawable(getDisabledCheckboxDrawable());
+ mCheckBox.setChecked(true);
+ mCheckBox.setEnabled(false);
+ } else {
+ mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
+ mCheckBox.setChecked(true);
+ mCheckBox.setEnabled(true);
+ }
+ }
+ }
+
+ @Override
+ void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
+ super.onBind(customizedItem, topMargin, bottomMargin);
+ if (customizedItem == CUSTOMIZED_ITEM_GROUP) {
+ setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group),
+ true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
+ false /* showSubtitle*/);
+ mTitleIcon.setImageDrawable(getSpeakerDrawable());
+ mBottomDivider.setVisibility(View.VISIBLE);
+ mCheckBox.setVisibility(View.GONE);
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
+ initSessionSeekbar();
+ }
+ }
+
+ private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
+ if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+ mController.addDeviceToPlayMedia(device);
+ } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
+ device)) {
+ mController.removeDeviceFromPlayMedia(device);
+ }
+ }
+
+ private void initSessionSeekbar() {
+ mSeekBar.setMax(mController.getSessionVolumeMax());
+ mSeekBar.setMin(0);
+ final int currentVolume = mController.getSessionVolume();
+ if (mSeekBar.getProgress() != currentVolume) {
+ mSeekBar.setProgress(currentVolume);
+ }
+ mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (!fromUser) {
+ return;
+ }
+ mController.adjustSessionVolume(progress);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ mIsDragging = true;
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mIsDragging = false;
+ }
+ });
+ }
+
+ private Drawable getDisabledCheckboxDrawable() {
+ final Drawable drawable = mContext.getDrawable(R.drawable.ic_check_box_blue_24dp)
+ .mutate();
+ final Bitmap checkbox = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(checkbox);
+ TypedValue value = new TypedValue();
+ mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true);
+ drawable.setAlpha((int) (value.getFloat() * 255));
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+
+ return drawable;
+ }
+
+ private Drawable getSpeakerDrawable() {
+ final Drawable drawable = mContext.getDrawable(R.drawable.ic_speaker_group_black_24dp)
+ .mutate();
+ final ColorStateList list = mContext.getResources().getColorStateList(
+ R.color.advanced_icon_color, mContext.getTheme());
+ drawable.setColorFilter(new PorterDuffColorFilter(list.getDefaultColor(),
+ PorterDuff.Mode.SRC_IN));
+ return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
+ }
+
+ private boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) {
+ for (MediaDevice device : deviceList) {
+ if (TextUtils.equals(device.getId(), targetDevice.getId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
new file mode 100644
index 000000000000..407930492fbe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 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.media.dialog;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.core.graphics.drawable.IconCompat;
+
+import com.android.systemui.R;
+
+/**
+ * Dialog for media output group.
+ */
+public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
+
+ MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
+ mediaOutputController) {
+ super(context, mediaOutputController);
+ mMediaOutputController.resetGroupMediaDevices();
+ mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
+ if (!aboveStatusbar) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+ show();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ int getHeaderIconRes() {
+ return R.drawable.ic_arrow_back;
+ }
+
+ @Override
+ IconCompat getHeaderIcon() {
+ return null;
+ }
+
+ @Override
+ int getHeaderIconSize() {
+ return mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_header_back_icon_size);
+ }
+
+ @Override
+ CharSequence getHeaderText() {
+ return mContext.getString(R.string.media_output_dialog_add_output);
+ }
+
+ @Override
+ CharSequence getHeaderSubtitle() {
+ final int size = mMediaOutputController.getSelectedMediaDevice().size();
+ if (size == 1) {
+ return mContext.getText(R.string.media_output_dialog_single_device);
+ }
+ return mContext.getString(R.string.media_output_dialog_multiple_devices, size);
+ }
+
+ @Override
+ int getStopButtonVisibility() {
+ return View.VISIBLE;
+ }
+
+ @Override
+ void onHeaderIconClick() {
+ mMediaOutputController.launchMediaOutputDialog();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index bdaeb137e368..6a78c64638aa 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -18,29 +18,20 @@ package com.android.systemui.people;
import android.app.Activity;
import android.app.INotificationManager;
-import android.app.people.ConversationChannel;
import android.app.people.IPeopleManager;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
-import android.icu.text.MeasureFormat;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
import android.os.Bundle;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
import android.util.Log;
import android.view.ViewGroup;
import com.android.systemui.R;
-import java.time.Duration;
import java.util.List;
-import java.util.Locale;
-import java.util.stream.Collectors;
/**
* Shows the user their tiles for their priority People (go/live-status).
@@ -78,21 +69,9 @@ public class PeopleSpaceActivity extends Activity {
*/
private void setTileViewsWithPriorityConversations() {
try {
- boolean showAllConversations = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE) == 0;
- List<ConversationChannelWrapper> conversations =
- mNotificationManager.getConversations(
- !showAllConversations /* priority only */).getList();
- List<ShortcutInfo> shortcutInfos = conversations.stream().filter(
- c -> shouldKeepConversation(c)).map(c -> c.getShortcutInfo()).collect(
- Collectors.toList());
- if (showAllConversations) {
- List<ConversationChannel> recentConversations =
- mPeopleManager.getRecentConversations().getList();
- List<ShortcutInfo> recentShortcuts = recentConversations.stream().map(
- c -> c.getShortcutInfo()).collect(Collectors.toList());
- shortcutInfos.addAll(recentShortcuts);
- }
+ List<ShortcutInfo> shortcutInfos =
+ PeopleSpaceUtils.getShortcutInfos(
+ mContext, mNotificationManager, mPeopleManager);
for (ShortcutInfo conversation : shortcutInfos) {
PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext,
mPeopleSpaceLayout,
@@ -117,7 +96,7 @@ public class PeopleSpaceActivity extends Activity {
shortcutInfo.getId());
String status = lastInteraction != 0l ? mContext.getString(
R.string.last_interaction_status,
- getLastInteractionString(
+ PeopleSpaceUtils.getLastInteractionString(
lastInteraction)) : mContext.getString(R.string.basic_status);
tileView.setStatus(status);
@@ -130,46 +109,6 @@ public class PeopleSpaceActivity extends Activity {
}
}
- /** Returns a readable representation of {@code lastInteraction}. */
- private String getLastInteractionString(long lastInteraction) {
- long now = System.currentTimeMillis();
- Duration durationSinceLastInteraction = Duration.ofMillis(
- now - lastInteraction);
- MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
- MeasureFormat.FormatWidth.WIDE);
- if (durationSinceLastInteraction.toDays() >= 1) {
- return
- formatter
- .formatMeasures(new Measure(durationSinceLastInteraction.toDays(),
- MeasureUnit.DAY));
- } else if (durationSinceLastInteraction.toHours() >= 1) {
- return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toHours(),
- MeasureUnit.HOUR));
- } else if (durationSinceLastInteraction.toMinutes() >= 1) {
- return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toMinutes(),
- MeasureUnit.MINUTE));
- } else {
- return formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toMillis() / 1000,
- MeasureUnit.SECOND));
- }
- }
-
- /**
- * Returns whether the {@code conversation} should be kept for display in the People Space.
- *
- * <p>A valid {@code conversation} must:
- * <ul>
- * <li>Have a non-null {@link ShortcutInfo}
- * <li>Have an associated label in the {@link ShortcutInfo}
- * </ul>
- * </li>
- */
- private boolean shouldKeepConversation(ConversationChannelWrapper conversation) {
- ShortcutInfo shortcutInfo = conversation.getShortcutInfo();
- return shortcutInfo != null && shortcutInfo.getLabel().length() != 0;
- }
-
@Override
protected void onResume() {
super.onResume();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
new file mode 100644
index 000000000000..9d46b321c52c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 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.people;
+
+import android.app.INotificationManager;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.icu.text.MeasureFormat;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.provider.Settings;
+import android.service.notification.ConversationChannelWrapper;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+/** Utils class for People Space. */
+public class PeopleSpaceUtils {
+ private static final String TAG = "PeopleSpaceUtils";
+
+ /** Turns on debugging information about People Space. */
+ public static final boolean DEBUG = true;
+
+ /** Returns a list of {@link ShortcutInfo} corresponding to user's conversations. */
+ public static List<ShortcutInfo> getShortcutInfos(
+ Context context,
+ INotificationManager notificationManager,
+ IPeopleManager peopleManager
+ ) throws Exception {
+ boolean showAllConversations = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE) == 0;
+ List<ConversationChannelWrapper> conversations =
+ notificationManager.getConversations(
+ !showAllConversations /* priority only */).getList();
+ List<ShortcutInfo> shortcutInfos = conversations.stream().filter(
+ c -> shouldKeepConversation(c)).map(
+ c -> c.getShortcutInfo()).collect(Collectors.toList());
+ if (showAllConversations) {
+ List<ConversationChannel> recentConversations =
+ peopleManager.getRecentConversations().getList();
+ List<ShortcutInfo> recentShortcuts = recentConversations.stream().map(
+ c -> c.getShortcutInfo()).collect(Collectors.toList());
+ shortcutInfos.addAll(recentShortcuts);
+ }
+ return shortcutInfos;
+ }
+
+ /** Converts {@code drawable} to a {@link Bitmap}. */
+ public static Bitmap convertDrawableToBitmap(Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap();
+ }
+ // We use max below because the drawable might have no intrinsic width/height (e.g. if the
+ // drawable is a solid color).
+ Bitmap bitmap =
+ Bitmap.createBitmap(
+ Math.max(drawable.getIntrinsicWidth(), 1),
+ Math.max(drawable.getIntrinsicHeight(), 1),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+ /** Returns a readable representation of {@code lastInteraction}. */
+ public static String getLastInteractionString(long lastInteraction) {
+ long now = System.currentTimeMillis();
+ Duration durationSinceLastInteraction = Duration.ofMillis(
+ now - lastInteraction);
+ MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
+ MeasureFormat.FormatWidth.WIDE);
+ if (durationSinceLastInteraction.toDays() >= 1) {
+ return
+ formatter
+ .formatMeasures(new Measure(durationSinceLastInteraction.toDays(),
+ MeasureUnit.DAY));
+ } else if (durationSinceLastInteraction.toHours() >= 1) {
+ return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toHours(),
+ MeasureUnit.HOUR));
+ } else if (durationSinceLastInteraction.toMinutes() >= 1) {
+ return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toMinutes(),
+ MeasureUnit.MINUTE));
+ } else {
+ return formatter.formatMeasures(
+ new Measure(durationSinceLastInteraction.toMillis() / 1000,
+ MeasureUnit.SECOND));
+ }
+ }
+
+ /**
+ * Returns whether the {@code conversation} should be kept for display in the People Space.
+ *
+ * <p>A valid {@code conversation} must:
+ * <ul>
+ * <li>Have a non-null {@link ShortcutInfo}
+ * <li>Have an associated label in the {@link ShortcutInfo}
+ * </ul>
+ * </li>
+ */
+ public static boolean shouldKeepConversation(ConversationChannelWrapper conversation) {
+ ShortcutInfo shortcutInfo = conversation.getShortcutInfo();
+ return shortcutInfo != null && shortcutInfo.getLabel().length() != 0;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
new file mode 100644
index 000000000000..85801f9e7206
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 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.people.widget;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.people.PeopleSpaceUtils;
+
+/** People Space Widget Provider class. */
+public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
+ private static final String TAG = "PeopleSpaceWidgetPvd";
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+ /** Called when widget updates. */
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ super.onUpdate(context, appWidgetManager, appWidgetIds);
+
+ if (DEBUG) Log.d(TAG, "onUpdate called");
+ // Perform this loop procedure for each App Widget that belongs to this provider
+ for (int appWidgetId : appWidgetIds) {
+ RemoteViews views =
+ new RemoteViews(context.getPackageName(), R.layout.people_space_widget);
+
+ Intent intent = new Intent(context, PeopleSpaceWidgetService.class);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ views.setRemoteAdapter(R.id.widget_list_view, intent);
+
+ // Tell the AppWidgetManager to perform an update on the current app widget
+ appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view);
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
new file mode 100644
index 000000000000..093925a2664a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 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.people.widget;
+
+import android.app.INotificationManager;
+import android.app.people.IPeopleManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+import com.android.systemui.R;
+import com.android.systemui.people.PeopleSpaceTileView;
+import com.android.systemui.people.PeopleSpaceUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** People Space Widget RemoteViewsFactory class. */
+public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
+ private static final String TAG = "PeopleSpaceWRVFactory";
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+ private IPeopleManager mPeopleManager;
+ private INotificationManager mNotificationManager;
+ private PackageManager mPackageManager;
+ private LauncherApps mLauncherApps;
+ private List<ShortcutInfo> mShortcutInfos = new ArrayList<>();
+ private Context mContext;
+
+ public PeopleSpaceWidgetRemoteViewsFactory(Context context, Intent intent) {
+ this.mContext = context;
+ }
+
+ @Override
+ public void onCreate() {
+ if (DEBUG) Log.d(TAG, "onCreate called");
+ mNotificationManager =
+ INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mPackageManager = mContext.getPackageManager();
+ mPeopleManager = IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE));
+ mLauncherApps = mContext.getSystemService(LauncherApps.class);
+ setTileViewsWithPriorityConversations();
+ }
+
+ /**
+ * Retrieves all priority conversations and sets a {@link PeopleSpaceTileView}s for each
+ * priority conversation.
+ */
+ private void setTileViewsWithPriorityConversations() {
+ try {
+ mShortcutInfos =
+ PeopleSpaceUtils.getShortcutInfos(
+ mContext, mNotificationManager, mPeopleManager);
+ } catch (Exception e) {
+ Log.e(TAG, "Couldn't retrieve conversations", e);
+ }
+ }
+
+ @Override
+ public void onDataSetChanged() {
+ if (DEBUG) Log.d(TAG, "onDataSetChanged called");
+ setTileViewsWithPriorityConversations();
+ }
+
+ @Override
+ public void onDestroy() {
+ mShortcutInfos.clear();
+ }
+
+ @Override
+ public int getCount() {
+ return mShortcutInfos.size();
+ }
+
+ @Override
+ public RemoteViews getViewAt(int i) {
+ if (DEBUG) Log.d(TAG, "getViewAt called, index: " + i);
+
+ RemoteViews personView =
+ new RemoteViews(mContext.getPackageName(), R.layout.people_space_widget_item);
+ try {
+ ShortcutInfo shortcutInfo = mShortcutInfos.get(i);
+ int userId = UserHandle.getUserHandleForUid(
+ shortcutInfo.getUserId()).getIdentifier();
+ String pkg = shortcutInfo.getPackage();
+ long lastInteraction = mPeopleManager.getLastInteraction(
+ pkg, userId,
+ shortcutInfo.getId());
+
+ String status = lastInteraction != 0L ? mContext.getString(
+ R.string.last_interaction_status,
+ PeopleSpaceUtils.getLastInteractionString(
+ lastInteraction)) : mContext.getString(R.string.basic_status);
+
+ personView.setTextViewText(R.id.status, status);
+ personView.setTextViewText(R.id.name, shortcutInfo.getLabel().toString());
+
+ personView.setImageViewBitmap(
+ R.id.package_icon,
+ PeopleSpaceUtils.convertDrawableToBitmap(
+ mPackageManager.getApplicationIcon(pkg)
+ )
+ );
+ personView.setImageViewBitmap(
+ R.id.person_icon,
+ PeopleSpaceUtils.convertDrawableToBitmap(
+ mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0)
+ )
+ );
+ } catch (Exception e) {
+ Log.e(TAG, "Couldn't retrieve shortcut information", e);
+ }
+ return personView;
+ }
+
+ @Override
+ public RemoteViews getLoadingView() {
+ return null;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return i;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetService.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetService.java
new file mode 100644
index 000000000000..c0e43473d069
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 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.people.widget;
+import android.content.Intent;
+import android.util.Log;
+import android.widget.RemoteViewsService;
+
+import com.android.systemui.people.PeopleSpaceUtils;
+
+/** People Space Widget Service class. */
+public class PeopleSpaceWidgetService extends RemoteViewsService {
+ private static final String TAG = "PeopleSpaceWidgetSvc";
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ if (DEBUG) Log.d(TAG, "onGetViewFactory called");
+ return new PeopleSpaceWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index acead987a06a..34ebe3e202e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -34,8 +34,7 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
}
@Override
- public void init() {
- super.init();
+ public void initInternal() {
mQuickStatusBarHeaderController.init();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index 3fd7f94514f3..5c26d9400c9f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -16,12 +16,12 @@
package com.android.systemui.screenshot;
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_EDIT;
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_EDIT;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_DISALLOW_ENTER_PIP;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
import android.app.ActivityOptions;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
index 9028bb57c8e5..35839f39b491 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -16,10 +16,10 @@
package com.android.systemui.screenshot;
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
deleted file mode 100644
index 818bb9d8d78f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ /dev/null
@@ -1,1199 +0,0 @@
-/*
- * Copyright (C) 2020 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 static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.WindowContext;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.Outline;
-import android.graphics.PixelFormat;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.graphics.drawable.InsetDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.hardware.display.DisplayManager;
-import android.media.MediaActionSound;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * Class for handling device screen shots
- */
-@SysUISingleton
-public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInsetsListener {
-
- /**
- * POD used in the AsyncTask which saves an image in the background.
- */
- static class SaveImageInBackgroundData {
- public Bitmap image;
- public Consumer<Uri> finisher;
- public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
-
- void clearImage() {
- image = null;
- }
- }
-
- /**
- * Structure returned by the SaveImageInBackgroundTask
- */
- static class SavedImageData {
- public Uri uri;
- public Notification.Action shareAction;
- public Notification.Action editAction;
- public Notification.Action deleteAction;
- public List<Notification.Action> smartActions;
-
- /**
- * Used to reset the return data on error
- */
- public void reset() {
- uri = null;
- shareAction = null;
- editAction = null;
- deleteAction = null;
- smartActions = null;
- }
- }
-
- abstract static class ActionsReadyListener {
- abstract void onActionsReady(SavedImageData imageData);
- }
-
- // These strings are used for communicating the action invoked to
- // ScreenshotNotificationSmartActionsProvider.
- static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
- static final String EXTRA_ID = "android:screenshot_id";
- static final String ACTION_TYPE_DELETE = "Delete";
- static final String ACTION_TYPE_SHARE = "Share";
- static final String ACTION_TYPE_EDIT = "Edit";
- static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
- static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
-
- static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
- static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
- static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
-
- // From WizardManagerHelper.java
- private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
-
- private static final String TAG = "GlobalScreenshot";
-
- private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
- private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
- // delay before starting to fade in dismiss button
- private static final long SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS = 200;
- private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
- private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
- private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
- private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
- private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
- private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
- private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
- private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
- private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
- private static final float ROUNDED_CORNER_RADIUS = .05f;
- private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
- private static final int MESSAGE_CORNER_TIMEOUT = 2;
-
- private final Interpolator mAccelerateInterpolator = new AccelerateInterpolator();
-
- private final ScreenshotNotificationsController mNotificationsController;
- private final UiEventLogger mUiEventLogger;
-
- private final Context mContext;
- private final ScreenshotSmartActions mScreenshotSmartActions;
- private final WindowManager mWindowManager;
- private final WindowManager.LayoutParams mWindowLayoutParams;
- private final Display mDisplay;
- private final DisplayMetrics mDisplayMetrics;
- private final AccessibilityManager mAccessibilityManager;
-
- private View mScreenshotLayout;
- private ScreenshotSelectorView mScreenshotSelectorView;
- private ImageView mScreenshotAnimatedView;
- private ImageView mScreenshotPreview;
- private ImageView mScreenshotFlash;
- private ImageView mActionsContainerBackground;
- private HorizontalScrollView mActionsContainer;
- private LinearLayout mActionsView;
- private ScreenshotActionChip mShareChip;
- private ScreenshotActionChip mEditChip;
- private ImageView mBackgroundProtection;
- private FrameLayout mDismissButton;
-
- private Bitmap mScreenBitmap;
- private SaveImageInBackgroundTask mSaveInBgTask;
- private Animator mScreenshotAnimation;
- private Runnable mOnCompleteRunnable;
- private Animator mDismissAnimation;
- private boolean mInDarkMode;
- private boolean mDirectionLTR;
- private boolean mOrientationPortrait;
-
- private float mCornerSizeX;
- private float mDismissDeltaY;
-
- private MediaActionSound mCameraSound;
-
- private int mNavMode;
- private int mLeftInset;
- private int mRightInset;
-
- private ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
- private PendingInteraction mPendingInteraction;
- private enum PendingInteraction {
- PREVIEW,
- EDIT,
- SHARE
- }
-
- // standard material ease
- private final Interpolator mFastOutSlowIn;
-
- private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_CORNER_TIMEOUT:
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
- GlobalScreenshot.this.dismissScreenshot("timeout", false);
- mOnCompleteRunnable.run();
- break;
- default:
- break;
- }
- }
- };
-
- @Inject
- public GlobalScreenshot(Context context,
- ScreenshotSmartActions screenshotSmartActions,
- ScreenshotNotificationsController screenshotNotificationsController,
- UiEventLogger uiEventLogger) {
-
- // Create a visual (Window) context
- // After this, our windowToken is available from mContext.getActivityToken()
- final DisplayManager dm = context.getSystemService(DisplayManager.class);
- mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
- Context displayContext = context.createDisplayContext(mDisplay);
- mContext = new WindowContext(
- displayContext, WindowManager.LayoutParams.TYPE_SCREENSHOT, null);
- mWindowManager = mContext.getSystemService(WindowManager.class);
-
- mScreenshotSmartActions = screenshotSmartActions;
- mNotificationsController = screenshotNotificationsController;
- mUiEventLogger = uiEventLogger;
- mAccessibilityManager = AccessibilityManager.getInstance(mContext);
-
- reloadAssets();
- Configuration config = mContext.getResources().getConfiguration();
- mInDarkMode = config.isNightModeActive();
- mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
- mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
-
- // Setup the window that we are going to use
- mWindowLayoutParams = new WindowManager.LayoutParams(
- 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_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- PixelFormat.TRANSLUCENT);
- mWindowLayoutParams.setTitle("ScreenshotAnimation");
- mWindowLayoutParams.layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
- mDisplayMetrics = new DisplayMetrics();
- mDisplay.getRealMetrics(mDisplayMetrics);
-
- final Resources resources = mContext.getResources();
- mCornerSizeX = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
- mDismissDeltaY = resources.getDimensionPixelSize(R.dimen.screenshot_dismissal_height_delta);
-
- mFastOutSlowIn =
- AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
-
- // Setup the Camera shutter sound
- mCameraSound = new MediaActionSound();
- mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
- }
-
- @Override // ViewTreeObserver.OnComputeInternalInsetsListener
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- Region touchRegion = new Region();
-
- Rect screenshotRect = new Rect();
- mScreenshotPreview.getBoundsOnScreen(screenshotRect);
- touchRegion.op(screenshotRect, Region.Op.UNION);
- Rect actionsRect = new Rect();
- mActionsContainer.getBoundsOnScreen(actionsRect);
- touchRegion.op(actionsRect, Region.Op.UNION);
- Rect dismissRect = new Rect();
- mDismissButton.getBoundsOnScreen(dismissRect);
- touchRegion.op(dismissRect, Region.Op.UNION);
-
- if (QuickStepContract.isGesturalMode(mNavMode)) {
- // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
- Rect inset = new Rect(0, 0, mLeftInset, mDisplayMetrics.heightPixels);
- touchRegion.op(inset, Region.Op.UNION);
- inset.set(mDisplayMetrics.widthPixels - mRightInset, 0, mDisplayMetrics.widthPixels,
- mDisplayMetrics.heightPixels);
- touchRegion.op(inset, Region.Op.UNION);
- }
-
- inoutInfo.touchableRegion.set(touchRegion);
- }
-
- void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
- mOnCompleteRunnable = onComplete;
-
- mDisplay.getRealMetrics(mDisplayMetrics);
- takeScreenshotInternal(
- finisher,
- new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
- }
-
- void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
- Consumer<Uri> finisher, Runnable onComplete) {
- // TODO: use task Id, userId, topComponent for smart handler
- mOnCompleteRunnable = onComplete;
-
- if (screenshot == null) {
- Log.e(TAG, "Got null bitmap from screenshot message");
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- finisher.accept(null);
- mOnCompleteRunnable.run();
- return;
- }
-
- if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
- saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
- } else {
- saveScreenshot(screenshot, finisher,
- new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
- true);
- }
- }
-
- /**
- * Displays a screenshot selector
- */
- @SuppressLint("ClickableViewAccessibility")
- void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
- dismissScreenshot("new screenshot requested", true);
- mOnCompleteRunnable = onComplete;
-
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- mScreenshotSelectorView.setOnTouchListener((v, event) -> {
- ScreenshotSelectorView view = (ScreenshotSelectorView) v;
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- view.startSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_MOVE:
- view.updateSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_UP:
- view.setVisibility(View.GONE);
- mWindowManager.removeView(mScreenshotLayout);
- final Rect rect = view.getSelectionRect();
- if (rect != null) {
- if (rect.width() != 0 && rect.height() != 0) {
- // Need mScreenshotLayout to handle it after the view disappears
- mScreenshotLayout.post(() -> takeScreenshotInternal(finisher, rect));
- }
- }
-
- view.stopSelection();
- return true;
- }
-
- return false;
- });
- mScreenshotLayout.post(() -> {
- mScreenshotSelectorView.setVisibility(View.VISIBLE);
- mScreenshotSelectorView.requestFocus();
- });
- }
-
- /**
- * Cancels screenshot request
- */
- void stopScreenshot() {
- // If the selector layer still presents on screen, we remove it and resets its state.
- if (mScreenshotSelectorView.getSelectionRect() != null) {
- mWindowManager.removeView(mScreenshotLayout);
- mScreenshotSelectorView.stopSelection();
- }
- }
-
- /**
- * Clears current screenshot
- */
- void dismissScreenshot(String reason, boolean immediate) {
- Log.v(TAG, "clearing screenshot: " + reason);
- mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
- mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- if (!immediate) {
- mDismissAnimation = createScreenshotDismissAnimation();
- mDismissAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- clearScreenshot();
- }
- });
- mDismissAnimation.start();
- } else {
- clearScreenshot();
- }
- }
-
- private void onConfigChanged(Configuration newConfig) {
- boolean needsUpdate = false;
- // dark mode
- if (newConfig.isNightModeActive()) {
- // Night mode is active, we're using dark theme
- if (!mInDarkMode) {
- mInDarkMode = true;
- needsUpdate = true;
- }
- } else {
- // Night mode is not active, we're using the light theme
- if (mInDarkMode) {
- mInDarkMode = false;
- needsUpdate = true;
- }
- }
-
- // RTL configuration
- switch (newConfig.getLayoutDirection()) {
- case View.LAYOUT_DIRECTION_LTR:
- if (!mDirectionLTR) {
- mDirectionLTR = true;
- needsUpdate = true;
- }
- break;
- case View.LAYOUT_DIRECTION_RTL:
- if (mDirectionLTR) {
- mDirectionLTR = false;
- needsUpdate = true;
- }
- break;
- }
-
- // portrait/landscape orientation
- switch (newConfig.orientation) {
- case ORIENTATION_PORTRAIT:
- if (!mOrientationPortrait) {
- mOrientationPortrait = true;
- needsUpdate = true;
- }
- break;
- case ORIENTATION_LANDSCAPE:
- if (mOrientationPortrait) {
- mOrientationPortrait = false;
- needsUpdate = true;
- }
- break;
- }
-
- if (needsUpdate) {
- reloadAssets();
- }
-
- mNavMode = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_navBarInteractionMode);
- }
-
- /**
- * Update assets (called when the dark theme status changes). We only need to update the dismiss
- * button and the actions container background, since the buttons are re-inflated on demand.
- */
- private void reloadAssets() {
- boolean wasAttached = mScreenshotLayout != null && mScreenshotLayout.isAttachedToWindow();
- if (wasAttached) {
- mWindowManager.removeView(mScreenshotLayout);
- }
-
- // Inflate the screenshot layout
- mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
- // TODO(159460485): Remove this when focus is handled properly in the system
- mScreenshotLayout.setOnTouchListener((v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
- // Once the user touches outside, stop listening for input
- setWindowFocusable(false);
- }
- return false;
- });
- mScreenshotLayout.setOnApplyWindowInsetsListener((v, insets) -> {
- if (QuickStepContract.isGesturalMode(mNavMode)) {
- Insets gestureInsets = insets.getInsets(
- WindowInsets.Type.systemGestures());
- mLeftInset = gestureInsets.left;
- mRightInset = gestureInsets.right;
- } else {
- mLeftInset = mRightInset = 0;
- }
- return mScreenshotLayout.onApplyWindowInsets(insets);
- });
- mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- dismissScreenshot("back pressed", false);
- return true;
- }
- return false;
- });
- // Get focus so that the key events go to the layout.
- mScreenshotLayout.setFocusableInTouchMode(true);
- mScreenshotLayout.requestFocus();
-
- mScreenshotAnimatedView =
- mScreenshotLayout.findViewById(R.id.global_screenshot_animated_view);
- mScreenshotAnimatedView.setClipToOutline(true);
- mScreenshotAnimatedView.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
- ROUNDED_CORNER_RADIUS * view.getWidth());
- }
- });
- mScreenshotPreview = mScreenshotLayout.findViewById(R.id.global_screenshot_preview);
- mScreenshotPreview.setClipToOutline(true);
- mScreenshotPreview.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
- ROUNDED_CORNER_RADIUS * view.getWidth());
- }
- });
-
- mActionsContainerBackground = mScreenshotLayout.findViewById(
- R.id.global_screenshot_actions_container_background);
- mActionsContainer = mScreenshotLayout.findViewById(
- R.id.global_screenshot_actions_container);
- mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
- mBackgroundProtection = mScreenshotLayout.findViewById(
- R.id.global_screenshot_actions_background);
- mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
- mDismissButton.setOnClickListener(view -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
- dismissScreenshot("dismiss_button", false);
- mOnCompleteRunnable.run();
- });
-
- mShareChip = mActionsContainer.findViewById(R.id.screenshot_share_chip);
- mEditChip = mActionsContainer.findViewById(R.id.screenshot_edit_chip);
-
- mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
- mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
- mScreenshotLayout.setFocusable(true);
- mScreenshotSelectorView.setFocusable(true);
- mScreenshotSelectorView.setFocusableInTouchMode(true);
- mScreenshotAnimatedView.setPivotX(0);
- mScreenshotAnimatedView.setPivotY(0);
- mActionsContainer.setScrollX(0);
-
- if (wasAttached) {
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- }
- }
-
- /**
- * Takes a screenshot of the current display and shows an animation.
- */
- private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
- // copy the input Rect, since SurfaceControl.screenshot can mutate it
- Rect screenRect = new Rect(crop);
- int width = crop.width();
- int height = crop.height();
- final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
- .setSourceCrop(crop)
- .setSize(width, height)
- .build();
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureDisplay(captureArgs);
- Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
-
- if (screenshot == null) {
- Log.e(TAG, "Screenshot bitmap was null");
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- finisher.accept(null);
- mOnCompleteRunnable.run();
- return;
- }
-
- saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
- }
-
- private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
- Insets screenInsets, boolean showFlash) {
- if (mAccessibilityManager.isEnabled()) {
- AccessibilityEvent event =
- new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- event.setContentDescription(
- mContext.getResources().getString(R.string.screenshot_saving_title));
- mAccessibilityManager.sendAccessibilityEvent(event);
- }
-
- if (mScreenshotLayout.isAttachedToWindow()) {
- // if we didn't already dismiss for another reason
- if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
- }
- dismissScreenshot("new screenshot requested", true);
- }
-
- mScreenBitmap = screenshot;
-
- if (!isUserSetupComplete()) {
- // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
- // and sharing shouldn't be exposed to the user.
- saveScreenshotAndToast(finisher);
- return;
- }
-
- // Optimizations
- mScreenBitmap.setHasAlpha(false);
- mScreenBitmap.prepareToDraw();
-
- onConfigChanged(mContext.getResources().getConfiguration());
-
- if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
- mDismissAnimation.cancel();
- }
-
- // The window is focusable by default
- setWindowFocusable(true);
-
- // Start the post-screenshot animation
- startAnimation(finisher, screenRect, screenInsets, showFlash);
- }
-
- /**
- * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
- * failure).
- */
- private void saveScreenshotAndToast(Consumer<Uri> finisher) {
- // Play the shutter sound to notify that we've taken a screenshot
- mScreenshotHandler.post(() -> {
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
- });
-
- saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- finisher.accept(imageData.uri);
- if (imageData.uri == null) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_save_text);
- } else {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
-
- mScreenshotHandler.post(() -> {
- Toast.makeText(mContext, R.string.screenshot_saved_title,
- Toast.LENGTH_SHORT).show();
- });
- }
- }
- });
- }
-
- /**
- * Starts the animation after taking the screenshot
- */
- private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
- boolean showFlash) {
- mScreenshotHandler.post(() -> {
- if (!mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- }
- mScreenshotAnimatedView.setImageDrawable(
- createScreenDrawable(mScreenBitmap, screenInsets));
- setAnimatedViewSize(screenRect.width(), screenRect.height());
- // Show when the animation starts
- mScreenshotAnimatedView.setVisibility(View.GONE);
-
- mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
- // make static preview invisible (from gone) so we can query its location on screen
- mScreenshotPreview.setVisibility(View.INVISIBLE);
-
- mScreenshotHandler.post(() -> {
- mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-
- mScreenshotAnimation =
- createScreenshotDropInAnimation(screenRect, showFlash);
-
- saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- showUiOnActionsReady(imageData);
- }
- });
-
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotPreview.buildLayer();
- mScreenshotAnimation.start();
- });
- });
- }
-
- /**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(
- Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
- SaveImageInBackgroundData data = new SaveImageInBackgroundData();
- data.image = mScreenBitmap;
- data.finisher = finisher;
- data.mActionsReadyListener = actionsReadyListener;
-
- if (mSaveInBgTask != null) {
- // just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- logSuccessOnActionsReady(imageData);
- }
- });
- }
-
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
- mSaveInBgTask.execute();
- }
-
- /**
- * Sets up the action shade and its entrance animation, once we get the screenshot URI.
- */
- private void showUiOnActionsReady(SavedImageData imageData) {
- logSuccessOnActionsReady(imageData);
-
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
- SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
- AccessibilityManager.FLAG_CONTENT_CONTROLS);
-
- mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
- mScreenshotHandler.sendMessageDelayed(
- mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
- timeoutMs);
-
- if (imageData.uri != null) {
- mScreenshotHandler.post(() -> {
- if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
- mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- setChipIntents(imageData);
- }
- });
- } else {
- setChipIntents(imageData);
- }
- });
- }
- }
-
- /**
- * Logs success/failure of the screenshot saving task, and shows an error if it failed.
- */
- private void logSuccessOnActionsReady(SavedImageData imageData) {
- if (imageData.uri == null) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_save_text);
- } else {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
- }
- }
-
- private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
- Rect previewBounds = new Rect();
- mScreenshotPreview.getBoundsOnScreen(previewBounds);
-
- float cornerScale =
- mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
- final float currentScale = 1f;
-
- mScreenshotAnimatedView.setScaleX(currentScale);
- mScreenshotAnimatedView.setScaleY(currentScale);
-
- mDismissButton.setAlpha(0);
- mDismissButton.setVisibility(View.VISIBLE);
-
- AnimatorSet dropInAnimation = new AnimatorSet();
- ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
- flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
- flashInAnimator.setInterpolator(mFastOutSlowIn);
- flashInAnimator.addUpdateListener(animation ->
- mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
-
- ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
- flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
- flashOutAnimator.setInterpolator(mFastOutSlowIn);
- flashOutAnimator.addUpdateListener(animation ->
- mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
-
- // animate from the current location, to the static preview location
- final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
- final PointF finalPos = new PointF(previewBounds.centerX(), previewBounds.centerY());
-
- ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
- toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
- float xPositionPct =
- SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
- float dismissPct =
- SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
- float scalePct =
- SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
- toCorner.addUpdateListener(animation -> {
- float t = animation.getAnimatedFraction();
- if (t < scalePct) {
- float scale = MathUtils.lerp(
- currentScale, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
- mScreenshotAnimatedView.setScaleX(scale);
- mScreenshotAnimatedView.setScaleY(scale);
- } else {
- mScreenshotAnimatedView.setScaleX(cornerScale);
- mScreenshotAnimatedView.setScaleY(cornerScale);
- }
-
- float currentScaleX = mScreenshotAnimatedView.getScaleX();
- float currentScaleY = mScreenshotAnimatedView.getScaleY();
-
- if (t < xPositionPct) {
- float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
- mFastOutSlowIn.getInterpolation(t / xPositionPct));
- mScreenshotAnimatedView.setX(xCenter - bounds.width() * currentScaleX / 2f);
- } else {
- mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * currentScaleX / 2f);
- }
- float yCenter = MathUtils.lerp(
- startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t));
- mScreenshotAnimatedView.setY(yCenter - bounds.height() * currentScaleY / 2f);
-
- if (t >= dismissPct) {
- mDismissButton.setAlpha((t - dismissPct) / (1 - dismissPct));
- float currentX = mScreenshotAnimatedView.getX();
- float currentY = mScreenshotAnimatedView.getY();
- mDismissButton.setY(currentY - mDismissButton.getHeight() / 2f);
- if (mDirectionLTR) {
- mDismissButton.setX(currentX
- + bounds.width() * currentScaleX - mDismissButton.getWidth() / 2f);
- } else {
- mDismissButton.setX(currentX - mDismissButton.getWidth() / 2f);
- }
- }
- });
-
- toCorner.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- mScreenshotAnimatedView.setVisibility(View.VISIBLE);
- }
- });
-
- mScreenshotFlash.setAlpha(0f);
- mScreenshotFlash.setVisibility(View.VISIBLE);
-
- if (showFlash) {
- dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
- dropInAnimation.play(flashOutAnimator).with(toCorner);
- } else {
- dropInAnimation.play(toCorner);
- }
-
- dropInAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mDismissButton.setAlpha(1);
- float dismissOffset = mDismissButton.getWidth() / 2f;
- float finalDismissX = mDirectionLTR
- ? finalPos.x - dismissOffset + bounds.width() * cornerScale / 2f
- : finalPos.x - dismissOffset - bounds.width() * cornerScale / 2f;
- mDismissButton.setX(finalDismissX);
- mDismissButton.setY(
- finalPos.y - dismissOffset - bounds.height() * cornerScale / 2f);
- mScreenshotAnimatedView.setScaleX(1);
- mScreenshotAnimatedView.setScaleY(1);
- mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * cornerScale / 2f);
- mScreenshotAnimatedView.setY(finalPos.y - bounds.height() * cornerScale / 2f);
- mScreenshotAnimatedView.setVisibility(View.GONE);
- mScreenshotPreview.setVisibility(View.VISIBLE);
- mScreenshotLayout.forceLayout();
- createScreenshotActionsShadeAnimation().start();
- }
- });
-
- return dropInAnimation;
- }
-
- private ValueAnimator createScreenshotActionsShadeAnimation() {
- // By default the activities won't be able to start immediately; override this to keep
- // the same behavior as if started from a notification
- try {
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
-
- ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
-
- mShareChip.setText(mContext.getString(com.android.internal.R.string.share));
- mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
- mShareChip.setOnClickListener(v -> {
- mShareChip.setIsPending(true);
- mEditChip.setIsPending(false);
- mPendingInteraction = PendingInteraction.SHARE;
- });
- chips.add(mShareChip);
-
- mEditChip.setText(mContext.getString(com.android.internal.R.string.screenshot_edit));
- mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
- mEditChip.setOnClickListener(v -> {
- mEditChip.setIsPending(true);
- mShareChip.setIsPending(false);
- mPendingInteraction = PendingInteraction.EDIT;
- });
- chips.add(mEditChip);
-
- mScreenshotPreview.setOnClickListener(v -> {
- mShareChip.setIsPending(false);
- mEditChip.setIsPending(false);
- mPendingInteraction = PendingInteraction.PREVIEW;
- });
-
- // remove the margin from the last chip so that it's correctly aligned with the end
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
- mActionsView.getChildAt(0).getLayoutParams();
- params.setMarginStart(0);
- mActionsView.getChildAt(0).setLayoutParams(params);
-
- ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
- animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS);
- float alphaFraction = (float) SCREENSHOT_ACTIONS_ALPHA_DURATION_MS
- / SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS;
- mActionsContainer.setAlpha(0f);
- mActionsContainerBackground.setAlpha(0f);
- mActionsContainer.setVisibility(View.VISIBLE);
- mActionsContainerBackground.setVisibility(View.VISIBLE);
-
- animator.addUpdateListener(animation -> {
- float t = animation.getAnimatedFraction();
- mBackgroundProtection.setAlpha(t);
- float containerAlpha = t < alphaFraction ? t / alphaFraction : 1;
- mActionsContainer.setAlpha(containerAlpha);
- mActionsContainerBackground.setAlpha(containerAlpha);
- float containerScale = SCREENSHOT_ACTIONS_START_SCALE_X
- + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X));
- mActionsContainer.setScaleX(containerScale);
- mActionsContainerBackground.setScaleX(containerScale);
- for (ScreenshotActionChip chip : chips) {
- chip.setAlpha(t);
- chip.setScaleX(1 / containerScale); // invert to keep size of children constant
- }
- mActionsContainer.setScrollX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
- mActionsContainer.setPivotX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
- mActionsContainerBackground.setPivotX(
- mDirectionLTR ? 0 : mActionsContainerBackground.getWidth());
- });
- return animator;
- }
-
- private void setChipIntents(SavedImageData imageData) {
- mShareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
- dismissScreenshot("chip tapped", false);
- mOnCompleteRunnable.run();
- });
-
- mEditChip.setPendingIntent(imageData.editAction.actionIntent, () -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
- dismissScreenshot("chip tapped", false);
- mOnCompleteRunnable.run();
- });
-
- mScreenshotPreview.setOnClickListener(v -> {
- try {
- imageData.editAction.actionIntent.send();
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
- dismissScreenshot("screenshot preview tapped", false);
- mOnCompleteRunnable.run();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Intent cancelled", e);
- }
- });
-
- if (mPendingInteraction != null) {
- switch(mPendingInteraction) {
- case PREVIEW:
- mScreenshotPreview.callOnClick();
- break;
- case SHARE:
- mShareChip.callOnClick();
- break;
- case EDIT:
- mEditChip.callOnClick();
- break;
- }
- } else {
- LayoutInflater inflater = LayoutInflater.from(mContext);
-
- for (Notification.Action smartAction : imageData.smartActions) {
- ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
- R.layout.global_screenshot_action_chip, mActionsView, false);
- actionChip.setText(smartAction.title);
- actionChip.setIcon(smartAction.getIcon(), false);
- actionChip.setPendingIntent(smartAction.actionIntent,
- () -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
- dismissScreenshot("chip tapped", false);
- mOnCompleteRunnable.run();
- });
- actionChip.setAlpha(1);
- mActionsView.addView(actionChip);
- mSmartChips.add(actionChip);
- }
- }
- }
-
- private AnimatorSet createScreenshotDismissAnimation() {
- ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
- alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
- alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS);
- alphaAnim.addUpdateListener(animation -> {
- mScreenshotLayout.setAlpha(1 - animation.getAnimatedFraction());
- });
-
- ValueAnimator yAnim = ValueAnimator.ofFloat(0, 1);
- yAnim.setInterpolator(mAccelerateInterpolator);
- yAnim.setDuration(SCREENSHOT_DISMISS_Y_DURATION_MS);
- float screenshotStartY = mScreenshotPreview.getTranslationY();
- float dismissStartY = mDismissButton.getTranslationY();
- yAnim.addUpdateListener(animation -> {
- float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());
- mScreenshotPreview.setTranslationY(screenshotStartY + yDelta);
- mDismissButton.setTranslationY(dismissStartY + yDelta);
- mActionsContainer.setTranslationY(yDelta);
- mActionsContainerBackground.setTranslationY(yDelta);
- });
-
- AnimatorSet animSet = new AnimatorSet();
- animSet.play(yAnim).with(alphaAnim);
-
- return animSet;
- }
-
- private void clearScreenshot() {
- if (mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.removeView(mScreenshotLayout);
- }
-
- // Clear any references to the bitmap
- mScreenshotPreview.setImageDrawable(null);
- mScreenshotAnimatedView.setImageDrawable(null);
- mScreenshotAnimatedView.setVisibility(View.GONE);
- mActionsContainerBackground.setVisibility(View.GONE);
- mActionsContainer.setVisibility(View.GONE);
- mBackgroundProtection.setAlpha(0f);
- mDismissButton.setVisibility(View.GONE);
- mScreenshotPreview.setVisibility(View.GONE);
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
- mScreenshotPreview.setOnClickListener(null);
- mShareChip.setOnClickListener(null);
- mEditChip.setOnClickListener(null);
- mShareChip.setIsPending(false);
- mEditChip.setIsPending(false);
- mPendingInteraction = null;
- for (ScreenshotActionChip chip : mSmartChips) {
- mActionsView.removeView(chip);
- }
- mSmartChips.clear();
- mScreenshotLayout.setAlpha(1);
- mDismissButton.setTranslationY(0);
- mActionsContainer.setTranslationY(0);
- mActionsContainerBackground.setTranslationY(0);
- mScreenshotPreview.setTranslationY(0);
- }
-
- private void setAnimatedViewSize(int width, int height) {
- ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
- layoutParams.width = width;
- layoutParams.height = height;
- mScreenshotAnimatedView.setLayoutParams(layoutParams);
- }
-
- /**
- * Updates the window focusability. If the window is already showing, then it updates the
- * window immediately, otherwise the layout params will be applied when the window is next
- * shown.
- */
- private void setWindowFocusable(boolean focusable) {
- if (focusable) {
- mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- } else {
- mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- }
- if (mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
- }
- }
-
- private boolean isUserSetupComplete() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
- }
-
- /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
- private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) {
- int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
- int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom;
-
- if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
- || bitmap.getHeight() == 0) {
- Log.e(TAG, String.format(
- "Provided bitmap and insets create degenerate region: %dx%d %s",
- bitmap.getWidth(), bitmap.getHeight(), bitmapInsets));
- return false;
- }
-
- float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight;
- float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
-
- boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
- if (!matchWithinTolerance) {
- Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f",
- insettedBitmapAspect, boundsAspect));
- }
-
- return matchWithinTolerance;
- }
-
- /**
- * Create a drawable using the size of the bitmap and insets as the fractional inset parameters.
- */
- private Drawable createScreenDrawable(Bitmap bitmap, Insets insets) {
- int insettedWidth = bitmap.getWidth() - insets.left - insets.right;
- int insettedHeight = bitmap.getHeight() - insets.top - insets.bottom;
-
- BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
- if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
- || bitmap.getHeight() == 0) {
- Log.e(TAG, String.format(
- "Can't create insetted drawable, using 0 insets "
- + "bitmap and insets create degenerate region: %dx%d %s",
- bitmap.getWidth(), bitmap.getHeight(), insets));
- return bitmapDrawable;
- }
-
- InsetDrawable insetDrawable = new InsetDrawable(bitmapDrawable,
- -1f * insets.left / insettedWidth,
- -1f * insets.top / insettedHeight,
- -1f * insets.right / insettedWidth,
- -1f * insets.bottom / insettedHeight);
-
- if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
- // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
- // to fill in the background of the drawable.
- return new LayerDrawable(new Drawable[]{
- new ColorDrawable(Color.BLACK), insetDrawable});
- } else {
- return insetDrawable;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index f0ea597c458d..c0061ad97293 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -82,8 +82,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final Context mContext;
private final ScreenshotSmartActions mScreenshotSmartActions;
- private final GlobalScreenshot.SaveImageInBackgroundData mParams;
- private final GlobalScreenshot.SavedImageData mImageData;
+ private final ScreenshotController.SaveImageInBackgroundData mParams;
+ private final ScreenshotController.SavedImageData mImageData;
private final String mImageFileName;
private final long mImageTime;
private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
@@ -92,10 +92,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final Random mRandom = new Random();
SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
- GlobalScreenshot.SaveImageInBackgroundData data) {
+ ScreenshotController.SaveImageInBackgroundData data) {
mContext = context;
mScreenshotSmartActions = screenshotSmartActions;
- mImageData = new GlobalScreenshot.SavedImageData();
+ mImageData = new ScreenshotController.SavedImageData();
// Prepare all the output metadata
mParams = data;
@@ -234,7 +234,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
* Update the listener run when the saving task completes. Used to avoid showing UI for the
* first screenshot when a second one is taken.
*/
- void setActionsReadyListener(GlobalScreenshot.ActionsReadyListener listener) {
+ void setActionsReadyListener(ScreenshotController.ActionsReadyListener listener) {
mParams.mActionsReadyListener = listener;
}
@@ -287,10 +287,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
new Intent(context, ActionProxyReceiver.class)
- .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
- .putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
- .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
- .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+ .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent)
+ .putExtra(ScreenshotController.EXTRA_DISALLOW_ENTER_PIP, true)
+ .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId)
+ .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
.setAction(Intent.ACTION_SEND)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
@@ -332,9 +332,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a edit action
PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
new Intent(context, ActionProxyReceiver.class)
- .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
- .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
- .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+ .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent)
+ .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId)
+ .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
.setAction(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
@@ -355,9 +355,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a delete action for the notification
PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
new Intent(context, DeleteScreenshotReceiver.class)
- .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
- .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
- .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+ .putExtra(ScreenshotController.SCREENSHOT_URI_ID, uri.toString())
+ .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId)
+ .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
@@ -395,7 +395,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
Intent intent = new Intent(context, SmartActionsReceiver.class)
- .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
+ .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, action.actionIntent)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
@@ -411,9 +411,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private static void addIntentExtras(String screenshotId, Intent intent, String actionType,
boolean smartActionsEnabled) {
intent
- .putExtra(GlobalScreenshot.EXTRA_ACTION_TYPE, actionType)
- .putExtra(GlobalScreenshot.EXTRA_ID, screenshotId)
- .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
+ .putExtra(ScreenshotController.EXTRA_ACTION_TYPE, actionType)
+ .putExtra(ScreenshotController.EXTRA_ID, screenshotId)
+ .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
new file mode 100644
index 000000000000..5d8f70c4e460
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2020 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 static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static java.util.Objects.requireNonNull;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.WindowContext;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.media.MediaActionSound;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.Toast;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * Controls the state and flow for screenshots.
+ */
+public class ScreenshotController {
+ /**
+ * POD used in the AsyncTask which saves an image in the background.
+ */
+ static class SaveImageInBackgroundData {
+ public Bitmap image;
+ public Consumer<Uri> finisher;
+ public ScreenshotController.ActionsReadyListener mActionsReadyListener;
+
+ void clearImage() {
+ image = null;
+ }
+ }
+
+ /**
+ * Structure returned by the SaveImageInBackgroundTask
+ */
+ static class SavedImageData {
+ public Uri uri;
+ public Notification.Action shareAction;
+ public Notification.Action editAction;
+ public Notification.Action deleteAction;
+ public List<Notification.Action> smartActions;
+
+ /**
+ * Used to reset the return data on error
+ */
+ public void reset() {
+ uri = null;
+ shareAction = null;
+ editAction = null;
+ deleteAction = null;
+ smartActions = null;
+ }
+ }
+
+ abstract static class ActionsReadyListener {
+ abstract void onActionsReady(ScreenshotController.SavedImageData imageData);
+ }
+
+ private static final String TAG = "GlobalScreenshotController";
+
+ // These strings are used for communicating the action invoked to
+ // ScreenshotNotificationSmartActionsProvider.
+ static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+ static final String EXTRA_ID = "android:screenshot_id";
+ static final String ACTION_TYPE_DELETE = "Delete";
+ static final String ACTION_TYPE_SHARE = "Share";
+ static final String ACTION_TYPE_EDIT = "Edit";
+ static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+ static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+
+ static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
+ static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
+ static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
+
+
+ private static final int MESSAGE_CORNER_TIMEOUT = 2;
+ private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
+
+ // From WizardManagerHelper.java
+ private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
+
+ private final Context mContext;
+ private final ScreenshotNotificationsController mNotificationsController;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+ private final UiEventLogger mUiEventLogger;
+
+ private final WindowManager mWindowManager;
+ private final WindowManager.LayoutParams mWindowLayoutParams;
+ private final Display mDisplay;
+ private final DisplayMetrics mDisplayMetrics;
+ private final AccessibilityManager mAccessibilityManager;
+ private final MediaActionSound mCameraSound;
+
+ private ScreenshotView mScreenshotView;
+ private Bitmap mScreenBitmap;
+ private SaveImageInBackgroundTask mSaveInBgTask;
+
+ private Animator mScreenshotAnimation;
+ private Animator mDismissAnimation;
+
+ private Runnable mOnCompleteRunnable;
+ private boolean mInDarkMode;
+ private boolean mDirectionLTR;
+ private boolean mOrientationPortrait;
+
+ private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_CORNER_TIMEOUT:
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
+ ScreenshotController.this.dismissScreenshot(false);
+ mOnCompleteRunnable.run();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ @Inject
+ ScreenshotController(Context context, ScreenshotSmartActions screenshotSmartActions,
+ ScreenshotNotificationsController screenshotNotificationsController,
+ UiEventLogger uiEventLogger) {
+ mScreenshotSmartActions = screenshotSmartActions;
+ mNotificationsController = screenshotNotificationsController;
+ mUiEventLogger = uiEventLogger;
+
+ // Create a visual (Window) context
+ // After this, our windowToken is available from mContext.getActivityToken()
+ final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class));
+ mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ Context displayContext = context.createDisplayContext(mDisplay);
+ mContext = new WindowContext(displayContext, WindowManager.LayoutParams.TYPE_SCREENSHOT,
+ null);
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+
+ mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+
+ reloadAssets();
+ Configuration config = mContext.getResources().getConfiguration();
+ mInDarkMode = config.isNightModeActive();
+ mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
+ mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
+
+ // Setup the window that we are going to use
+ mWindowLayoutParams = new WindowManager.LayoutParams(
+ 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_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ PixelFormat.TRANSLUCENT);
+ mWindowLayoutParams.setTitle("ScreenshotAnimation");
+ mWindowLayoutParams.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
+
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplay.getRealMetrics(mDisplayMetrics);
+
+ // Setup the Camera shutter sound
+ mCameraSound = new MediaActionSound();
+ mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+ }
+
+ void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
+ mOnCompleteRunnable = onComplete;
+
+ mDisplay.getRealMetrics(mDisplayMetrics);
+ takeScreenshotInternal(
+ finisher,
+ new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+ }
+
+ void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+ Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+ Consumer<Uri> finisher, Runnable onComplete) {
+ // TODO: use task Id, userId, topComponent for smart handler
+ mOnCompleteRunnable = onComplete;
+
+ if (screenshot == null) {
+ Log.e(TAG, "Got null bitmap from screenshot message");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ finisher.accept(null);
+ mOnCompleteRunnable.run();
+ return;
+ }
+
+ if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
+ saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
+ } else {
+ saveScreenshot(screenshot, finisher,
+ new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
+ true);
+ }
+ }
+
+ /**
+ * Displays a screenshot selector
+ */
+ @SuppressLint("ClickableViewAccessibility")
+ void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
+ dismissScreenshot(true);
+ mOnCompleteRunnable = onComplete;
+
+ mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+
+ mScreenshotView.takePartialScreenshot(
+ rect -> takeScreenshotInternal(finisher, rect));
+ }
+
+ boolean isDismissing() {
+ return (mDismissAnimation != null && mDismissAnimation.isRunning());
+ }
+
+ /**
+ * Clears current screenshot
+ */
+ void dismissScreenshot(boolean immediate) {
+ Log.v(TAG, "clearing screenshot");
+ mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+ mScreenshotView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
+ mScreenshotView);
+ if (!immediate) {
+ mDismissAnimation = mScreenshotView.createScreenshotDismissAnimation();
+ mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ clearScreenshot();
+ }
+ });
+ mDismissAnimation.start();
+ } else {
+ clearScreenshot();
+ }
+ }
+
+ private void onConfigChanged(Configuration newConfig) {
+ boolean needsUpdate = false;
+ // dark mode
+ if (newConfig.isNightModeActive()) {
+ // Night mode is active, we're using dark theme
+ if (!mInDarkMode) {
+ mInDarkMode = true;
+ needsUpdate = true;
+ }
+ } else {
+ // Night mode is not active, we're using the light theme
+ if (mInDarkMode) {
+ mInDarkMode = false;
+ needsUpdate = true;
+ }
+ }
+
+ // RTL configuration
+ switch (newConfig.getLayoutDirection()) {
+ case View.LAYOUT_DIRECTION_LTR:
+ if (!mDirectionLTR) {
+ mDirectionLTR = true;
+ needsUpdate = true;
+ }
+ break;
+ case View.LAYOUT_DIRECTION_RTL:
+ if (mDirectionLTR) {
+ mDirectionLTR = false;
+ needsUpdate = true;
+ }
+ break;
+ }
+
+ // portrait/landscape orientation
+ switch (newConfig.orientation) {
+ case ORIENTATION_PORTRAIT:
+ if (!mOrientationPortrait) {
+ mOrientationPortrait = true;
+ needsUpdate = true;
+ }
+ break;
+ case ORIENTATION_LANDSCAPE:
+ if (mOrientationPortrait) {
+ mOrientationPortrait = false;
+ needsUpdate = true;
+ }
+ break;
+ }
+
+ if (needsUpdate) {
+ reloadAssets();
+ }
+ }
+
+ /**
+ * Update assets (called when the dark theme status changes). We only need to update the dismiss
+ * button and the actions container background, since the buttons are re-inflated on demand.
+ */
+ private void reloadAssets() {
+ boolean wasAttached = mScreenshotView != null && mScreenshotView.isAttachedToWindow();
+ if (wasAttached) {
+ mWindowManager.removeView(mScreenshotView);
+ }
+
+ // Inflate the screenshot layout
+ mScreenshotView = (ScreenshotView)
+ LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
+
+ // TODO(159460485): Remove this when focus is handled properly in the system
+ mScreenshotView.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
+ // Once the user touches outside, stop listening for input
+ setWindowFocusable(false);
+ }
+ return false;
+ });
+
+ mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ dismissScreenshot(false);
+ return true;
+ }
+ return false;
+ });
+
+ if (wasAttached) {
+ mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+ }
+ }
+
+ /**
+ * Takes a screenshot of the current display and shows an animation.
+ */
+ private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
+ // copy the input Rect, since SurfaceControl.screenshot can mutate it
+ Rect screenRect = new Rect(crop);
+ int width = crop.width();
+ int height = crop.height();
+ final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+ final SurfaceControl.DisplayCaptureArgs captureArgs =
+ new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+ .setSourceCrop(crop)
+ .setSize(width, height)
+ .build();
+ final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ SurfaceControl.captureDisplay(captureArgs);
+ Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+
+ if (screenshot == null) {
+ Log.e(TAG, "Screenshot bitmap was null");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ finisher.accept(null);
+ mOnCompleteRunnable.run();
+ return;
+ }
+
+ saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
+ }
+
+ private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
+ Insets screenInsets, boolean showFlash) {
+ if (mAccessibilityManager.isEnabled()) {
+ AccessibilityEvent event =
+ new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ event.setContentDescription(
+ mContext.getResources().getString(R.string.screenshot_saving_title));
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
+ if (mScreenshotView.isAttachedToWindow()) {
+ // if we didn't already dismiss for another reason
+ if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
+ }
+ dismissScreenshot(true);
+ }
+
+ mScreenBitmap = screenshot;
+
+ if (!isUserSetupComplete()) {
+ // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+ // and sharing shouldn't be exposed to the user.
+ saveScreenshotAndToast(finisher);
+ return;
+ }
+
+ // Optimizations
+ mScreenBitmap.setHasAlpha(false);
+ mScreenBitmap.prepareToDraw();
+
+ onConfigChanged(mContext.getResources().getConfiguration());
+
+ if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+ mDismissAnimation.cancel();
+ }
+
+ // The window is focusable by default
+ setWindowFocusable(true);
+
+ // Start the post-screenshot animation
+ startAnimation(finisher, screenRect, screenInsets, showFlash);
+ }
+
+ /**
+ * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
+ * failure).
+ */
+ private void saveScreenshotAndToast(Consumer<Uri> finisher) {
+ // Play the shutter sound to notify that we've taken a screenshot
+ mScreenshotHandler.post(() -> {
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+ });
+
+ saveScreenshotInWorkerThread(finisher,
+ new ScreenshotController.ActionsReadyListener() {
+ @Override
+ void onActionsReady(ScreenshotController.SavedImageData imageData) {
+ finisher.accept(imageData.uri);
+ if (imageData.uri == null) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_save_text);
+ } else {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+
+ mScreenshotHandler.post(() -> {
+ Toast.makeText(mContext, R.string.screenshot_saved_title,
+ Toast.LENGTH_SHORT).show();
+ });
+ }
+ }
+ });
+ }
+
+ /**
+ * Starts the animation after taking the screenshot
+ */
+ private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
+ boolean showFlash) {
+ mScreenshotHandler.post(() -> {
+ if (!mScreenshotView.isAttachedToWindow()) {
+ mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+ }
+
+ mScreenshotView.prepareForAnimation(mScreenBitmap, screenRect, screenInsets);
+
+ mScreenshotHandler.post(() -> {
+ mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(
+ mScreenshotView);
+
+ mScreenshotAnimation =
+ mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash,
+ this::onElementTapped);
+
+ saveScreenshotInWorkerThread(finisher,
+ new ScreenshotController.ActionsReadyListener() {
+ @Override
+ void onActionsReady(
+ ScreenshotController.SavedImageData imageData) {
+ showUiOnActionsReady(imageData);
+ }
+ });
+
+ // Play the shutter sound to notify that we've taken a screenshot
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+
+ mScreenshotAnimation.start();
+ });
+ });
+ }
+
+ /**
+ * Creates a new worker thread and saves the screenshot to the media store.
+ */
+ private void saveScreenshotInWorkerThread(
+ Consumer<Uri> finisher,
+ @Nullable ScreenshotController.ActionsReadyListener actionsReadyListener) {
+ ScreenshotController.SaveImageInBackgroundData
+ data = new ScreenshotController.SaveImageInBackgroundData();
+ data.image = mScreenBitmap;
+ data.finisher = finisher;
+ data.mActionsReadyListener = actionsReadyListener;
+
+ if (mSaveInBgTask != null) {
+ // just log success/failure for the pre-existing screenshot
+ mSaveInBgTask.setActionsReadyListener(
+ new ScreenshotController.ActionsReadyListener() {
+ @Override
+ void onActionsReady(ScreenshotController.SavedImageData imageData) {
+ logSuccessOnActionsReady(imageData);
+ }
+ });
+ }
+
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
+ mSaveInBgTask.execute();
+ }
+
+ /**
+ * Sets up the action shade and its entrance animation, once we get the screenshot URI.
+ */
+ private void showUiOnActionsReady(ScreenshotController.SavedImageData imageData) {
+ logSuccessOnActionsReady(imageData);
+
+ AccessibilityManager accessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
+ SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+
+ mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+ mScreenshotHandler.sendMessageDelayed(
+ mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
+ timeoutMs);
+
+ if (imageData.uri != null) {
+ mScreenshotHandler.post(() -> {
+ if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+ mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mScreenshotView.setChipIntents(
+ imageData, event -> onElementTapped(event));
+ }
+ });
+ } else {
+ mScreenshotView.setChipIntents(
+ imageData, this::onElementTapped);
+ }
+ });
+ }
+ }
+
+ private void onElementTapped(ScreenshotEvent event) {
+ mUiEventLogger.log(event);
+ dismissScreenshot(false);
+ mOnCompleteRunnable.run();
+ }
+
+ /**
+ * Logs success/failure of the screenshot saving task, and shows an error if it failed.
+ */
+ private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) {
+ if (imageData.uri == null) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_save_text);
+ } else {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+ }
+ }
+
+ private void clearScreenshot() {
+ if (mScreenshotView.isAttachedToWindow()) {
+ mWindowManager.removeView(mScreenshotView);
+ }
+
+ mScreenshotView.reset();
+ }
+
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+ }
+
+
+ /**
+ * Updates the window focusability. If the window is already showing, then it updates the
+ * window immediately, otherwise the layout params will be applied when the window is next
+ * shown.
+ */
+ private void setWindowFocusable(boolean focusable) {
+ if (focusable) {
+ mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ } else {
+ mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ }
+ if (mScreenshotView.isAttachedToWindow()) {
+ mWindowManager.updateViewLayout(mScreenshotView, mWindowLayoutParams);
+ }
+ }
+
+ /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
+ private static boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets,
+ Rect screenBounds) {
+ int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
+ int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom;
+
+ if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
+ || bitmap.getHeight() == 0) {
+ Log.e(TAG, String.format(
+ "Provided bitmap and insets create degenerate region: %dx%d %s",
+ bitmap.getWidth(), bitmap.getHeight(), bitmapInsets));
+ return false;
+ }
+
+ float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight;
+ float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
+
+ boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
+ if (!matchWithinTolerance) {
+ Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f",
+ insettedBitmapAspect, boundsAspect));
+ }
+
+ return matchWithinTolerance;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 6d1299ba98ac..db5a4941ca58 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -25,13 +25,6 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
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.os.UserHandle;
import android.util.DisplayMetrics;
import android.view.WindowManager;
@@ -52,179 +45,16 @@ public class ScreenshotNotificationsController {
private final Context mContext;
private final Resources mResources;
private final NotificationManager mNotificationManager;
- private final Notification.BigPictureStyle mNotificationStyle;
-
- private int mIconSize;
- private int mPreviewWidth, mPreviewHeight;
- private Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
@Inject
ScreenshotNotificationsController(Context context, WindowManager windowManager) {
mContext = context;
mResources = context.getResources();
-
mNotificationManager =
(NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
- mIconSize = mResources.getDimensionPixelSize(
- android.R.dimen.notification_large_icon_height);
-
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
-
-
- // determine the optimal preview size
- int panelWidth = 0;
- try {
- panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
- } catch (Resources.NotFoundException e) {
- }
- if (panelWidth <= 0) {
- // includes notification_panel_width==match_parent (-1)
- panelWidth = displayMetrics.widthPixels;
- }
- mPreviewWidth = panelWidth;
- mPreviewHeight = mResources.getDimensionPixelSize(R.dimen.notification_max_height);
-
- // Setup the notification
- mNotificationStyle = new Notification.BigPictureStyle();
- }
-
- /**
- * Resets the notification builders.
- */
- public void reset() {
- // The public notification will show similar info but with the actual screenshot omitted
- mPublicNotificationBuilder =
- new Notification.Builder(mContext, NotificationChannels.SCREENSHOTS_HEADSUP);
- mNotificationBuilder =
- new Notification.Builder(mContext, NotificationChannels.SCREENSHOTS_HEADSUP);
- }
-
- /**
- * Sets the current screenshot bitmap.
- *
- * @param image the bitmap of the current screenshot (used for preview)
- */
- public void setImage(Bitmap image) {
- // Create the large notification icon
- int imageWidth = image.getWidth();
- int imageHeight = image.getHeight();
-
- 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((mPreviewWidth - imageWidth) / 2f, (mPreviewHeight - imageHeight) / 2f);
-
- Bitmap picture = generateAdjustedHwBitmap(
- image, mPreviewWidth, mPreviewHeight, matrix, paint, overlayColor);
-
- mNotificationStyle.bigPicture(picture.asShared());
-
- // Note, we can't use the preview for the small icon, since it is non-square
- float scale = (float) mIconSize / Math.min(imageWidth, imageHeight);
- matrix.setScale(scale, scale);
- matrix.postTranslate(
- (mIconSize - (scale * imageWidth)) / 2,
- (mIconSize - (scale * imageHeight)) / 2);
- Bitmap icon =
- generateAdjustedHwBitmap(image, mIconSize, mIconSize, matrix, paint, overlayColor);
-
- /**
- * 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.asShared());
- // But we still don't set it for the expanded view, allowing the smallIcon to show here.
- mNotificationStyle.bigLargeIcon((Bitmap) null);
- }
-
- /**
- * Shows a notification to inform the user that a screenshot is currently being saved.
- */
- public void showSavingScreenshotNotification() {
- final long now = System.currentTimeMillis();
-
- mPublicNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setCategory(Notification.CATEGORY_PROGRESS)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
-
- mNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setStyle(mNotificationStyle)
- .setPublicVersion(mPublicNotificationBuilder.build());
- mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
- SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
-
- mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
- }
-
- /**
- * Shows a notification with the saved screenshot and actions that can be taken with it.
- *
- * @param actionData SavedImageData struct with image URI and actions
- */
- public void showScreenshotActionsNotification(
- GlobalScreenshot.SavedImageData actionData) {
- mNotificationBuilder.addAction(actionData.shareAction);
- mNotificationBuilder.addAction(actionData.editAction);
- mNotificationBuilder.addAction(actionData.deleteAction);
- for (Notification.Action smartAction : actionData.smartActions) {
- mNotificationBuilder.addAction(smartAction);
- }
-
- // Create the intent to show the screenshot in gallery
- Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(actionData.uri, "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(mResources.getString(R.string.screenshot_saved_title))
- .setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent
- .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- mNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
- .setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent
- .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(mContext.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());
}
/**
@@ -263,31 +93,4 @@ public class ScreenshotNotificationsController {
.build();
mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT, n);
}
-
- /**
- * Cancels the current screenshot notification.
- */
- public void cancelNotification() {
- mNotificationManager.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
- }
-
- /**
- * 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);
- }
-
- static void cancelScreenshotNotification(Context context) {
- final NotificationManager nm =
- (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
index 07a92460f3ea..c793b5b9639e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
@@ -26,8 +26,11 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
+import java.util.function.Consumer;
+
/**
* Draws a selection rectangle while taking screenshot
*/
@@ -36,6 +39,8 @@ public class ScreenshotSelectorView extends View {
private Rect mSelectionRect;
private final Paint mPaintSelection, mPaintBackground;
+ private Consumer<Rect> mOnScreenshotSelected;
+
public ScreenshotSelectorView(Context context) {
this(context, null);
}
@@ -46,14 +51,54 @@ public class ScreenshotSelectorView extends View {
mPaintBackground.setAlpha(160);
mPaintSelection = new Paint(Color.TRANSPARENT);
mPaintSelection.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+ setOnTouchListener((v, event) -> {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ startSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ updateSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_UP:
+ setVisibility(View.GONE);
+ final Rect rect = getSelectionRect();
+ if (mOnScreenshotSelected != null
+ && rect != null
+ && rect.width() != 0 && rect.height() != 0) {
+ mOnScreenshotSelected.accept(rect);
+ }
+ stopSelection();
+ return true;
+ }
+ return false;
+ });
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaintBackground);
+ if (mSelectionRect != null) {
+ canvas.drawRect(mSelectionRect, mPaintSelection);
+ }
+ }
+
+ void setOnScreenshotSelected(Consumer<Rect> onScreenshotSelected) {
+ mOnScreenshotSelected = onScreenshotSelected;
+ }
+
+ void stop() {
+ if (getSelectionRect() != null) {
+ stopSelection();
+ }
}
- public void startSelection(int x, int y) {
+ private void startSelection(int x, int y) {
mStartPoint = new Point(x, y);
mSelectionRect = new Rect(x, y, x, y);
}
- public void updateSelection(int x, int y) {
+ private void updateSelection(int x, int y) {
if (mSelectionRect != null) {
mSelectionRect.left = Math.min(mStartPoint.x, x);
mSelectionRect.right = Math.max(mStartPoint.x, x);
@@ -63,20 +108,12 @@ public class ScreenshotSelectorView extends View {
}
}
- public Rect getSelectionRect() {
+ private Rect getSelectionRect() {
return mSelectionRect;
}
- public void stopSelection() {
+ private void stopSelection() {
mStartPoint = null;
mSelectionRect = null;
}
-
- @Override
- public void draw(Canvas canvas) {
- canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaintBackground);
- if (mSelectionRect != null) {
- canvas.drawRect(mSelectionRect, mPaintSelection);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
new file mode 100644
index 000000000000..03fe2920405f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2020 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 static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static java.util.Objects.requireNonNull;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Outline;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Handles the visual elements and animations for the screenshot flow.
+ */
+public class ScreenshotView extends FrameLayout implements
+ ViewTreeObserver.OnComputeInternalInsetsListener {
+
+ private static final String TAG = "GlobalScreenshotView";
+
+ private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
+ private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
+ // delay before starting to fade in dismiss button
+ private static final long SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS = 200;
+ private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
+ private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
+ private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
+ private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
+ private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
+ private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
+ private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
+ private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
+ private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
+ private static final float ROUNDED_CORNER_RADIUS = .05f;
+
+ private final Interpolator mAccelerateInterpolator = new AccelerateInterpolator();
+
+ private final Resources mResources;
+ private final Interpolator mFastOutSlowIn;
+ private final DisplayMetrics mDisplayMetrics;
+ private final float mCornerSizeX;
+ private final float mDismissDeltaY;
+
+ private int mNavMode;
+ private int mLeftInset;
+ private int mRightInset;
+ private boolean mOrientationPortrait;
+ private boolean mDirectionLTR;
+
+ private ScreenshotSelectorView mScreenshotSelectorView;
+ private ImageView mScreenshotAnimatedView;
+ private ImageView mScreenshotPreview;
+ private ImageView mScreenshotFlash;
+ private ImageView mActionsContainerBackground;
+ private HorizontalScrollView mActionsContainer;
+ private LinearLayout mActionsView;
+ private ImageView mBackgroundProtection;
+ private FrameLayout mDismissButton;
+ private ScreenshotActionChip mShareChip;
+ private ScreenshotActionChip mEditChip;
+
+ private ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
+ private PendingInteraction mPendingInteraction;
+
+ private enum PendingInteraction {
+ PREVIEW,
+ EDIT,
+ SHARE
+ }
+
+ public ScreenshotView(Context context) {
+ this(context, null);
+ }
+
+ public ScreenshotView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ScreenshotView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ScreenshotView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mResources = mContext.getResources();
+
+ mCornerSizeX = mResources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
+ mDismissDeltaY = mResources.getDimensionPixelSize(
+ R.dimen.screenshot_dismissal_height_delta);
+
+ // standard material ease
+ mFastOutSlowIn =
+ AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
+
+ mDisplayMetrics = new DisplayMetrics();
+ mContext.getDisplay().getRealMetrics(mDisplayMetrics);
+ }
+
+ @Override // ViewTreeObserver.OnComputeInternalInsetsListener
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ Region touchRegion = new Region();
+
+ Rect screenshotRect = new Rect();
+ mScreenshotPreview.getBoundsOnScreen(screenshotRect);
+ touchRegion.op(screenshotRect, Region.Op.UNION);
+ Rect actionsRect = new Rect();
+ mActionsContainer.getBoundsOnScreen(actionsRect);
+ touchRegion.op(actionsRect, Region.Op.UNION);
+ Rect dismissRect = new Rect();
+ mDismissButton.getBoundsOnScreen(dismissRect);
+ touchRegion.op(dismissRect, Region.Op.UNION);
+
+ if (QuickStepContract.isGesturalMode(mNavMode)) {
+ // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
+ Rect inset = new Rect(0, 0, mLeftInset, mDisplayMetrics.heightPixels);
+ touchRegion.op(inset, Region.Op.UNION);
+ inset.set(mDisplayMetrics.widthPixels - mRightInset, 0, mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels);
+ touchRegion.op(inset, Region.Op.UNION);
+ }
+
+ inoutInfo.touchableRegion.set(touchRegion);
+ }
+
+ @Override // View
+ protected void onFinishInflate() {
+ mScreenshotAnimatedView = requireNonNull(
+ findViewById(R.id.global_screenshot_animated_view));
+ mScreenshotPreview = requireNonNull(findViewById(R.id.global_screenshot_preview));
+ mActionsContainerBackground = requireNonNull(findViewById(
+ R.id.global_screenshot_actions_container_background));
+ mActionsContainer = requireNonNull(findViewById(R.id.global_screenshot_actions_container));
+ mActionsView = requireNonNull(findViewById(R.id.global_screenshot_actions));
+ mBackgroundProtection = requireNonNull(
+ findViewById(R.id.global_screenshot_actions_background));
+ mDismissButton = requireNonNull(findViewById(R.id.global_screenshot_dismiss_button));
+ mScreenshotFlash = requireNonNull(findViewById(R.id.global_screenshot_flash));
+ mScreenshotSelectorView = requireNonNull(findViewById(R.id.global_screenshot_selector));
+ mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip));
+ mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip));
+
+ mScreenshotAnimatedView.setClipToOutline(true);
+ mScreenshotAnimatedView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
+ ROUNDED_CORNER_RADIUS * view.getWidth());
+ }
+ });
+ mScreenshotPreview.setClipToOutline(true);
+ mScreenshotPreview.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
+ ROUNDED_CORNER_RADIUS * view.getWidth());
+ }
+ });
+
+ setFocusable(true);
+ mScreenshotSelectorView.setFocusable(true);
+ mScreenshotSelectorView.setFocusableInTouchMode(true);
+ mScreenshotAnimatedView.setPivotX(0);
+ mScreenshotAnimatedView.setPivotY(0);
+ mActionsContainer.setScrollX(0);
+
+ mNavMode = getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode);
+ mOrientationPortrait =
+ getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
+ mDirectionLTR =
+ getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
+
+ setOnApplyWindowInsetsListener((v, insets) -> {
+ if (QuickStepContract.isGesturalMode(mNavMode)) {
+ Insets gestureInsets = insets.getInsets(
+ WindowInsets.Type.systemGestures());
+ mLeftInset = gestureInsets.left;
+ mRightInset = gestureInsets.right;
+ } else {
+ mLeftInset = mRightInset = 0;
+ }
+ return ScreenshotView.this.onApplyWindowInsets(insets);
+ });
+
+ // Get focus so that the key events go to the layout.
+ setFocusableInTouchMode(true);
+ requestFocus();
+ }
+
+ void takePartialScreenshot(Consumer<Rect> onPartialScreenshotSelected) {
+ mScreenshotSelectorView.setOnScreenshotSelected(onPartialScreenshotSelected);
+ mScreenshotSelectorView.setVisibility(View.VISIBLE);
+ mScreenshotSelectorView.requestFocus();
+ }
+
+ void prepareForAnimation(Bitmap bitmap, Rect screenRect, Insets screenInsets) {
+ mScreenshotAnimatedView.setImageDrawable(
+ createScreenDrawable(mResources, bitmap, screenInsets));
+ setAnimatedViewSize(screenRect.width(), screenRect.height());
+
+ // will show when the animation starts
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+
+ mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
+ // make static preview invisible (from gone) so we can query its location on screen
+ mScreenshotPreview.setVisibility(View.INVISIBLE);
+ }
+
+ AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash,
+ Consumer<ScreenshotEvent> onElementTapped) {
+ mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mScreenshotPreview.buildLayer();
+
+ Rect previewBounds = new Rect();
+ mScreenshotPreview.getBoundsOnScreen(previewBounds);
+
+ float cornerScale =
+ mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
+ final float currentScale = 1f;
+
+ mScreenshotAnimatedView.setScaleX(currentScale);
+ mScreenshotAnimatedView.setScaleY(currentScale);
+
+ mDismissButton.setAlpha(0);
+ mDismissButton.setVisibility(View.VISIBLE);
+
+ AnimatorSet dropInAnimation = new AnimatorSet();
+ ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
+ flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
+ flashInAnimator.setInterpolator(mFastOutSlowIn);
+ flashInAnimator.addUpdateListener(animation ->
+ mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+ ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
+ flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
+ flashOutAnimator.setInterpolator(mFastOutSlowIn);
+ flashOutAnimator.addUpdateListener(animation ->
+ mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+ // animate from the current location, to the static preview location
+ final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
+ final PointF finalPos = new PointF(previewBounds.centerX(), previewBounds.centerY());
+
+ ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
+ toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+ float xPositionPct =
+ SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ float dismissPct =
+ SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ float scalePct =
+ SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ toCorner.addUpdateListener(animation -> {
+ float t = animation.getAnimatedFraction();
+ if (t < scalePct) {
+ float scale = MathUtils.lerp(
+ currentScale, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
+ mScreenshotAnimatedView.setScaleX(scale);
+ mScreenshotAnimatedView.setScaleY(scale);
+ } else {
+ mScreenshotAnimatedView.setScaleX(cornerScale);
+ mScreenshotAnimatedView.setScaleY(cornerScale);
+ }
+
+ float currentScaleX = mScreenshotAnimatedView.getScaleX();
+ float currentScaleY = mScreenshotAnimatedView.getScaleY();
+
+ if (t < xPositionPct) {
+ float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
+ mFastOutSlowIn.getInterpolation(t / xPositionPct));
+ mScreenshotAnimatedView.setX(xCenter - bounds.width() * currentScaleX / 2f);
+ } else {
+ mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * currentScaleX / 2f);
+ }
+ float yCenter = MathUtils.lerp(
+ startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t));
+ mScreenshotAnimatedView.setY(yCenter - bounds.height() * currentScaleY / 2f);
+
+ if (t >= dismissPct) {
+ mDismissButton.setAlpha((t - dismissPct) / (1 - dismissPct));
+ float currentX = mScreenshotAnimatedView.getX();
+ float currentY = mScreenshotAnimatedView.getY();
+ mDismissButton.setY(currentY - mDismissButton.getHeight() / 2f);
+ if (mDirectionLTR) {
+ mDismissButton.setX(currentX
+ + bounds.width() * currentScaleX - mDismissButton.getWidth() / 2f);
+ } else {
+ mDismissButton.setX(currentX - mDismissButton.getWidth() / 2f);
+ }
+ }
+ });
+
+ toCorner.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mScreenshotAnimatedView.setVisibility(View.VISIBLE);
+ }
+ });
+
+ mScreenshotFlash.setAlpha(0f);
+ mScreenshotFlash.setVisibility(View.VISIBLE);
+
+ if (showFlash) {
+ dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
+ dropInAnimation.play(flashOutAnimator).with(toCorner);
+ } else {
+ dropInAnimation.play(toCorner);
+ }
+
+ dropInAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mDismissButton.setOnClickListener(view ->
+ onElementTapped.accept(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL));
+ mDismissButton.setAlpha(1);
+ float dismissOffset = mDismissButton.getWidth() / 2f;
+ float finalDismissX = mDirectionLTR
+ ? finalPos.x - dismissOffset + bounds.width() * cornerScale / 2f
+ : finalPos.x - dismissOffset - bounds.width() * cornerScale / 2f;
+ mDismissButton.setX(finalDismissX);
+ mDismissButton.setY(
+ finalPos.y - dismissOffset - bounds.height() * cornerScale / 2f);
+ mScreenshotAnimatedView.setScaleX(1);
+ mScreenshotAnimatedView.setScaleY(1);
+ mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * cornerScale / 2f);
+ mScreenshotAnimatedView.setY(finalPos.y - bounds.height() * cornerScale / 2f);
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+ mScreenshotPreview.setVisibility(View.VISIBLE);
+ forceLayout();
+ createScreenshotActionsShadeAnimation().start();
+ }
+ });
+
+ return dropInAnimation;
+ }
+
+ ValueAnimator createScreenshotActionsShadeAnimation() {
+ // By default the activities won't be able to start immediately; override this to keep
+ // the same behavior as if started from a notification
+ try {
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+
+ ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
+
+ mShareChip.setText(mContext.getString(com.android.internal.R.string.share));
+ mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
+ mShareChip.setOnClickListener(v -> {
+ mShareChip.setIsPending(true);
+ mEditChip.setIsPending(false);
+ mPendingInteraction = PendingInteraction.SHARE;
+ });
+ chips.add(mShareChip);
+
+ mEditChip.setText(mContext.getString(com.android.internal.R.string.screenshot_edit));
+ mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
+ mEditChip.setOnClickListener(v -> {
+ mEditChip.setIsPending(true);
+ mShareChip.setIsPending(false);
+ mPendingInteraction = PendingInteraction.EDIT;
+ });
+ chips.add(mEditChip);
+
+ mScreenshotPreview.setOnClickListener(v -> {
+ mShareChip.setIsPending(false);
+ mEditChip.setIsPending(false);
+ mPendingInteraction = PendingInteraction.PREVIEW;
+ });
+
+ // remove the margin from the last chip so that it's correctly aligned with the end
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
+ mActionsView.getChildAt(0).getLayoutParams();
+ params.setMarginEnd(0);
+ mActionsView.getChildAt(0).setLayoutParams(params);
+
+ ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS);
+ float alphaFraction = (float) SCREENSHOT_ACTIONS_ALPHA_DURATION_MS
+ / SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS;
+ mActionsContainer.setAlpha(0f);
+ mActionsContainerBackground.setAlpha(0f);
+ mActionsContainer.setVisibility(View.VISIBLE);
+ mActionsContainerBackground.setVisibility(View.VISIBLE);
+
+ animator.addUpdateListener(animation -> {
+ float t = animation.getAnimatedFraction();
+ mBackgroundProtection.setAlpha(t);
+ float containerAlpha = t < alphaFraction ? t / alphaFraction : 1;
+ mActionsContainer.setAlpha(containerAlpha);
+ mActionsContainerBackground.setAlpha(containerAlpha);
+ float containerScale = SCREENSHOT_ACTIONS_START_SCALE_X
+ + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X));
+ mActionsContainer.setScaleX(containerScale);
+ mActionsContainerBackground.setScaleX(containerScale);
+ for (ScreenshotActionChip chip : chips) {
+ chip.setAlpha(t);
+ chip.setScaleX(1 / containerScale); // invert to keep size of children constant
+ }
+ mActionsContainer.setScrollX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
+ mActionsContainer.setPivotX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
+ mActionsContainerBackground.setPivotX(
+ mDirectionLTR ? 0 : mActionsContainerBackground.getWidth());
+ });
+ return animator;
+ }
+
+ void setChipIntents(ScreenshotController.SavedImageData imageData,
+ Consumer<ScreenshotEvent> onElementTapped) {
+ mShareChip.setPendingIntent(imageData.shareAction.actionIntent,
+ () -> onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED));
+ mEditChip.setPendingIntent(imageData.editAction.actionIntent,
+ () -> onElementTapped.accept(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED));
+ mScreenshotPreview.setOnClickListener(v -> {
+ try {
+ imageData.editAction.actionIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Intent cancelled", e);
+ }
+ onElementTapped.accept(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
+ });
+
+ if (mPendingInteraction != null) {
+ switch (mPendingInteraction) {
+ case PREVIEW:
+ mScreenshotPreview.callOnClick();
+ break;
+ case SHARE:
+ mShareChip.callOnClick();
+ break;
+ case EDIT:
+ mEditChip.callOnClick();
+ break;
+ }
+ } else {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ for (Notification.Action smartAction : imageData.smartActions) {
+ ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
+ R.layout.global_screenshot_action_chip, mActionsView, false);
+ actionChip.setText(smartAction.title);
+ actionChip.setIcon(smartAction.getIcon(), false);
+ actionChip.setPendingIntent(smartAction.actionIntent,
+ () -> onElementTapped.accept(
+ ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED));
+ mActionsView.addView(actionChip);
+ mSmartChips.add(actionChip);
+ }
+ }
+ }
+
+
+ AnimatorSet createScreenshotDismissAnimation() {
+ ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
+ alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
+ alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS);
+ alphaAnim.addUpdateListener(animation -> {
+ setAlpha(1 - animation.getAnimatedFraction());
+ });
+
+ ValueAnimator yAnim = ValueAnimator.ofFloat(0, 1);
+ yAnim.setInterpolator(mAccelerateInterpolator);
+ yAnim.setDuration(SCREENSHOT_DISMISS_Y_DURATION_MS);
+ float screenshotStartY = mScreenshotPreview.getTranslationY();
+ float dismissStartY = mDismissButton.getTranslationY();
+ yAnim.addUpdateListener(animation -> {
+ float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());
+ mScreenshotPreview.setTranslationY(screenshotStartY + yDelta);
+ mDismissButton.setTranslationY(dismissStartY + yDelta);
+ mActionsContainer.setTranslationY(yDelta);
+ mActionsContainerBackground.setTranslationY(yDelta);
+ });
+
+ AnimatorSet animSet = new AnimatorSet();
+ animSet.play(yAnim).with(alphaAnim);
+
+ return animSet;
+ }
+
+ void reset() {
+ // Clear any references to the bitmap
+ mScreenshotPreview.setImageDrawable(null);
+ mScreenshotAnimatedView.setImageDrawable(null);
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+ mActionsContainerBackground.setVisibility(View.GONE);
+ mActionsContainer.setVisibility(View.GONE);
+ mBackgroundProtection.setAlpha(0f);
+ mDismissButton.setVisibility(View.GONE);
+ mScreenshotPreview.setVisibility(View.GONE);
+ mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
+ mScreenshotPreview.setContentDescription(
+ mContext.getResources().getString(R.string.screenshot_preview_description));
+ mScreenshotPreview.setOnClickListener(null);
+ mShareChip.setOnClickListener(null);
+ mEditChip.setOnClickListener(null);
+ mShareChip.setIsPending(false);
+ mEditChip.setIsPending(false);
+ mPendingInteraction = null;
+ for (ScreenshotActionChip chip : mSmartChips) {
+ mActionsView.removeView(chip);
+ }
+ mSmartChips.clear();
+ setAlpha(1);
+ mDismissButton.setTranslationY(0);
+ mActionsContainer.setTranslationY(0);
+ mActionsContainerBackground.setTranslationY(0);
+ mScreenshotPreview.setTranslationY(0);
+ mScreenshotSelectorView.stop();
+ }
+
+ private void setAnimatedViewSize(int width, int height) {
+ ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
+ layoutParams.width = width;
+ layoutParams.height = height;
+ mScreenshotAnimatedView.setLayoutParams(layoutParams);
+ }
+
+ /**
+ * Create a drawable using the size of the bitmap and insets as the fractional inset parameters.
+ */
+ private static Drawable createScreenDrawable(Resources res, Bitmap bitmap, Insets insets) {
+ int insettedWidth = bitmap.getWidth() - insets.left - insets.right;
+ int insettedHeight = bitmap.getHeight() - insets.top - insets.bottom;
+
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bitmap);
+ if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
+ || bitmap.getHeight() == 0) {
+ Log.e(TAG, String.format(
+ "Can't create insetted drawable, using 0 insets "
+ + "bitmap and insets create degenerate region: %dx%d %s",
+ bitmap.getWidth(), bitmap.getHeight(), insets));
+ return bitmapDrawable;
+ }
+
+ InsetDrawable insetDrawable = new InsetDrawable(bitmapDrawable,
+ -1f * insets.left / insettedWidth,
+ -1f * insets.top / insettedHeight,
+ -1f * insets.right / insettedWidth,
+ -1f * insets.bottom / insettedHeight);
+
+ if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
+ // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
+ // to fill in the background of the drawable.
+ return new LayerDrawable(new Drawable[]{
+ new ColorDrawable(Color.BLACK), insetDrawable});
+ } else {
+ return insetDrawable;
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
index 217235b16ecf..f32529fdaf04 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -16,9 +16,9 @@
package com.android.systemui.screenshot;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
import android.app.ActivityOptions;
import android.app.PendingIntent;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index a043f0f1e50c..4e2283396e25 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -52,7 +52,7 @@ import javax.inject.Inject;
public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";
- private final GlobalScreenshot mScreenshot;
+ private final ScreenshotController mScreenshot;
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
@@ -61,7 +61,7 @@ public class TakeScreenshotService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
- mScreenshot.dismissScreenshot("close system dialogs", false);
+ mScreenshot.dismissScreenshot(false);
}
}
};
@@ -125,7 +125,7 @@ public class TakeScreenshotService extends Service {
};
@Inject
- public TakeScreenshotService(GlobalScreenshot globalScreenshot, UserManager userManager,
+ public TakeScreenshotService(ScreenshotController globalScreenshot, UserManager userManager,
UiEventLogger uiEventLogger) {
mScreenshot = globalScreenshot;
mUserManager = userManager;
@@ -144,7 +144,9 @@ public class TakeScreenshotService extends Service {
@Override
public boolean onUnbind(Intent intent) {
- if (mScreenshot != null) mScreenshot.stopScreenshot();
+ if (mScreenshot != null && !mScreenshot.isDismissing()) {
+ mScreenshot.dismissScreenshot(true);
+ }
unregisterReceiver(mBroadcastReceiver);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index b7f4e67e48d1..597658276d49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -17,18 +17,16 @@
package com.android.systemui.statusbar;
import android.app.Notification;
-import android.content.res.Configuration;
-import android.graphics.PorterDuff;
import android.graphics.drawable.Icon;
import android.text.TextUtils;
-import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.ConversationLayout;
+import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentView;
@@ -67,30 +65,14 @@ public class NotificationHeaderUtil {
private final static ResultApplicator mGreyApplicator = new ResultApplicator() {
@Override
public void apply(View parent, View view, boolean apply, boolean reset) {
- NotificationHeaderView header = (NotificationHeaderView) view;
- ImageView icon = view.findViewById(com.android.internal.R.id.icon);
- ImageView expand = view.findViewById(com.android.internal.R.id.expand_button);
- applyToChild(icon, apply, header.getOriginalIconColor());
- applyToChild(expand, apply, header.getOriginalNotificationColor());
- }
-
- private void applyToChild(View view, boolean shouldApply, int originalColor) {
- if (originalColor != NotificationHeaderView.NO_COLOR) {
- ImageView imageView = (ImageView) view;
- imageView.getDrawable().mutate();
- if (shouldApply) {
- // lets gray it out
- Configuration config = view.getContext().getResources().getConfiguration();
- boolean inNightMode = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- int grey = ContrastColorUtil.resolveColor(view.getContext(),
- Notification.COLOR_DEFAULT, inNightMode);
- imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
- } else {
- // lets reset it
- imageView.getDrawable().setColorFilter(originalColor,
- PorterDuff.Mode.SRC_ATOP);
- }
+ CachingIconView icon = view.findViewById(com.android.internal.R.id.icon);
+ if (icon != null) {
+ icon.setGrayedOut(apply);
+ }
+ NotificationExpandButton expand =
+ view.findViewById(com.android.internal.R.id.expand_button);
+ if (expand != null) {
+ expand.setGrayedOut(apply);
}
}
};
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 f788dfe47a61..3f13306b7cf2 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
@@ -2856,7 +2856,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
float x = event.getX();
float y = event.getY();
- NotificationHeaderView header = getVisibleNotificationViewWrapper().getNotificationHeader();
+ NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
+ NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader();
if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
return true;
}
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 8314aff99c65..8c8275628fac 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
@@ -276,6 +276,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
@Override
+ public void setExpanded(boolean expanded) {
+ mExpandButton.setExpanded(expanded);
+ }
+
+ @Override
public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
if (mAudiblyAlertedIcon != null) {
mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index e2f0799a5c35..6920e3f4a7c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -229,6 +229,9 @@ public abstract class NotificationViewWrapper implements TransformableView {
*/
public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
+ /** Set the expanded state on the view wrapper */
+ public void setExpanded(boolean expanded) {}
+
/**
* @return the notification header if it exists
*/
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 a5d51c64d4ee..00bccfc1a323 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
@@ -848,8 +848,8 @@ public class NotificationChildrenContainer extends ViewGroup {
public void setChildrenExpanded(boolean childrenExpanded) {
mChildrenExpanded = childrenExpanded;
updateExpansionStates();
- if (mNotificationHeader != null) {
- mNotificationHeader.setExpanded(childrenExpanded);
+ if (mNotificationHeaderWrapper != null) {
+ mNotificationHeaderWrapper.setExpanded(childrenExpanded);
}
final int count = mAttachedChildren.size();
for (int childIdx = 0; childIdx < count; childIdx++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index eb2d9bce6c4e..6b991f8ec521 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -23,7 +23,6 @@ import android.os.Handler;
import android.os.Looper;
import android.provider.Settings.Global;
import android.telephony.Annotation;
-import android.telephony.CdmaEriInformation;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.PhoneStateListener;
@@ -427,11 +426,8 @@ public class MobileSignalController extends SignalController<
if (isCarrierNetworkChangeActive()) {
return false;
}
- if (isCdma() && mServiceState != null) {
- final int iconMode = mPhone.getCdmaEriInformation().getEriIconMode();
- return mPhone.getCdmaEriInformation().getEriIconIndex() != CdmaEriInformation.ERI_OFF
- && (iconMode == CdmaEriInformation.ERI_ICON_MODE_NORMAL
- || iconMode == CdmaEriInformation.ERI_ICON_MODE_FLASH);
+ if (isCdma()) {
+ return mPhone.getCdmaEnhancedRoamingIndicatorIconIndex() != TelephonyManager.ERI_OFF;
} else {
return mServiceState != null && mServiceState.getRoaming();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index 3dbc6f101a90..e8b837ee5bed 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -26,8 +26,8 @@ import android.view.View.OnAttachStateChangeListener;
*
* Implementations should handle setup and teardown related activities inside of
* {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on
- * any child controllers that this uses. This can be done in {@link init()} if the controllers
- * are injected, or right after creation time of the child controller.
+ * any child controllers that this uses. This can be done in {@link #initInternal()} if the
+ * controllers are injected, or right after creation time of the child controller.
*
* Tip: View "attachment" happens top down - parents are notified that they are attached before
* any children. That means that if you call a method on a child controller in
@@ -62,11 +62,18 @@ public abstract class ViewController<T extends View> {
mView = view;
}
- /** Call immediately after constructing Controller in order to handle view lifecycle events. */
+ /**
+ * Call immediately after constructing Controller in order to handle view lifecycle events.
+ *
+ * Generally speaking, you don't want to override this method. Instead, override
+ * {@link #initInternal()} as a way to have an run-once idempotent method that you can use for
+ * setup of your ViewController.
+ */
public void init() {
if (mInited) {
return;
}
+ initInternal();
mInited = true;
if (mView != null) {
@@ -77,6 +84,14 @@ public abstract class ViewController<T extends View> {
}
}
+ /**
+ * Run once when {@link #init()} is called.
+ *
+ * Override this to perform idempotent, one-time setup that your controller needs. It will
+ * be called before {@link #onViewAttached()}.
+ */
+ protected void initInternal() {}
+
protected Context getContext() {
return mView.getContext();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index da00e7e9518b..1a78ca407467 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -53,7 +53,7 @@ class FakeBroadcastDispatcher(
receiver: BroadcastReceiver,
filter: IntentFilter,
executor: Executor?,
- user: UserHandle
+ user: UserHandle?
) {
registeredReceivers.add(receiver)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 2d460aa0c9c1..521888658d68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -25,7 +25,7 @@ import static org.mockito.Mockito.when;
import android.graphics.drawable.Icon;
import android.testing.AndroidTestingRunner;
import android.view.View;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
@@ -66,7 +66,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
public void setUp() {
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
- .onCreateViewHolder(new FrameLayout(mContext), 0);
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
@@ -75,6 +75,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1);
+ when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(true);
when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
@@ -107,6 +108,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
R.string.media_output_dialog_pairing_new));
}
@@ -118,19 +124,41 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@Test
+ public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+ }
+
+ @Test
public void onBindViewHolder_bindDisconnectedBluetoothDevice_verifyView() {
when(mMediaDevice2.getDeviceType()).thenReturn(
MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
when(mMediaDevice2.isConnected()).thenReturn(false);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2));
@@ -142,9 +170,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText(
@@ -162,7 +194,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@@ -174,8 +210,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@@ -183,7 +224,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
when(mMediaOutputController.isZeroMode()).thenReturn(true);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
- mViewHolder.mFrameLayout.performClick();
+ mViewHolder.mContainerLayout.performClick();
verify(mMediaOutputController).launchBluetoothPairing();
}
@@ -194,7 +235,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- mViewHolder.mFrameLayout.performClick();
+ mViewHolder.mContainerLayout.performClick();
verify(mMediaOutputController).connectDevice(mMediaDevice2);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 27b5b7fda684..c897d8a53e20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -39,6 +39,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.Before;
@@ -58,6 +59,8 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
private ShadeController mShadeController = mock(ShadeController.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
@@ -68,8 +71,9 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
@Before
public void setUp() {
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0dcdecfdaadb..6ceac131ed4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -27,14 +27,19 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Notification;
import android.content.Context;
+import android.graphics.drawable.Icon;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
+import android.text.TextUtils;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -44,6 +49,8 @@ import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.Before;
@@ -60,6 +67,9 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "com.test.package.name";
private static final String TEST_DEVICE_1_ID = "test_device_1_id";
private static final String TEST_DEVICE_2_ID = "test_device_2_id";
+ private static final String TEST_DEVICE_3_ID = "test_device_3_id";
+ private static final String TEST_DEVICE_4_ID = "test_device_4_id";
+ private static final String TEST_DEVICE_5_ID = "test_device_5_id";
private static final String TEST_ARTIST = "test_artist";
private static final String TEST_SONG = "test_song";
private static final String TEST_SESSION_ID = "test_session_id";
@@ -77,6 +87,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class);
private ShadeController mShadeController = mock(ShadeController.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
@@ -96,8 +108,10 @@ public class MediaOutputControllerTest extends SysuiTestCase {
MediaSessionManager.class);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
mCachedBluetoothDeviceManager);
- mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
- mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+
+ mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -139,8 +153,9 @@ public class MediaOutputControllerTest extends SysuiTestCase {
@Test
public void start_withoutPackageName_verifyMediaControllerInit() {
- mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
- mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mMediaOutputController.start(mCb);
@@ -159,8 +174,10 @@ public class MediaOutputControllerTest extends SysuiTestCase {
@Test
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
- mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
- mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
+
mMediaOutputController.start(mCb);
mMediaOutputController.stop();
@@ -345,4 +362,127 @@ public class MediaOutputControllerTest extends SysuiTestCase {
assertThat(mMediaOutputController.isZeroMode()).isFalse();
}
+
+ @Test
+ public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
+ final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+ final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+ final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+ when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+ when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+ when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+ when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ selectableMediaDevices.add(selectableMediaDevice2);
+ doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+ doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+ final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+ // Reset order
+ selectedMediaDevices.clear();
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectableMediaDevices.clear();
+ selectableMediaDevices.add(selectableMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+ assertThat(newDevices.size()).isEqualTo(groupMediaDevices.size());
+ for (int i = 0; i < groupMediaDevices.size(); i++) {
+ assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+ newDevices.get(i).getId())).isTrue();
+ }
+ }
+
+ @Test
+ public void getGroupMediaDevices_newDevice_verifyDeviceOrder() {
+ final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice3 = mock(MediaDevice.class);
+ final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+ final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+ when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+ when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+ when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+ when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+ when(selectableMediaDevice3.getId()).thenReturn(TEST_DEVICE_5_ID);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ selectableMediaDevices.add(selectableMediaDevice2);
+ doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+ doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+ final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+ // Reset order
+ selectedMediaDevices.clear();
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectableMediaDevices.clear();
+ selectableMediaDevices.add(selectableMediaDevice3);
+ selectableMediaDevices.add(selectableMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+ assertThat(newDevices.size()).isEqualTo(5);
+ for (int i = 0; i < groupMediaDevices.size(); i++) {
+ assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+ newDevices.get(i).getId())).isTrue();
+ }
+ assertThat(newDevices.get(4).getId()).isEqualTo(TEST_DEVICE_5_ID);
+ }
+
+ @Test
+ public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
+
+ assertThat(mMediaOutputController.getNotificationIcon()).isNull();
+ }
+
+ @Test
+ public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() {
+ final List<NotificationEntry> entryList = new ArrayList<>();
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ final StatusBarNotification sbn = mock(StatusBarNotification.class);
+ final Notification notification = mock(Notification.class);
+ final Icon icon = mock(Icon.class);
+ entryList.add(entry);
+
+ when(mNotificationEntryManager.getActiveNotificationsForCurrentUser())
+ .thenReturn(entryList);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(notification.hasMediaSession()).thenReturn(true);
+ when(notification.getLargeIcon()).thenReturn(icon);
+
+ assertThat(mMediaOutputController.getNotificationIcon() instanceof IconCompat).isTrue();
+ }
+
+ @Test
+ public void getNotificationLargeIcon_withPackageNameAndNoMediaSession_returnsNull() {
+ final List<NotificationEntry> entryList = new ArrayList<>();
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ final StatusBarNotification sbn = mock(StatusBarNotification.class);
+ final Notification notification = mock(Notification.class);
+ final Icon icon = mock(Icon.class);
+ entryList.add(entry);
+
+ when(mNotificationEntryManager.getActiveNotificationsForCurrentUser())
+ .thenReturn(entryList);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(notification.hasMediaSession()).thenReturn(false);
+ when(notification.getLargeIcon()).thenReturn(icon);
+
+ assertThat(mMediaOutputController.getNotificationIcon()).isNull();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 9ebb587d3d85..c1e7db1f9b5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -34,6 +34,7 @@ import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.After;
@@ -58,6 +59,8 @@ public class MediaOutputDialogTest extends SysuiTestCase {
private ActivityStarter mStarter = mock(ActivityStarter.class);
private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
private MediaDevice mMediaDevice = mock(MediaDevice.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -65,8 +68,9 @@ public class MediaOutputDialogTest extends SysuiTestCase {
@Before
public void setUp() {
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
new file mode 100644
index 000000000000..1f85112dfb74
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2020 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.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.drawable.Icon;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MediaOutputGroupAdapterTest extends SysuiTestCase {
+
+ private static final String TEST_DEVICE_NAME_1 = "test_device_name_1";
+ private static final String TEST_DEVICE_NAME_2 = "test_device_name_2";
+ private static final String TEST_DEVICE_ID_1 = "test_device_id_1";
+ private static final String TEST_DEVICE_ID_2 = "test_device_id_2";
+ private static final int TEST_VOLUME = 10;
+ private static final int TEST_MAX_VOLUME = 50;
+
+ // Mock
+ private MediaOutputController mMediaOutputController = mock(MediaOutputController.class);
+ private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
+ private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
+ private Icon mIcon = mock(Icon.class);
+ private IconCompat mIconCompat = mock(IconCompat.class);
+
+ private MediaOutputGroupAdapter mGroupAdapter;
+ private MediaOutputGroupAdapter.GroupViewHolder mGroupViewHolder;
+ private List<MediaDevice> mGroupMediaDevices = new ArrayList<>();
+ private List<MediaDevice> mSelectableMediaDevices = new ArrayList<>();
+ private List<MediaDevice> mSelectedMediaDevices = new ArrayList<>();
+ private List<MediaDevice> mDeselectableMediaDevices = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ when(mMediaOutputController.getGroupMediaDevices()).thenReturn(mGroupMediaDevices);
+ when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
+ when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mSelectableMediaDevices);
+ when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mSelectedMediaDevices);
+ when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn(
+ mDeselectableMediaDevices);
+ when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
+ when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
+ when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(mMediaDevice2.getName()).thenReturn(TEST_DEVICE_NAME_2);
+ when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_ID_2);
+ mGroupMediaDevices.add(mMediaDevice1);
+ mGroupMediaDevices.add(mMediaDevice2);
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectableMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.add(mMediaDevice1);
+
+ mGroupAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
+ mGroupViewHolder = (MediaOutputGroupAdapter.GroupViewHolder) mGroupAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ }
+
+ @Test
+ public void onBindViewHolder_verifyGroupItem() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText(
+ R.string.media_output_dialog_group));
+ }
+
+ @Test
+ public void onBindViewHolder_singleSelectedDevice_verifyView() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+ // Disabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_multipleSelectedDevice_verifyView() {
+ mSelectedMediaDevices.clear();
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectedMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.clear();
+ mDeselectableMediaDevices.add(mMediaDevice1);
+ mDeselectableMediaDevices.add(mMediaDevice2);
+ mSelectableMediaDevices.clear();
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+ // Enabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void onBindViewHolder_notDeselectedDevice_verifyView() {
+ mSelectedMediaDevices.clear();
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectedMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.clear();
+ mDeselectableMediaDevices.add(mMediaDevice1);
+ mSelectableMediaDevices.clear();
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+ // Disabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_selectableDevice_verifyCheckBox() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isFalse();
+ // Enabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void onBindViewHolder_verifySessionVolume() {
+ when(mMediaOutputController.getSessionVolume()).thenReturn(TEST_VOLUME);
+ when(mMediaOutputController.getSessionVolumeMax()).thenReturn(TEST_MAX_VOLUME);
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);
+
+ assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
+ assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
+ }
+
+ @Test
+ public void onBindViewHolder_verifyDeviceVolume() {
+ when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_VOLUME);
+ when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+ assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
+ assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
+ }
+
+ @Test
+ public void clickSelectedDevice_verifyRemoveDeviceFromPlayMedia() {
+ mSelectedMediaDevices.clear();
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectedMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.clear();
+ mDeselectableMediaDevices.add(mMediaDevice1);
+ mDeselectableMediaDevices.add(mMediaDevice2);
+ mSelectableMediaDevices.clear();
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+ mGroupViewHolder.mCheckBox.performClick();
+
+ verify(mMediaOutputController).removeDeviceFromPlayMedia(mMediaDevice1);
+ }
+
+ @Test
+ public void clickSelectabelDevice_verifyAddDeviceToPlayMedia() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+ mGroupViewHolder.mCheckBox.performClick();
+
+ verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
new file mode 100644
index 000000000000..581335027671
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 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.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.media.session.MediaSessionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputGroupDialogTest extends SysuiTestCase {
+
+ private static final String TEST_PACKAGE = "test_package";
+
+ // Mock
+ private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+ private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private ShadeController mShadeController = mock(ShadeController.class);
+ private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
+ private MediaDevice mMediaDevice = mock(MediaDevice.class);
+ private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
+
+ private MediaOutputGroupDialog mMediaOutputGroupDialog;
+ private MediaOutputController mMediaOutputController;
+ private List<MediaDevice> mMediaDevices = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
+ mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+ mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
+ mMediaOutputController);
+ when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+ }
+
+ @After
+ public void tearDown() {
+ mMediaOutputGroupDialog.dismissDialog();
+ }
+
+ @Test
+ public void getStopButtonVisibility_returnVisible() {
+ assertThat(mMediaOutputGroupDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void getHeaderSubtitle_singleDevice_verifyTitle() {
+ mMediaDevices.add(mMediaDevice);
+
+ assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(
+ mContext.getText(R.string.media_output_dialog_single_device));
+ }
+
+ @Test
+ public void getHeaderSubtitle_multipleDevices_verifyTitle() {
+ mMediaDevices.add(mMediaDevice);
+ mMediaDevices.add(mMediaDevice1);
+
+ assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(mContext.getString(
+ R.string.media_output_dialog_multiple_devices, mMediaDevices.size()));
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index 4aaafbdaec1d..de176b84ac4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -16,9 +16,9 @@
package com.android.systemui.screenshot;
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
import static org.mockito.ArgumentMatchers.any;
@@ -79,7 +79,7 @@ public class ActionProxyReceiverTest extends SysuiTestCase {
public void setup() throws InterruptedException, ExecutionException, TimeoutException {
MockitoAnnotations.initMocks(this);
mIntent = new Intent(mContext, ActionProxyReceiver.class)
- .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+ .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
when(mMockActivityManagerWrapper.closeSystemWindows(anyString())).thenReturn(mMockFuture);
when(mMockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
index b9249131c191..14c76798e0ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -16,10 +16,10 @@
package com.android.systemui.screenshot;
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index e23f92616565..2374b82aa9ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -169,8 +169,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Looper.prepare();
}
- GlobalScreenshot.SaveImageInBackgroundData
- data = new GlobalScreenshot.SaveImageInBackgroundData();
+ ScreenshotController.SaveImageInBackgroundData
+ data = new ScreenshotController.SaveImageInBackgroundData();
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
@@ -183,9 +183,9 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Intent intent = shareAction.actionIntent.getIntent();
assertNotNull(intent);
Bundle bundle = intent.getExtras();
- assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
- assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
- assertEquals(GlobalScreenshot.ACTION_TYPE_SHARE, shareAction.title);
+ assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
+ assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
+ assertEquals(ScreenshotController.ACTION_TYPE_SHARE, shareAction.title);
assertEquals(Intent.ACTION_SEND, intent.getAction());
}
@@ -196,8 +196,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Looper.prepare();
}
- GlobalScreenshot.SaveImageInBackgroundData
- data = new GlobalScreenshot.SaveImageInBackgroundData();
+ ScreenshotController.SaveImageInBackgroundData
+ data = new ScreenshotController.SaveImageInBackgroundData();
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
@@ -210,9 +210,9 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Intent intent = editAction.actionIntent.getIntent();
assertNotNull(intent);
Bundle bundle = intent.getExtras();
- assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
- assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
- assertEquals(GlobalScreenshot.ACTION_TYPE_EDIT, editAction.title);
+ assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
+ assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
+ assertEquals(ScreenshotController.ACTION_TYPE_EDIT, editAction.title);
assertEquals(Intent.ACTION_EDIT, intent.getAction());
}
@@ -223,8 +223,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Looper.prepare();
}
- GlobalScreenshot.SaveImageInBackgroundData
- data = new GlobalScreenshot.SaveImageInBackgroundData();
+ ScreenshotController.SaveImageInBackgroundData
+ data = new ScreenshotController.SaveImageInBackgroundData();
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
@@ -238,9 +238,9 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Intent intent = deleteAction.actionIntent.getIntent();
assertNotNull(intent);
Bundle bundle = intent.getExtras();
- assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
- assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
- assertEquals(deleteAction.title, GlobalScreenshot.ACTION_TYPE_DELETE);
+ assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
+ assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
+ assertEquals(deleteAction.title, ScreenshotController.ACTION_TYPE_DELETE);
assertNull(intent.getAction());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
index ce6f0736ec33..6f3a4a17a4a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -16,8 +16,8 @@
package com.android.systemui.screenshot;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -56,7 +56,7 @@ public class SmartActionsReceiverTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
mIntent = new Intent(mContext, SmartActionsReceiver.class)
- .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+ .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 9971e0cf81a3..71f146bf0220 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -74,8 +74,6 @@ public class StatusBarIconViewTest extends SysuiTestCase {
mMockResources = mock(Resources.class);
mPackageManagerSpy = spy(getContext().getPackageManager());
doReturn(mMockResources).when(mPackageManagerSpy)
- .getResourcesForApplicationAsUser(eq("mockPackage"), anyInt());
- doReturn(mMockResources).when(mPackageManagerSpy)
.getResourcesForApplication(eq("mockPackage"));
doReturn(mMockResources).when(mPackageManagerSpy).getResourcesForApplication(argThat(
(ArgumentMatcher<ApplicationInfo>) o -> "mockPackage".equals(o.packageName)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 7db1b836f428..13cf679cc5d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -45,7 +45,6 @@ import android.net.wifi.WifiManager;
import android.os.Handler;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.telephony.CdmaEriInformation;
import android.telephony.CellSignalStrength;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
@@ -121,8 +120,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
private NetworkCapabilities mNetCapabilities;
private ConnectivityManager.NetworkCallback mNetworkCallback;
- private CdmaEriInformation mEriInformation;
-
@Rule
public TestWatcher failWatcher = new TestWatcher() {
@Override
@@ -184,11 +181,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mTelephonyDisplayInfo).getNetworkType();
doReturn(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE).when(mTelephonyDisplayInfo)
.getOverrideNetworkType();
-
- mEriInformation = new CdmaEriInformation(CdmaEriInformation.ERI_OFF,
- CdmaEriInformation.ERI_ICON_MODE_NORMAL);
- when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
-
mConfig = new Config();
mConfig.hspaDataDistinguishable = true;
mCallbackHandler = mock(CallbackHandler.class);
@@ -322,9 +314,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
}
public void setCdmaRoaming(boolean isRoaming) {
- mEriInformation.setEriIconIndex(isRoaming ?
- CdmaEriInformation.ERI_ON : CdmaEriInformation.ERI_OFF);
- when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
+ when(mMockTm.getCdmaEnhancedRoamingIndicatorIconIndex()).thenReturn(
+ isRoaming ? TelephonyManager.ERI_ON : TelephonyManager.ERI_OFF);
}
public void setVoiceRegState(int voiceRegState) {
diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json
index 538ffb3ad6e4..11e205d1b7ab 100644
--- a/packages/Tethering/apex/manifest.json
+++ b/packages/Tethering/apex/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.tethering",
- "version": 300000000
+ "version": 309999900
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1a7f0d1ce37e..f76dd3ff6b35 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -73,6 +73,7 @@ java_library_static {
":vold_aidl",
":platform-compat-config",
":display-device-config",
+ ":cec-config",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
@@ -116,6 +117,7 @@ java_library_static {
"android.hardware.contexthub-V1.0-java",
"android.hardware.rebootescrow-java",
"android.hardware.soundtrigger-V2.3-java",
+ "android.hardware.power.stats-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
"dnsresolver_aidl_interface-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 43c54b4a97b3..031ad4286225 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -60,6 +60,27 @@ import java.util.function.Consumer;
* @hide Only for use within the system server.
*/
public abstract class PackageManagerInternal {
+ @IntDef(prefix = "PACKAGE_", value = {
+ PACKAGE_SYSTEM,
+ PACKAGE_SETUP_WIZARD,
+ PACKAGE_INSTALLER,
+ PACKAGE_VERIFIER,
+ PACKAGE_BROWSER,
+ PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+ PACKAGE_PERMISSION_CONTROLLER,
+ PACKAGE_WELLBEING,
+ PACKAGE_DOCUMENTER,
+ PACKAGE_CONFIGURATOR,
+ PACKAGE_INCIDENT_REPORT_APPROVER,
+ PACKAGE_APP_PREDICTOR,
+ PACKAGE_OVERLAY_CONFIG_SIGNATURE,
+ PACKAGE_WIFI,
+ PACKAGE_COMPANION,
+ PACKAGE_RETAIL_DEMO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KnownPackage {}
+
public static final int PACKAGE_SYSTEM = 0;
public static final int PACKAGE_SETUP_WIZARD = 1;
public static final int PACKAGE_INSTALLER = 2;
@@ -72,11 +93,13 @@ public abstract class PackageManagerInternal {
public static final int PACKAGE_CONFIGURATOR = 9;
public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
public static final int PACKAGE_APP_PREDICTOR = 11;
+ public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 12;
public static final int PACKAGE_WIFI = 13;
public static final int PACKAGE_COMPANION = 14;
public static final int PACKAGE_RETAIL_DEMO = 15;
- public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 16;
- public static final int LAST_KNOWN_PACKAGE = PACKAGE_OVERLAY_CONFIG_SIGNATURE;
+ // Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
+ // Please note the numbers should be continuous.
+ public static final int LAST_KNOWN_PACKAGE = PACKAGE_RETAIL_DEMO;
@IntDef(flag = true, prefix = "RESOLVE_", value = {
RESOLVE_NON_BROWSER_ONLY,
@@ -118,26 +141,6 @@ public abstract class PackageManagerInternal {
*/
public static final int INTEGRITY_VERIFICATION_REJECT = 0;
- @IntDef(value = {
- PACKAGE_SYSTEM,
- PACKAGE_SETUP_WIZARD,
- PACKAGE_INSTALLER,
- PACKAGE_VERIFIER,
- PACKAGE_BROWSER,
- PACKAGE_SYSTEM_TEXT_CLASSIFIER,
- PACKAGE_PERMISSION_CONTROLLER,
- PACKAGE_WELLBEING,
- PACKAGE_DOCUMENTER,
- PACKAGE_CONFIGURATOR,
- PACKAGE_INCIDENT_REPORT_APPROVER,
- PACKAGE_APP_PREDICTOR,
- PACKAGE_WIFI,
- PACKAGE_COMPANION,
- PACKAGE_RETAIL_DEMO,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface KnownPackage {}
-
/** Observer called whenever the list of packages changes */
public interface PackageListObserver {
/** A package was added to the system. */
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0cd6e08101da..afea9760078a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -929,13 +929,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * @see ServiceManager#checkService(String)
- */
- public boolean hasService(@NonNull String name) {
- return ServiceManager.checkService(name) != null;
- }
-
- /**
* @see IpConnectivityMetrics.Logger
*/
public IpConnectivityMetrics.Logger getMetricsLogger() {
@@ -1078,7 +1071,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Do the same for Ethernet, since it's often not specified in the configs, although many
// devices can use it via USB host adapters.
- if (mNetConfigs[TYPE_ETHERNET] == null && mDeps.hasService(Context.ETHERNET_SERVICE)) {
+ if (mNetConfigs[TYPE_ETHERNET] == null
+ && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
mNetworksDefined++;
}
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 4a1820a8e538..78bd4cdd4eab 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -824,7 +824,7 @@ public class NsdService extends INsdManager.Stub {
@Override
public String toString() {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append("mChannel ").append(mChannel).append("\n");
sb.append("mMessenger ").append(mMessenger).append("\n");
sb.append("mResolvedService ").append(mResolvedService).append("\n");
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index 2f35da79420e..356cd0cd937b 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -16,12 +16,15 @@
package com.android.server;
+import android.annotation.Nullable;
import android.content.Context;
+import android.os.CombinedVibrationEffect;
import android.os.IBinder;
import android.os.IVibratorManagerService;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.VibrationAttributes;
import com.android.internal.annotations.VisibleForTesting;
@@ -66,6 +69,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
}
+ @Override // Binder call
+ public void vibrate(int uid, String opPkg, CombinedVibrationEffect effect,
+ @Nullable VibrationAttributes attrs, String reason, IBinder token) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override // Binder call
+ public void cancelVibrate(IBinder token) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index 8e5c73bfc022..7fa93c045ce0 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -319,7 +319,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
}
private String switchCodeToString(int switchValues, int switchMask) {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
if ((switchMask & SW_HEADPHONE_INSERT_BIT) != 0 &&
(switchValues & SW_HEADPHONE_INSERT_BIT) != 0) {
sb.append("SW_HEADPHONE_INSERT ");
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 31712becec05..6e5c0412985c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1940,7 +1940,9 @@ public final class ActiveServices {
ActivityServiceConnectionsHolder<ConnectionRecord> activity = null;
if (token != null) {
activity = mAm.mAtmInternal.getServiceConnectionsHolder(token);
- if (activity == null) {
+ // TODO(b/171280916): Remove the check after we have another API get window context
+ // token than getActivityToken.
+ if (activity == null && !mAm.mWindowManager.isWindowToken(token)) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 57f811215e50..f1c591522e51 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.text.TextUtils.formatSimple;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -1892,7 +1893,7 @@ public final class BroadcastQueue {
}
private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
- return String.format("Broadcast %s from %s (%s) %s",
+ return formatSimple("Broadcast %s from %s (%s) %s",
state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
record.callerPackage == null ? "" : record.callerPackage,
record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 8112bb854b71..cd0d5b47c238 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -777,27 +777,27 @@ public final class CachedAppOptimizer {
long freezeTime = app.freezeUnfreezeTime;
try {
+ freezeBinder(app.pid, false);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
+ + ". Killing it");
+ app.kill("Unable to unfreeze",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ return;
+ }
+
+ try {
Process.setProcessFrozen(app.pid, app.uid, false);
app.freezeUnfreezeTime = SystemClock.uptimeMillis();
app.frozen = false;
} catch (Exception e) {
Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName
- + ". Any related user experience might be hanged.");
+ + ". This might cause inconsistency or UI hangs.");
}
if (!app.frozen) {
- try {
- freezeBinder(app.pid, false);
- } catch (RuntimeException e) {
- Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
- + ". Killing it");
- app.kill("Unable to unfreeze",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
- return;
- }
-
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
}
@@ -1110,14 +1110,6 @@ public final class CachedAppOptimizer {
return;
}
- try {
- freezeBinder(pid, true);
- } catch (RuntimeException e) {
- // TODO: it might be preferable to kill the target pid in this case
- Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
- return;
- }
-
if (pid == 0 || proc.frozen) {
// Already frozen or not a real process, either one being
// launched or one being killed
@@ -1146,6 +1138,15 @@ public final class CachedAppOptimizer {
EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
+ try {
+ freezeBinder(pid, true);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+ proc.kill("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ }
+
// See above for why we're not taking mPhenotypeFlagLock here
if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index df8bff285592..be49ce4b02ce 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -265,4 +265,13 @@ public final class ContentProviderConnection extends Binder {
return mUnstableCount;
}
}
+
+ /**
+ * Returns the total number of stable and unstable references.
+ */
+ int totalRefCount() {
+ synchronized (mLock) {
+ return mStableCount + mUnstableCount;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 950f0a0f56a3..34256a06ed71 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -62,6 +62,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.RescueParty;
@@ -261,7 +262,8 @@ public class ContentProviderHelper {
// doesn't kill our process.
Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
- boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+ boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
+ false, false);
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we will be killed during cleaning up, bail.
@@ -697,10 +699,7 @@ public class ContentProviderHelper {
if (conn == null) {
throw new NullPointerException("connection is null");
}
- if (decProviderCountLocked(conn, null, null, stable)) {
- mService.updateOomAdjLocked(conn.provider.proc,
- OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
- }
+ decProviderCountLocked(conn, null, null, stable, true, true);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1275,30 +1274,56 @@ public class ContentProviderHelper {
@GuardedBy("mService")
private boolean decProviderCountLocked(ContentProviderConnection conn,
- ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+ ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable,
+ boolean enforceDelay, boolean updateOomAdj) {
if (conn == null) {
cpr.removeExternalProcessHandleLocked(externalProcessToken);
return false;
}
- if (conn.decrementCount(stable) != 0) {
+
+ if (conn.totalRefCount() > 1) {
+ conn.decrementCount(stable);
return false;
}
+ if (enforceDelay) {
+ // delay the removal of the provider for 5 seconds - this optimizes for those cases
+ // where providers are released and then quickly re-acquired, causing lots of churn.
+ BackgroundThread.getHandler().postDelayed(() -> {
+ handleProviderRemoval(conn, stable, updateOomAdj);
+ }, 5 * 1000);
+ } else {
+ handleProviderRemoval(conn, stable, updateOomAdj);
+ }
+ return true;
+ }
- cpr = conn.provider;
- conn.stopAssociation();
- cpr.connections.remove(conn);
- conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- // The client is more important than last activity -- note the time this
- // is happening, so we keep the old provider process around a bit as last
- // activity to avoid thrashing it.
- if (cpr.proc != null) {
- cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ private void handleProviderRemoval(ContentProviderConnection conn, boolean stable,
+ boolean updateOomAdj) {
+ synchronized (mService) {
+ // if the proc was already killed or this is not the last reference, simply exit.
+ if (conn == null || conn.provider == null || conn.decrementCount(stable) != 0) {
+ return;
+ }
+
+ final ContentProviderRecord cpr = conn.provider;
+ conn.stopAssociation();
+ cpr.connections.remove(conn);
+ conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
+ mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ if (updateOomAdj) {
+ mService.updateOomAdjLocked(conn.provider.proc,
+ OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
}
}
- mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
- cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- return true;
}
/**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 662d8d57775e..cfc58a57c35f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1078,7 +1078,7 @@ public class AudioService extends IAudioService.Stub
// Restore call state
synchronized (mDeviceBroker.mSetModeLock) {
- if (AudioSystem.setPhoneState(mMode, getModeOwnerUid())
+ if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid())
== AudioSystem.AUDIO_STATUS_OK) {
mModeLogger.log(new AudioEventLogger.StringEvent(
"onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
@@ -1165,7 +1165,7 @@ public class AudioService extends IAudioService.Stub
HashMap<Integer, Integer> allowedCapturePolicies =
mPlaybackMonitor.getAllAllowedCapturePolicies();
for (HashMap.Entry<Integer, Integer> entry : allowedCapturePolicies.entrySet()) {
- int result = AudioSystem.setAllowedCapturePolicy(
+ int result = mAudioSystem.setAllowedCapturePolicy(
entry.getKey(),
AudioAttributes.capturePolicyToFlags(entry.getValue(), 0x0));
if (result != AudioSystem.AUDIO_STATUS_OK) {
@@ -2112,7 +2112,7 @@ public class AudioService extends IAudioService.Stub
protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt(
@NonNull AudioAttributes attributes) {
Objects.requireNonNull(attributes);
- return AudioSystem.getDevicesForAttributes(attributes);
+ return mAudioSystem.getDevicesForAttributes(attributes);
}
/** Indicates no special treatment in the handling of the volume adjustement */
@@ -2219,7 +2219,7 @@ public class AudioService extends IAudioService.Stub
|| maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
} else {
- activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
+ activeForReal = mAudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
if (activeForReal || mVolumeControlStream == -1) {
streamType = maybeActiveStreamType;
@@ -2916,7 +2916,7 @@ public class AudioService extends IAudioService.Stub
int streamType = getHearingAidStreamType(newMode);
final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet(
- AudioSystem.getDevicesForStream(streamType));
+ mAudioSystem.getDevicesForStream(streamType));
final Set<Integer> absVolumeMultiModeCaseDevices = AudioSystem.intersectionAudioDeviceTypes(
mAbsVolumeMultiModeCaseDevices, deviceTypes);
if (absVolumeMultiModeCaseDevices.isEmpty()) {
@@ -4123,7 +4123,7 @@ public class AudioService extends IAudioService.Stub
if (actualMode != mMode) {
final long identity = Binder.clearCallingIdentity();
- status = AudioSystem.setPhoneState(actualMode, getModeOwnerUid());
+ status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid());
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.AUDIO_STATUS_OK) {
if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
@@ -4597,7 +4597,7 @@ public class AudioService extends IAudioService.Stub
caller,
MUSIC_ACTIVE_POLL_PERIOD_MS);
int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
- if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
+ if (mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
&& (index > safeMediaVolumeIndex(device))) {
// Approximate cumulative active music time
mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
@@ -4981,8 +4981,8 @@ public class AudioService extends IAudioService.Stub
* in the last "delay_ms" ms.
*/
private boolean wasStreamActiveRecently(int stream, int delay_ms) {
- return AudioSystem.isStreamActive(stream, delay_ms)
- || AudioSystem.isStreamActiveRemotely(stream, delay_ms);
+ return mAudioSystem.isStreamActive(stream, delay_ms)
+ || mAudioSystem.isStreamActiveRemotely(stream, delay_ms);
}
private int getActiveStreamType(int suggestedStreamType) {
@@ -4994,7 +4994,7 @@ public class AudioService extends IAudioService.Stub
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
if (isInCommunication()) {
- if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+ if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
== AudioSystem.FORCE_BT_SCO) {
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
return AudioSystem.STREAM_BLUETOOTH_SCO;
@@ -5031,7 +5031,7 @@ public class AudioService extends IAudioService.Stub
}
default:
if (isInCommunication()) {
- if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+ if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
== AudioSystem.FORCE_BT_SCO) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
return AudioSystem.STREAM_BLUETOOTH_SCO;
@@ -5039,30 +5039,30 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
return AudioSystem.STREAM_VOICE_CALL;
}
- } else if (AudioSystem.isStreamActive(
+ } else if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
- } else if (AudioSystem.isStreamActive(
+ } else if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
return AudioSystem.STREAM_RING;
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (AudioSystem.isStreamActive(
+ if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
- } else if (AudioSystem.isStreamActive(
+ }
+ if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
return AudioSystem.STREAM_RING;
- } else {
- if (DEBUG_VOL) {
- Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
- + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
- }
- return DEFAULT_VOL_STREAM_NO_PLAYBACK;
}
+ if (DEBUG_VOL) {
+ Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
+ + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
+ }
+ return DEFAULT_VOL_STREAM_NO_PLAYBACK;
}
break;
}
@@ -5477,7 +5477,7 @@ public class AudioService extends IAudioService.Stub
&& DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.contains(newDevice)
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
- && (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
+ && (newDevice & mAudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
if (DEBUG_VOL) {
Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
@@ -5945,7 +5945,7 @@ public class AudioService extends IAudioService.Stub
if (!mSystemServer.isPrivileged()) {
return AudioSystem.DEVICE_NONE;
}
- final int devices = AudioSystem.getDevicesForStream(mStreamType);
+ final int devices = mAudioSystem.getDevicesForStream(mStreamType);
if (devices == mObservedDevices) {
return devices;
}
@@ -6670,7 +6670,7 @@ public class AudioService extends IAudioService.Stub
.record();
sForceUseLogger.log(
new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource));
- AudioSystem.setForceUse(useCase, config);
+ mAudioSystem.setForceUse(useCase, config);
}
break;
@@ -7993,7 +7993,7 @@ public class AudioService extends IAudioService.Stub
}
}
- public static class VolumeController {
+ public class VolumeController {
private static final String TAG = "VolumeController";
private IVolumeController mController;
@@ -8033,7 +8033,7 @@ public class AudioService extends IAudioService.Stub
if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) {
// never suppress media vol adjustement during media playback
if (resolvedStream == AudioSystem.STREAM_MUSIC
- && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, mLongPressTimeout))
+ && mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, mLongPressTimeout))
{
// media is playing, adjust the volume right away
return false;
@@ -8071,7 +8071,7 @@ public class AudioService extends IAudioService.Stub
return binder(mController);
}
- private static IBinder binder(IVolumeController controller) {
+ private IBinder binder(IVolumeController controller) {
return controller == null ? null : controller.asBinder();
}
@@ -8800,7 +8800,7 @@ public class AudioService extends IAudioService.Stub
int flags = AudioAttributes.capturePolicyToFlags(capturePolicy, 0x0);
final long identity = Binder.clearCallingIdentity();
synchronized (mPlaybackMonitor) {
- int result = AudioSystem.setAllowedCapturePolicy(callingUid, flags);
+ int result = mAudioSystem.setAllowedCapturePolicy(callingUid, flags);
if (result == AudioSystem.AUDIO_STATUS_OK) {
mPlaybackMonitor.setAllowedCapturePolicy(callingUid, capturePolicy);
}
@@ -8943,7 +8943,7 @@ public class AudioService extends IAudioService.Stub
}
}
final long identity = Binder.clearCallingIdentity();
- AudioSystem.registerPolicyMixes(mMixes, false);
+ mAudioSystem.registerPolicyMixes(mMixes, false);
Binder.restoreCallingIdentity(identity);
synchronized (mAudioPolicies) {
mAudioPolicies.remove(mPolicyCallback.asBinder());
@@ -8986,24 +8986,24 @@ public class AudioService extends IAudioService.Stub
int addMixes(@NonNull ArrayList<AudioMix> mixes) {
// TODO optimize to not have to unregister the mixes already in place
synchronized (mMixes) {
- AudioSystem.registerPolicyMixes(mMixes, false);
+ mAudioSystem.registerPolicyMixes(mMixes, false);
this.add(mixes);
- return AudioSystem.registerPolicyMixes(mMixes, true);
+ return mAudioSystem.registerPolicyMixes(mMixes, true);
}
}
int removeMixes(@NonNull ArrayList<AudioMix> mixes) {
// TODO optimize to not have to unregister the mixes already in place
synchronized (mMixes) {
- AudioSystem.registerPolicyMixes(mMixes, false);
+ mAudioSystem.registerPolicyMixes(mMixes, false);
this.remove(mixes);
- return AudioSystem.registerPolicyMixes(mMixes, true);
+ return mAudioSystem.registerPolicyMixes(mMixes, true);
}
}
@AudioSystem.AudioSystemError int connectMixes() {
final long identity = Binder.clearCallingIdentity();
- int status = AudioSystem.registerPolicyMixes(mMixes, true);
+ int status = mAudioSystem.registerPolicyMixes(mMixes, true);
Binder.restoreCallingIdentity(identity);
return status;
}
@@ -9039,7 +9039,7 @@ public class AudioService extends IAudioService.Stub
@AudioSystem.AudioSystemError private int removeUidDeviceAffinitiesFromSystem(int uid) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.removeUidDeviceAffinities(uid);
+ return mAudioSystem.removeUidDeviceAffinities(uid);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -9049,7 +9049,7 @@ public class AudioService extends IAudioService.Stub
AudioDeviceArray deviceArray) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
+ return mAudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
deviceArray.mDeviceAddresses);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -9091,7 +9091,7 @@ public class AudioService extends IAudioService.Stub
@UserIdInt int userId) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.removeUserIdDeviceAffinities(userId);
+ return mAudioSystem.removeUserIdDeviceAffinities(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -9101,7 +9101,7 @@ public class AudioService extends IAudioService.Stub
@UserIdInt int userId, AudioDeviceArray deviceArray) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
+ return mAudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
deviceArray.mDeviceAddresses);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index ae64990fd8d0..891c713d30fb 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,9 +17,12 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
+import android.media.audiopolicy.AudioMix;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -40,6 +43,25 @@ public class AudioSystemAdapter {
}
/**
+ * Same as {@link AudioSystem#getDevicesForStream(int)}
+ * @param stream a valid stream type
+ * @return a mask of device types
+ */
+ public int getDevicesForStream(int stream) {
+ return AudioSystem.getDevicesForStream(stream);
+ }
+
+ /**
+ * Same as {@link AudioSystem#getDevicesForAttributes(AudioAttributes)}
+ * @param attributes the attributes for which the routing is queried
+ * @return the devices that the stream with the given attributes would be routed to
+ */
+ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
+ @NonNull AudioAttributes attributes) {
+ return AudioSystem.getDevicesForAttributes(attributes);
+ }
+
+ /**
* Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)}
* @param device
* @param state
@@ -178,4 +200,104 @@ public class AudioSystemAdapter {
public boolean isStreamActive(int stream, int inPastMs) {
return AudioSystem.isStreamActive(stream, inPastMs);
}
+
+ /**
+ * Same as {@link AudioSystem#isStreamActiveRemotely(int, int)}
+ * @param stream
+ * @param inPastMs
+ * @return
+ */
+ public boolean isStreamActiveRemotely(int stream, int inPastMs) {
+ return AudioSystem.isStreamActiveRemotely(stream, inPastMs);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setPhoneState(int, int)}
+ * @param state
+ * @param uid
+ * @return
+ */
+ public int setPhoneState(int state, int uid) {
+ return AudioSystem.setPhoneState(state, uid);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setAllowedCapturePolicy(int, int)}
+ * @param uid
+ * @param flags
+ * @return
+ */
+ public int setAllowedCapturePolicy(int uid, int flags) {
+ return AudioSystem.setAllowedCapturePolicy(uid, flags);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setForceUse(int, int)}
+ * @param usage
+ * @param config
+ * @return
+ */
+ public int setForceUse(int usage, int config) {
+ return AudioSystem.setForceUse(usage, config);
+ }
+
+ /**
+ * Same as {@link AudioSystem#getForceUse(int)}
+ * @param usage
+ * @return
+ */
+ public int getForceUse(int usage) {
+ return AudioSystem.getForceUse(usage);
+ }
+
+ /**
+ * Same as {@link AudioSystem#registerPolicyMixes(ArrayList, boolean)}
+ * @param mixes
+ * @param register
+ * @return
+ */
+ public int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register) {
+ return AudioSystem.registerPolicyMixes(mixes, register);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setUidDeviceAffinities(int, int[], String[])}
+ * @param uid
+ * @param types
+ * @param addresses
+ * @return
+ */
+ public int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+ return AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+ }
+
+ /**
+ * Same as {@link AudioSystem#removeUidDeviceAffinities(int)}
+ * @param uid
+ * @return
+ */
+ public int removeUidDeviceAffinities(int uid) {
+ return AudioSystem.removeUidDeviceAffinities(uid);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setUserIdDeviceAffinities(int, int[], String[])}
+ * @param userId
+ * @param types
+ * @param addresses
+ * @return
+ */
+ public int setUserIdDeviceAffinities(int userId, @NonNull int[] types,
+ @NonNull String[] addresses) {
+ return AudioSystem.setUserIdDeviceAffinities(userId, types, addresses);
+ }
+
+ /**
+ * Same as {@link AudioSystem#removeUserIdDeviceAffinities(int)}
+ * @param userId
+ * @return
+ */
+ public int removeUserIdDeviceAffinities(int userId) {
+ return AudioSystem.removeUserIdDeviceAffinities(userId);
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 0304cdc47515..15f43a0481bd 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -19,12 +19,12 @@ package com.android.server.connectivity;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
@@ -36,6 +36,9 @@ import android.util.Log;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
public class DataConnectionStats extends BroadcastReceiver {
private static final String TAG = "DataConnectionStats";
private static final boolean DEBUG = false;
@@ -55,7 +58,8 @@ public class DataConnectionStats extends BroadcastReceiver {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
mListenerHandler = listenerHandler;
- mPhoneStateListener = new PhoneStateListenerImpl(listenerHandler.getLooper());
+ mPhoneStateListener =
+ new PhoneStateListenerImpl(new PhoneStateListenerExecutor(listenerHandler));
}
public void startMonitoring() {
@@ -140,9 +144,24 @@ public class DataConnectionStats extends BroadcastReceiver {
&& mServiceState.getState() != ServiceState.STATE_POWER_OFF;
}
+ private static class PhoneStateListenerExecutor implements Executor {
+ @NonNull
+ private final Handler mHandler;
+
+ PhoneStateListenerExecutor(@NonNull Handler handler) {
+ mHandler = handler;
+ }
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
+ }
+ }
+
private class PhoneStateListenerImpl extends PhoneStateListener {
- PhoneStateListenerImpl(Looper looper) {
- super(looper);
+ PhoneStateListenerImpl(Executor executor) {
+ super(executor);
}
@Override
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 93cada7adca3..eb61a1c2ad40 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1051,7 +1051,7 @@ class AutomaticBrightnessController {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append('[');
for (int i = 0; i < mCount; i++) {
final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/display/utils/History.java b/services/core/java/com/android/server/display/utils/History.java
index ed171b8f7408..988d573f276d 100644
--- a/services/core/java/com/android/server/display/utils/History.java
+++ b/services/core/java/com/android/server/display/utils/History.java
@@ -83,7 +83,7 @@ public class History {
* @return The buffer as string.
*/
public String toString() {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < mCount; i++) {
final int index = (mStart + i) % mSize;
diff --git a/services/core/java/com/android/server/display/utils/RollingBuffer.java b/services/core/java/com/android/server/display/utils/RollingBuffer.java
index dd5b7ab2403d..883f6eb4fb7d 100644
--- a/services/core/java/com/android/server/display/utils/RollingBuffer.java
+++ b/services/core/java/com/android/server/display/utils/RollingBuffer.java
@@ -136,7 +136,7 @@ public class RollingBuffer {
* @return The buffer as string.
*/
public String toString() {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < mCount; i++) {
final int index = offsetOf(i);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
new file mode 100644
index 000000000000..2b4d51522e41
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2020 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.hdmi;
+
+import static android.hardware.hdmi.HdmiControlManager.CecSettingName;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.provider.Settings.Global;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.Setting;
+import com.android.server.hdmi.cec.config.Value;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+/**
+ * The {@link HdmiCecConfig} class is used for getting information about
+ * available HDMI CEC settings.
+ */
+public class HdmiCecConfig {
+ private static final String TAG = "HdmiCecConfig";
+
+ private static final String ETC_DIR = "etc";
+ private static final String CONFIG_FILE = "cec_config.xml";
+
+ @Nullable private CecSettings mProductConfig = null;
+ @Nullable private CecSettings mVendorOverride = null;
+ @Nullable private StorageAdapter mStorageAdapter = null;
+
+ @IntDef({
+ STORAGE_SYSPROPS,
+ STORAGE_GLOBAL_SETTINGS,
+ })
+ private @interface Storage {}
+
+ private static final int STORAGE_SYSPROPS = 0;
+ private static final int STORAGE_GLOBAL_SETTINGS = 1;
+
+ /**
+ * System property key for Power State Change on Active Source Lost.
+ */
+ public static final String SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+ "ro.hdmi.cec.source.power_state_change_on_active_source_lost";
+
+ /**
+ * System property key for Audio Mode Muting.
+ */
+ public static final String SYSPROP_SYSTEM_AUDIO_MODE_MUTING =
+ "ro.hdmi.cec.audio.system_audio_mode_muting.enabled";
+
+ /**
+ * Setting storage input/output helper class.
+ */
+ public static class StorageAdapter {
+ /**
+ * Read the value from a system property.
+ * Returns the given default value if the system property is not set.
+ */
+ public String retrieveSystemProperty(@NonNull String storageKey,
+ @NonNull String defaultValue) {
+ return SystemProperties.get(storageKey, defaultValue);
+ }
+
+ /**
+ * Write the value to a system property.
+ */
+ public void storeSystemProperty(@NonNull String storageKey,
+ @NonNull String value) {
+ SystemProperties.set(storageKey, value);
+ }
+
+ /**
+ * Read the value from a global setting.
+ * Returns the given default value if the system property is not set.
+ */
+ public String retrieveGlobalSetting(@NonNull Context context,
+ @NonNull String storageKey,
+ @NonNull String defaultValue) {
+ String value = Global.getString(context.getContentResolver(), storageKey);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * Write the value to a global setting.
+ */
+ public void storeGlobalSetting(@NonNull Context context,
+ @NonNull String storageKey,
+ @NonNull String value) {
+ Global.putString(context.getContentResolver(), storageKey, value);
+ }
+ }
+
+ @VisibleForTesting
+ HdmiCecConfig(@Nullable CecSettings productConfig,
+ @Nullable CecSettings vendorOverride,
+ @Nullable StorageAdapter storageAdapter) {
+ mProductConfig = productConfig;
+ mVendorOverride = vendorOverride;
+ mStorageAdapter = storageAdapter;
+ }
+
+ HdmiCecConfig() {
+ this(readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(),
+ ETC_DIR, CONFIG_FILE)),
+ readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
+ ETC_DIR, CONFIG_FILE)),
+ new StorageAdapter());
+ }
+
+ @Nullable
+ private static CecSettings readSettingsFromFile(@NonNull File file) {
+ if (!file.exists()) {
+ return null;
+ }
+ if (!file.isFile()) {
+ Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping.");
+ return null;
+ }
+ try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
+ return XmlParser.read(in);
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e);
+ }
+ return null;
+ }
+
+ @Nullable
+ private Setting getSetting(@NonNull String name) {
+ if (mVendorOverride != null) {
+ // First read from the vendor override.
+ for (Setting setting : mVendorOverride.getSetting()) {
+ if (setting.getName().equals(name)) {
+ return setting;
+ }
+ }
+ }
+ // If not found, try the product config.
+ for (Setting setting : mProductConfig.getSetting()) {
+ if (setting.getName().equals(name)) {
+ return setting;
+ }
+ }
+ return null;
+ }
+
+ @Storage
+ private int getStorage(@NonNull Setting setting) {
+ switch (setting.getName()) {
+ case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
+ return STORAGE_GLOBAL_SETTINGS;
+ case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+ return STORAGE_GLOBAL_SETTINGS;
+ case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+ return STORAGE_SYSPROPS;
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+ return STORAGE_SYSPROPS;
+ default:
+ throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+ + "' storage.");
+ }
+ }
+
+ private String getStorageKey(@NonNull Setting setting) {
+ switch (setting.getName()) {
+ case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
+ return Global.HDMI_CONTROL_ENABLED;
+ case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+ return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
+ case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+ return SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST;
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+ return SYSPROP_SYSTEM_AUDIO_MODE_MUTING;
+ default:
+ throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+ + "' storage key.");
+ }
+ }
+
+ private String retrieveValue(@NonNull Context context, @NonNull Setting setting) {
+ @Storage int storage = getStorage(setting);
+ String storageKey = getStorageKey(setting);
+ if (storage == STORAGE_SYSPROPS) {
+ Slog.d(TAG, "Reading '" + storageKey + "' sysprop.");
+ return mStorageAdapter.retrieveSystemProperty(storageKey,
+ setting.getDefaultValue().getStringValue());
+ } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+ Slog.d(TAG, "Reading '" + storageKey + "' global setting.");
+ return mStorageAdapter.retrieveGlobalSetting(context, storageKey,
+ setting.getDefaultValue().getStringValue());
+ }
+ return null;
+ }
+
+ private void storeValue(@NonNull Context context, @NonNull Setting setting,
+ @NonNull String value) {
+ @Storage int storage = getStorage(setting);
+ String storageKey = getStorageKey(setting);
+ if (storage == STORAGE_SYSPROPS) {
+ Slog.d(TAG, "Setting '" + storageKey + "' sysprop.");
+ mStorageAdapter.storeSystemProperty(storageKey, value);
+ } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+ Slog.d(TAG, "Setting '" + storageKey + "' global setting.");
+ mStorageAdapter.storeGlobalSetting(context, storageKey, value);
+ }
+ }
+
+ /**
+ * Returns a list of all settings based on the XML metadata.
+ */
+ public @CecSettingName List<String> getAllSettings() {
+ List<String> allSettings = new ArrayList<String>();
+ for (Setting setting : mProductConfig.getSetting()) {
+ allSettings.add(setting.getName());
+ }
+ return allSettings;
+ }
+
+ /**
+ * Returns a list of user-modifiable settings based on the XML metadata.
+ */
+ public @CecSettingName List<String> getUserSettings() {
+ Set<String> userSettings = new HashSet<String>();
+ // First read from the product config.
+ for (Setting setting : mProductConfig.getSetting()) {
+ if (setting.getUserConfigurable()) {
+ userSettings.add(setting.getName());
+ }
+ }
+ if (mVendorOverride != null) {
+ // Next either add or remove based on the vendor override.
+ for (Setting setting : mVendorOverride.getSetting()) {
+ if (setting.getUserConfigurable()) {
+ userSettings.add(setting.getName());
+ } else {
+ userSettings.remove(setting.getName());
+ }
+ }
+ }
+ return new ArrayList(userSettings);
+ }
+
+ /**
+ * For a given setting name returns values that are allowed for that setting.
+ */
+ public List<String> getAllowedValues(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ List<String> allowedValues = new ArrayList<String>();
+ for (Value allowedValue : setting.getAllowedValues().getValue()) {
+ allowedValues.add(allowedValue.getStringValue());
+ }
+ return allowedValues;
+ }
+
+ /**
+ * For a given setting name returns the default value for that setting.
+ */
+ public String getDefaultValue(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ return getSetting(name).getDefaultValue().getStringValue();
+ }
+
+ /**
+ * For a given setting name returns the current value of that setting.
+ */
+ public String getValue(@NonNull Context context, @NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+ return retrieveValue(context, setting);
+ }
+
+ /**
+ * For a given setting name and value sets the current value of that setting.
+ */
+ public void setValue(@NonNull Context context, @NonNull @CecSettingName String name,
+ @NonNull String value) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ if (!setting.getUserConfigurable()) {
+ throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
+ }
+ if (!getAllowedValues(name).contains(value)) {
+ throw new IllegalArgumentException("Invalid CEC setting '" + name
+ + "' value: '" + value + "'.");
+ }
+ Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
+ storeValue(context, setting, value);
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index b88a37e7b8b4..946fb0d00d60 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -127,7 +127,7 @@ abstract class HdmiCecLocalDevice {
@Override
public String toString() {
- StringBuffer s = new StringBuffer();
+ StringBuilder s = new StringBuilder();
String logicalAddressString =
(logicalAddress == Constants.ADDR_INVALID)
? "invalid"
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index ff7da11340eb..7a6ce8de8c24 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -111,7 +111,7 @@ public final class HdmiCecMessage {
@Override
public String toString() {
- StringBuffer s = new StringBuffer();
+ StringBuilder s = new StringBuilder();
s.append(String.format("<%s> %X%X:%02X",
opcodeToString(mOpcode), mSource, mDestination, mOpcode));
if (mParams.length > 0) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 27bd0563cea5..0034df3b373a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -95,10 +95,12 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -187,6 +189,9 @@ public class HdmiControlService extends SystemService {
@GuardedBy("mLock")
private boolean mHdmiCecVolumeControlEnabled;
+ // Make sure HdmiCecConfig is instantiated and the XMLs are read.
+ private final HdmiCecConfig mHdmiCecConfig = new HdmiCecConfig();
+
/**
* Interface to report send result.
*/
@@ -2316,6 +2321,19 @@ public class HdmiControlService extends SystemService {
pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
pw.decreaseIndent();
+ // CEC settings
+ pw.println("CEC settings:");
+ pw.increaseIndent();
+ HdmiCecConfig hdmiCecConfig = HdmiControlService.this.getHdmiCecConfig();
+ List<String> allSettings = hdmiCecConfig.getAllSettings();
+ Set<String> userSettings = new HashSet<>(hdmiCecConfig.getUserSettings());
+ for (String setting : allSettings) {
+ pw.println(setting + ": " + hdmiCecConfig.getValue(getContext(), setting)
+ + " (default: " + hdmiCecConfig.getDefaultValue(setting) + ")"
+ + (userSettings.contains(setting) ? " [modifiable]" : ""));
+ }
+ pw.decreaseIndent();
+
pw.println("mMhlController: ");
pw.increaseIndent();
mMhlController.dump(pw);
@@ -2329,6 +2347,50 @@ public class HdmiControlService extends SystemService {
pw.decreaseIndent();
}
}
+
+ @Override
+ public List<String> getUserCecSettings() {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public List<String> getAllowedCecSettingValues(String name) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return HdmiControlService.this.getHdmiCecConfig().getAllowedValues(name);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public String getCecSettingValue(String name) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return HdmiControlService.this.getHdmiCecConfig().getValue(getContext(), name);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setCecSettingValue(String name, String value) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ HdmiControlService.this.getHdmiCecConfig().setValue(getContext(), name, value);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
// Get the source address to send out commands to devices connected to the current device
@@ -3389,4 +3451,8 @@ public class HdmiControlService extends SystemService {
getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
HdmiControlService.PERMISSION);
}
+
+ HdmiCecConfig getHdmiCecConfig() {
+ return mHdmiCecConfig;
+ }
}
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index fd5f48c91867..51e6cf413074 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -215,7 +215,7 @@ public class InputShellCommand extends ShellCommand {
* @param text is a string of characters you want to input to the device.
*/
private void sendText(int source, final String text, int displayId) {
- final StringBuffer buff = new StringBuffer(text);
+ final StringBuilder buff = new StringBuilder(text);
boolean escapeFlag = false;
for (int i = 0; i < buff.length(); i++) {
if (escapeFlag) {
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index fb23e0187ebf..7a2e4ed3cc0b 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -242,6 +242,19 @@ public class ContextHubService extends IContextHubService.Stub {
}
}, UserHandle.USER_ALL);
}
+
+ if (mContextHubWrapper.supportsAirplaneModeSettingNotifications()) {
+ sendAirplaneModeSettingUpdate();
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+ true /* notifyForDescendants */,
+ new ContentObserver(null /* handler */) {
+ @Override
+ public void onChange(boolean selfChange) {
+ sendAirplaneModeSettingUpdate();
+ }
+ }, UserHandle.USER_ALL);
+ }
}
/**
@@ -614,6 +627,7 @@ public class ContextHubService extends IContextHubService.Stub {
if (eventType == AsyncEventType.RESTARTED) {
sendLocationSettingUpdate();
sendWifiSettingUpdate(true /* forceUpdate */);
+ sendAirplaneModeSettingUpdate();
mTransactionManager.onHubReset();
queryNanoAppsInternal(contextHubId);
@@ -958,6 +972,7 @@ public class ContextHubService extends IContextHubService.Stub {
/**
* Obtains the latest WiFi availability setting value and notifies the Context Hub.
+ *
* @param forceUpdate True to force send update to the Context Hub, otherwise only send the
* update when the WiFi availability changes.
*/
@@ -972,6 +987,17 @@ public class ContextHubService extends IContextHubService.Stub {
}
}
+ /**
+ * Obtains the latest airplane mode setting value and notifies the Context Hub.
+ */
+ private void sendAirplaneModeSettingUpdate() {
+ boolean enabled =
+ (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0)
+ == 1);
+ mContextHubWrapper.onAirplaneModeSettingChanged(enabled);
+ }
+
private String getCallingPackageName() {
return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
}
diff --git a/services/core/java/com/android/server/location/IContextHubWrapper.java b/services/core/java/com/android/server/location/IContextHubWrapper.java
index 613964a5ed14..9ac7c6b95583 100644
--- a/services/core/java/com/android/server/location/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/IContextHubWrapper.java
@@ -116,6 +116,19 @@ public abstract class IContextHubWrapper {
*/
public abstract void onWifiSettingChanged(boolean enabled);
+ /**
+ * @return True if this version of the Contexthub HAL supports airplane mode setting
+ * notifications.
+ */
+ public abstract boolean supportsAirplaneModeSettingNotifications();
+
+ /**
+ * Notifies the Contexthub implementation of an airplane mode setting change.
+ *
+ * @param enabled true if the airplane mode setting has been enabled.
+ */
+ public abstract void onAirplaneModeSettingChanged(boolean enabled);
+
private static class ContextHubWrapperV1_0 extends IContextHubWrapper {
private android.hardware.contexthub.V1_0.IContexthub mHub;
@@ -135,11 +148,18 @@ public abstract class IContextHubWrapper {
return false;
}
+ public boolean supportsAirplaneModeSettingNotifications() {
+ return false;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
}
public void onWifiSettingChanged(boolean enabled) {
}
+
+ public void onAirplaneModeSettingChanged(boolean enabled) {
+ }
}
private static class ContextHubWrapperV1_1 extends IContextHubWrapper {
@@ -161,6 +181,10 @@ public abstract class IContextHubWrapper {
return false;
}
+ public boolean supportsAirplaneModeSettingNotifications() {
+ return false;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
try {
mHub.onSettingChanged(Setting.LOCATION,
@@ -172,6 +196,9 @@ public abstract class IContextHubWrapper {
public void onWifiSettingChanged(boolean enabled) {
}
+
+ public void onAirplaneModeSettingChanged(boolean enabled) {
+ }
}
private static class ContextHubWrapperV1_2 extends IContextHubWrapper {
@@ -193,6 +220,10 @@ public abstract class IContextHubWrapper {
return true;
}
+ public boolean supportsAirplaneModeSettingNotifications() {
+ return true;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
sendSettingChanged(Setting.LOCATION,
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
@@ -203,6 +234,11 @@ public abstract class IContextHubWrapper {
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
}
+ public void onAirplaneModeSettingChanged(boolean enabled) {
+ sendSettingChanged(android.hardware.contexthub.V1_2.Setting.AIRPLANE_MODE,
+ enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ }
+
private void sendSettingChanged(byte setting, byte newValue) {
try {
mHub.onSettingChanged_1_2(setting, newValue);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 715e41c62a05..5d0544b33cd7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -17,6 +17,7 @@
package com.android.server.locksettings;
import static android.content.Context.USER_SERVICE;
+import static android.text.TextUtils.formatSimple;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -527,7 +528,7 @@ class LockSettingsStorage {
protected String getSynthenticPasswordStateFilePathForUser(int userId, long handle,
String name) {
final File baseDir = getSyntheticPasswordDirectoryForUser(userId);
- final String baseName = String.format("%016x.%s", handle, name);
+ final String baseName = formatSimple("%016x.%s", handle, name);
return new File(baseDir, baseName).getAbsolutePath();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index f15e22f92cad..f8ff5b5f8e66 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -416,7 +416,7 @@ class MediaSessionStack {
// Code copied from android.os.Debug#getCallers(int)
private static String getCallers(final int depth) {
final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
for (int i = 0; i < depth; i++) {
sb.append(getCaller(callStack, i)).append(" ");
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b145e1eee3a3..b4347e148ce0 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,6 +15,8 @@
*/
package com.android.server.notification;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.NonNull;
import android.app.NotificationManager;
import android.content.Context;
@@ -138,7 +140,7 @@ public class RankingHelper {
boolean isGroupSummary = record.getNotification().isGroupSummary();
record.setGlobalSortKey(
- String.format("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
+ formatSimple("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
record.getCriticality(),
record.isRecentlyIntrusive()
&& record.getImportance() > NotificationManager.IMPORTANCE_MIN
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3f16abfa04b7..c39152be518a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2006 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
@@ -47,8 +46,10 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
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;
@@ -233,6 +234,7 @@ import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
@@ -17617,6 +17619,49 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName,
+ @NonNull ParsedPackage parsedPackage, int scanFlags) {
+ // If the defining package is signed with our cert, it's okay. This
+ // also includes the "updating the same package" case, of course.
+ // "updating same package" could also involve key-rotation.
+
+ final PackageSetting sourcePackageSetting;
+ synchronized (mLock) {
+ sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
+ }
+
+ final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
+ ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
+ final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ if (sourcePackageName.equals(parsedPackage.getPackageName())
+ && (ksms.shouldCheckUpgradeKeySetLocked(
+ sourcePackageSetting, scanFlags))) {
+ return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
+ } else {
+
+ // in the event of signing certificate rotation, we need to see if the
+ // package's certificate has rotated from the current one, or if it is an
+ // older certificate with which the current is ok with sharing permissions
+ if (sourceSigningDetails.checkCapability(
+ parsedPackage.getSigningDetails(),
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ return true;
+ } else if (parsedPackage.getSigningDetails().checkCapability(
+ sourceSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ // the scanned package checks out, has signing certificate rotation
+ // history, and is newer; bring it over
+ synchronized (mLock) {
+ sourcePackageSetting.signatures.mSigningDetails =
+ parsedPackage.getSigningDetails();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
@GuardedBy("mInstallLock")
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
@@ -17775,6 +17820,15 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ /*
+ * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
+ * as this only works for packages that are installed
+ *
+ * TODO: Move logic for permission group compatibility into PermissionManagerService
+ */
+ boolean cannotInstallWithBadPermissionGroups =
+ parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
+
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
@@ -17826,8 +17880,33 @@ public class PackageManagerService extends IPackageManager.Stub
res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
}
+ final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ final ParsedPermissionGroup group =
+ parsedPackage.getPermissionGroups().get(groupNum);
+ final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0);
+
+ if (sourceGroup != null && cannotInstallWithBadPermissionGroups) {
+ final String sourcePackageName = sourceGroup.packageName;
+
+ if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
+ && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to redeclare permission group "
+ + group.getName() + " already owned by "
+ + sourcePackageName);
+ }
+ }
+ }
- int N = ArrayUtils.size(parsedPackage.getPermissions());
+ // TODO: Move logic for checking permission compatibility into PermissionManagerService
+ final int N = ArrayUtils.size(parsedPackage.getPermissions());
for (int i = N - 1; i >= 0; i--) {
final ParsedPermission perm = parsedPackage.getPermissions().get(i);
final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());
@@ -17843,46 +17922,10 @@ public class PackageManagerService extends IPackageManager.Stub
// Check whether the newly-scanned package wants to define an already-defined perm
if (bp != null) {
- // If the defining package is signed with our cert, it's okay. This
- // also includes the "updating the same package" case, of course.
- // "updating same package" could also involve key-rotation.
- final boolean sigsOk;
final String sourcePackageName = bp.getPackageName();
- final PackageSetting sourcePackageSetting;
- synchronized (mLock) {
- sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
- }
- final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
- ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
- if (sourcePackageName.equals(parsedPackage.getPackageName())
- && (ksms.shouldCheckUpgradeKeySetLocked(
- sourcePackageSetting, scanFlags))) {
- sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
- } else {
- // in the event of signing certificate rotation, we need to see if the
- // package's certificate has rotated from the current one, or if it is an
- // older certificate with which the current is ok with sharing permissions
- if (sourceSigningDetails.checkCapability(
- parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
- sigsOk = true;
- } else if (parsedPackage.getSigningDetails().checkCapability(
- sourceSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
- // the scanned package checks out, has signing certificate rotation
- // history, and is newer; bring it over
- synchronized (mLock) {
- sourcePackageSetting.signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
- }
- sigsOk = true;
- } else {
- sigsOk = false;
- }
- }
- if (!sigsOk) {
+ if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
@@ -17917,6 +17960,52 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
}
+
+ if (perm.getGroup() != null && cannotInstallWithBadPermissionGroups) {
+ boolean isPermGroupDefinedByPackage = false;
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ if (parsedPackage.getPermissionGroups().get(groupNum).getName()
+ .equals(perm.getGroup())) {
+ isPermGroupDefinedByPackage = true;
+ break;
+ }
+ }
+
+ if (!isPermGroupDefinedByPackage) {
+ final PermissionGroupInfo sourceGroup =
+ getPermissionGroupInfo(perm.getGroup(), 0);
+
+ if (sourceGroup == null) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in non-existing group "
+ + perm.getGroup());
+ } else {
+ String groupSourcePackageName = sourceGroup.packageName;
+
+ if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)
+ && !doesSignatureMatchForPermissions(groupSourcePackageName,
+ parsedPackage, scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in group "
+ + perm.getGroup() + " owned by package "
+ + groupSourcePackageName
+ + " with incompatible certificate");
+ }
+ }
+ }
+ }
}
}
@@ -19120,7 +19209,7 @@ public class PackageManagerService extends IPackageManager.Stub
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
- extras.putBoolean(Intent.EXTRA_REMOVED_BY_SYSTEM, removedBySystem);
+ extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
if (isUpdate || isRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
@@ -22328,11 +22417,8 @@ public class PackageManagerService extends IPackageManager.Stub
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println("Known Packages:");
ipw.increaseIndent();
- for (int i = 0; i < LAST_KNOWN_PACKAGE; i++) {
+ for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
final String knownPackage = mPmInternal.knownPackageToString(i);
- if ("Unknown".equals(knownPackage)) {
- continue;
- }
ipw.print(knownPackage);
ipw.println(":");
final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index de9d3a09df35..83f6c5285874 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -28,11 +28,13 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.PackageStateUnserialized;
import java.io.File;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -300,7 +302,8 @@ public class PackageSetting extends PackageSettingBase {
return mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
}
- public void dumpDebug(ProtoOutputStream proto, long fieldId, List<UserInfo> users) {
+ public void dumpDebug(ProtoOutputStream proto, long fieldId, List<UserInfo> users,
+ LegacyPermissionDataProvider dataProvider) {
final long packageToken = proto.start(fieldId);
proto.write(PackageProto.NAME, (realName != null ? realName : name));
proto.write(PackageProto.UID, appId);
@@ -337,9 +340,33 @@ public class PackageSetting extends PackageSettingBase {
proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable());
proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading());
writeUsersInfoToProto(proto, PackageProto.USERS);
+ writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider);
proto.end(packageToken);
}
+ /**
+ * TODO (b/170263003) refactor to dump to permissiongr proto
+ * Dumps the permissions that are granted to users for this package.
+ */
+ void writePackageUserPermissionsProto(ProtoOutputStream proto, long fieldId,
+ List<UserInfo> users, LegacyPermissionDataProvider dataProvider) {
+ Collection<LegacyPermissionState.PermissionState> runtimePermissionStates;
+ for (UserInfo user : users) {
+ final long permissionsToken = proto.start(PackageProto.USER_PERMISSIONS);
+ proto.write(PackageProto.UserPermissionsProto.ID, user.id);
+
+ runtimePermissionStates = dataProvider.getLegacyPermissionState(appId)
+ .getRuntimePermissionStates(user.id);
+ for (LegacyPermissionState.PermissionState permission : runtimePermissionStates) {
+ if (permission.isGranted()) {
+ proto.write(PackageProto.UserPermissionsProto.GRANTED_PERMISSIONS,
+ permission.getName());
+ }
+ }
+ proto.end(permissionsToken);
+ }
+ }
+
/** Updates all fields in the current setting from another. */
public void updateFrom(PackageSetting other) {
super.updateFrom(other);
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 6bce78862087..b7d1eec68c7a 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -309,7 +309,7 @@ class PackageSignatures {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer(128);
+ StringBuilder buf = new StringBuilder(128);
buf.append("PackageSignatures{");
buf.append(Integer.toHexString(System.identityHashCode(this)));
buf.append(" version:");
diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java
index ef37a20479d6..ec8977988e3f 100644
--- a/services/core/java/com/android/server/pm/PackageUsage.java
+++ b/services/core/java/com/android/server/pm/PackageUsage.java
@@ -97,7 +97,7 @@ class PackageUsage extends AbstractStatsBase<Map<String, PackageSetting>> {
BufferedInputStream in = null;
try {
in = new BufferedInputStream(file.openRead());
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
String firstLine = readLine(in, sb);
if (firstLine == null) {
@@ -117,7 +117,7 @@ class PackageUsage extends AbstractStatsBase<Map<String, PackageSetting>> {
}
private void readVersion0LP(Map<String, PackageSetting> pkgSettings, InputStream in,
- StringBuffer sb, String firstLine)
+ StringBuilder sb, String firstLine)
throws IOException {
// Initial version of the file had no version number and stored one
// package-timestamp pair per line.
@@ -145,7 +145,7 @@ class PackageUsage extends AbstractStatsBase<Map<String, PackageSetting>> {
}
private void readVersion1LP(Map<String, PackageSetting> pkgSettings, InputStream in,
- StringBuffer sb) throws IOException {
+ StringBuilder sb) throws IOException {
// Version 1 of the file started with the corresponding version
// number and then stored a package name and eight timestamps per line.
String line;
@@ -178,11 +178,11 @@ class PackageUsage extends AbstractStatsBase<Map<String, PackageSetting>> {
}
}
- private String readLine(InputStream in, StringBuffer sb) throws IOException {
+ private String readLine(InputStream in, StringBuilder sb) throws IOException {
return readToken(in, sb, '\n');
}
- private String readToken(InputStream in, StringBuffer sb, char endOfToken)
+ private String readToken(InputStream in, StringBuilder sb, char endOfToken)
throws IOException {
sb.setLength(0);
while (true) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4c8d2b9b0a9e..c7304c2695c9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5018,7 +5018,7 @@ public final class Settings {
final int count = mPackages.size();
for (int i = 0; i < count; i++) {
final PackageSetting ps = mPackages.valueAt(i);
- ps.dumpDebug(proto, PackageServiceDumpProto.PACKAGES, users);
+ ps.dumpDebug(proto, PackageServiceDumpProto.PACKAGES, users, mPermissionDataProvider);
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 96f9982a16a5..e471ac697679 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3907,10 +3907,11 @@ public class ShortcutService extends IShortcutService.Stub {
final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
- return mContext.getPackageManager().getResourcesForApplicationAsUser(
- packageName, userId);
+ return mContext.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+ .getPackageManager().getResourcesForApplication(packageName);
} catch (NameNotFoundException e) {
- Slog.e(TAG, "Resources for package " + packageName + " not found");
+ Slog.e(TAG, "Resources of package " + packageName + " for user " + userId
+ + " not found");
return null;
} finally {
injectRestoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d871325fe829..6c01017fb975 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2988,7 +2988,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (String permission : ps.getGrantedPermissions()) {
if (!pkg.getImplicitPermissions().contains(permission)) {
BasePermission bp = mSettings.getPermissionLocked(permission);
- if (bp.isRuntime()) {
+ if (bp != null && bp.isRuntime()) {
int flags = ps.getPermissionFlags(permission);
if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsData.java b/services/core/java/com/android/server/powerstats/PowerStatsData.java
deleted file mode 100644
index 755bd5fce45e..000000000000
--- a/services/core/java/com/android/server/powerstats/PowerStatsData.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2020 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.powerstats;
-
-import android.util.Log;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-import android.util.proto.WireTypeMismatchException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * PowerStatsData is a class that performs two operations:
- * 1) Unpacks serialized protobuf byte arrays, as defined in powerstatsservice.proto,
- * into RailInfo or EnergyData object arrays.
- *
- * 2) Packs RailInfo or EnergyData object arrays in protobuf byte arrays as
- * defined in powerstatsservice.proto.
- *
- * Inside frameworks, proto source is generated with the genstream option
- * and therefore the getter/setter helper functions are not available.
- * The protos need to be packed/unpacked in a more manual way using
- * ProtoOutputStream/ProtoInputStream.
- */
-public class PowerStatsData {
- private static final String TAG = PowerStatsData.class.getSimpleName();
-
- private List<Data> mDataList;
-
- public PowerStatsData(ProtoInputStream pis) throws IOException {
- mDataList = new ArrayList<Data>();
- unpackProto(pis);
- }
-
- public PowerStatsData(Data[] data) {
- mDataList = new ArrayList<Data>(Arrays.asList(data));
- }
-
- private void unpackProto(ProtoInputStream pis) throws IOException {
- long token;
-
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) PowerStatsServiceProto.RAIL_INFO:
- token = pis.start(PowerStatsServiceProto.RAIL_INFO);
- mDataList.add(new RailInfo(pis));
- pis.end(token);
- break;
-
- case (int) PowerStatsServiceProto.ENERGY_DATA:
- token = pis.start(PowerStatsServiceProto.ENERGY_DATA);
- mDataList.add(new EnergyData(pis));
- pis.end(token);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in proto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in proto: " + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- /**
- * Write this object to an output stream in protobuf format.
- *
- * @param pos ProtoOutputStream of file where data is to be written. Data is
- * written in protobuf format as defined by powerstatsservice.proto.
- */
- public void toProto(ProtoOutputStream pos) {
- long token;
-
- for (Data data : mDataList) {
- if (data instanceof RailInfo) {
- token = pos.start(PowerStatsServiceProto.RAIL_INFO);
- } else {
- token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
- }
- data.toProto(pos);
- pos.end(token);
- }
- }
-
- /**
- * Convert mDataList to proto format and return the serialized byte array.
- *
- * @return byte array containing a serialized protobuf of mDataList.
- */
- public byte[] getProtoBytes() {
- ProtoOutputStream pos = new ProtoOutputStream();
- long token;
-
- for (Data data : mDataList) {
- if (data instanceof RailInfo) {
- token = pos.start(PowerStatsServiceProto.RAIL_INFO);
- } else {
- token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
- }
- data.toProto(pos);
- pos.end(token);
- }
- return pos.getBytes();
- }
-
- /**
- * Print this object to logcat.
- */
- public void print() {
- for (Data data : mDataList) {
- Log.d(TAG, data.toString());
- }
- }
-
- /**
- * RailInfo is a class that stores a description for an individual ODPM
- * rail. It provides functionality to unpack a RailInfo object from a
- * serialized protobuf byte array, and to pack a RailInfo object into
- * a ProtoOutputStream.
- */
- public static class RailInfo extends Data {
- public String mRailName;
- public String mSubSysName;
- public long mSamplingRate;
-
- public RailInfo(ProtoInputStream pis) throws IOException {
- unpackProto(pis);
- }
-
- public RailInfo(long index, String railName, String subSysName, long samplingRate) {
- mIndex = index;
- mRailName = railName;
- mSubSysName = subSysName;
- mSamplingRate = samplingRate;
- }
-
- @Override
- protected void unpackProto(ProtoInputStream pis) throws IOException {
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) RailInfoProto.INDEX:
- mIndex = pis.readInt(RailInfoProto.INDEX);
- break;
-
- case (int) RailInfoProto.RAIL_NAME:
- mRailName = pis.readString(RailInfoProto.RAIL_NAME);
- break;
-
- case (int) RailInfoProto.SUBSYS_NAME:
- mSubSysName = pis.readString(RailInfoProto.SUBSYS_NAME);
- break;
-
- case (int) RailInfoProto.SAMPLING_RATE:
- mSamplingRate = pis.readInt(RailInfoProto.SAMPLING_RATE);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in RailInfoProto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in RailInfoProto: "
- + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- @Override
- public void toProto(ProtoOutputStream pos) {
- pos.write(RailInfoProto.INDEX, mIndex);
- pos.write(RailInfoProto.RAIL_NAME, mRailName);
- pos.write(RailInfoProto.SUBSYS_NAME, mSubSysName);
- pos.write(RailInfoProto.SAMPLING_RATE, mSamplingRate);
- }
-
- @Override
- public String toString() {
- return String.format("Index = " + mIndex
- + ", RailName = " + mRailName
- + ", SubSysName = " + mSubSysName
- + ", SamplingRate = " + mSamplingRate);
- }
- }
-
- /**
- * EnergyData is a class that stores an energy (uWs) data reading for an
- * individual ODPM rail. It provides functionality to unpack an EnergyData
- * object from a serialized protobuf byte array, and to pack an EnergyData
- * object into a ProtoOutputStream.
- */
- public static class EnergyData extends Data {
- public long mTimestampMs;
- public long mEnergyUWs;
-
- public EnergyData(ProtoInputStream pis) throws IOException {
- unpackProto(pis);
- }
-
- public EnergyData(long index, long timestampMs, long energyUWs) {
- mIndex = index;
- mTimestampMs = timestampMs;
- mEnergyUWs = energyUWs;
- }
-
- @Override
- protected void unpackProto(ProtoInputStream pis) throws IOException {
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) EnergyDataProto.INDEX:
- mIndex = pis.readInt(EnergyDataProto.INDEX);
- break;
-
- case (int) EnergyDataProto.TIMESTAMP_MS:
- mTimestampMs = pis.readLong(EnergyDataProto.TIMESTAMP_MS);
- break;
-
- case (int) EnergyDataProto.ENERGY_UWS:
- mEnergyUWs = pis.readLong(EnergyDataProto.ENERGY_UWS);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in EnergyDataProto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in EnergyDataProto: "
- + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- @Override
- protected void toProto(ProtoOutputStream pos) {
- pos.write(EnergyDataProto.INDEX, mIndex);
- pos.write(EnergyDataProto.TIMESTAMP_MS, mTimestampMs);
- pos.write(EnergyDataProto.ENERGY_UWS, mEnergyUWs);
- }
-
- @Override
- public String toString() {
- return String.format("Index = " + mIndex
- + ", Timestamp (ms) = " + mTimestampMs
- + ", Energy (uWs) = " + mEnergyUWs);
- }
- }
-
- private abstract static class Data {
- public long mIndex;
- protected abstract void unpackProto(ProtoInputStream pis) throws IOException;
- protected abstract void toProto(ProtoOutputStream pos);
- }
-}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 84a6fc94598e..5d8afd43a5f7 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -198,19 +198,21 @@ public class PowerStatsDataStorage {
* array and written to on-device storage.
*/
public void write(byte[] data) {
- mLock.lock();
-
- long currentTimeMillis = System.currentTimeMillis();
- try {
- DataElement dataElement = new DataElement(data);
- mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
- currentTimeMillis);
- mFileRotator.maybeRotate(currentTimeMillis);
- } catch (IOException e) {
- Log.e(TAG, "Failed to write to on-device storage: " + e);
- }
+ if (data.length > 0) {
+ mLock.lock();
+
+ long currentTimeMillis = System.currentTimeMillis();
+ try {
+ DataElement dataElement = new DataElement(data);
+ mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
+ currentTimeMillis);
+ mFileRotator.maybeRotate(currentTimeMillis);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write to on-device storage: " + e);
+ }
- mLock.unlock();
+ mLock.unlock();
+ }
}
/**
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index dc996a3e2d2e..18646b9cc06c 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -16,6 +16,17 @@
package com.android.server.powerstats;
+import android.hardware.power.stats.IPowerStats;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.function.Supplier;
+
/**
* PowerStatsHALWrapper is a wrapper class for the PowerStats HAL API calls.
*/
@@ -27,30 +38,50 @@ public final class PowerStatsHALWrapper {
*/
public interface IPowerStatsHALWrapper {
/**
- * Returns rail info for all available ODPM rails.
+ * Returns the energy consumer IDs for all available energy consumers (power models) on the
+ * device. Examples of subsystems for which energy consumer results (power models)
+ * may be available are GPS, display, wifi, etc. The default list of energy
+ * consumers can be found in the PowerStats HAL definition (EnergyConsumerId.aidl).
+ * The availability of energy consumer IDs is hardware dependent.
+ *
+ * @return List of EnergyConsumerIds all available energy consumers.
+ */
+ int[] getEnergyConsumerInfo();
+
+ /**
+ * Returns the energy consumer result for all available energy consumers (power models).
+ * Available consumers can be retrieved by calling getEnergyConsumerInfo(). The
+ * subsystem corresponding to the energy consumer result is defined by the energy
+ * consumer ID.
+ *
+ * @return List of EnergyConsumerResult objects containing energy consumer results for all
+ * available energy consumers (power models).
+ */
+ android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed();
+
+ /**
+ * Returns channel info for all available energy meters.
*
- * @return array of RailInfo objects containing rail info for all
- * available rails.
+ * @return List of ChannelInfo objects containing channel info for all available energy
+ * meters.
*/
- PowerStatsData.RailInfo[] readRailInfo();
+ android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo();
/**
- * Returns energy data for all available ODPM rails. Available rails can
- * be retrieved by calling nativeGetRailInfo. Energy data and
- * rail info can be linked through the index field.
+ * Returns energy measurements for all available energy meters. Available channels can be
+ * retrieved by calling getEnergyMeterInfo(). Energy measurements and channel info
+ * can be linked through the channelId field.
*
- * @return array of EnergyData objects containing energy data for all
- * available rails.
+ * @return List of EnergyMeasurement objects containing energy measurements for all
+ * available energy meters.
*/
- PowerStatsData.EnergyData[] readEnergyData();
+ android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters();
/**
* Returns boolean indicating if connection to power stats HAL was
* established.
*
- * @return true if connection to power stats HAL was correctly established
- * and that energy data and rail info can be read from the interface.
- * false otherwise
+ * @return true if connection to power stats HAL was correctly established.
*/
boolean initialize();
}
@@ -61,45 +92,108 @@ public final class PowerStatsHALWrapper {
* framework and will be passed into the PowerStatsService through an injector.
*/
public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper {
- private static native boolean nativeInit();
- private static native PowerStatsData.RailInfo[] nativeGetRailInfo();
- private static native PowerStatsData.EnergyData[] nativeGetEnergyData();
+ private static Supplier<IPowerStats> sVintfPowerStats;
- /**
- * Returns rail info for all available ODPM rails.
- *
- * @return array of RailInfo objects containing rail info for all
- * available rails.
- */
@Override
- public PowerStatsData.RailInfo[] readRailInfo() {
- return nativeGetRailInfo();
+ public int[] getEnergyConsumerInfo() {
+ int[] energyConsumerInfoHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyConsumerInfoHAL = sVintfPowerStats.get().getEnergyConsumerInfo();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy consumer info from PowerStats HAL");
+ }
+ }
+
+ return energyConsumerInfoHAL;
}
- /**
- * Returns energy data for all available ODPM rails. Available rails can
- * be retrieved by calling nativeGetRailInfo. Energy data and
- * rail info can be linked through the index field.
- *
- * @return array of EnergyData objects containing energy data for all
- * available rails.
- */
@Override
- public PowerStatsData.EnergyData[] readEnergyData() {
- return nativeGetEnergyData();
+ public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed() {
+ android.hardware.power.stats.EnergyConsumerResult[] energyConsumedHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyConsumedHAL =
+ sVintfPowerStats.get().getEnergyConsumed(new int[0]);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL");
+ }
+ }
+
+ return energyConsumedHAL;
+ }
+
+ @Override
+ public android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo() {
+ android.hardware.power.stats.ChannelInfo[] energyMeterInfoHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyMeterInfoHAL = sVintfPowerStats.get().getEnergyMeterInfo();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy meter info from PowerStats HAL");
+ }
+ }
+
+ return energyMeterInfoHAL;
+ }
+
+ @Override
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters() {
+ android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyMeasurementHAL =
+ sVintfPowerStats.get().readEnergyMeters(new int[0]);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL");
+ }
+ }
+
+ return energyMeasurementHAL;
}
- /**
- * Returns boolean indicating if connection to power stats HAL was
- * established.
- *
- * @return true if connection to power stats HAL was correctly established
- * and that energy data and rail info can be read from the interface.
- * false otherwise
- */
@Override
public boolean initialize() {
- return nativeInit();
+ Supplier<IPowerStats> service = new VintfHalCache();
+
+ if (service.get() == null) {
+ sVintfPowerStats = null;
+ return false;
+ } else {
+ sVintfPowerStats = service;
+ return true;
+ }
+ }
+ }
+
+ private static class VintfHalCache implements Supplier<IPowerStats>, IBinder.DeathRecipient {
+ @GuardedBy("this")
+ private IPowerStats mInstance = null;
+
+ @Override
+ public synchronized IPowerStats get() {
+ if (mInstance == null) {
+ IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
+ "android.hardware.power.stats.IPowerStats/default"));
+ if (binder != null) {
+ mInstance = IPowerStats.Stub.asInterface(binder);
+ try {
+ binder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register DeathRecipient for " + mInstance);
+ }
+ }
+ }
+ return mInstance;
+ }
+
+ @Override
+ public synchronized void binderDied() {
+ mInstance = null;
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 71a34a4174e5..bec99bca4d46 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -17,6 +17,9 @@
package com.android.server.powerstats;
import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -25,6 +28,10 @@ import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyMeasurementUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -32,18 +39,19 @@ import java.io.FileDescriptor;
import java.io.IOException;
/**
- * PowerStatsLogger is responsible for logging energy data to on-device
- * storage. Messages are sent to its message handler to request that energy
- * data be logged, at which time it queries the PowerStats HAL and logs the
- * data to on-device storage. The on-device storage is dumped to file by
- * calling writeToFile with a file descriptor that points to the output file.
+ * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
+ * Messages are sent to its message handler to request that energy data be logged, at which time it
+ * queries the PowerStats HAL and logs the data to on-device storage. The on-device storage is
+ * dumped to file by calling writeModelDataToFile or writeMeterDataToFile with a file descriptor
+ * that points to the output file.
*/
public final class PowerStatsLogger extends Handler {
private static final String TAG = PowerStatsLogger.class.getSimpleName();
private static final boolean DEBUG = false;
protected static final int MSG_LOG_TO_DATA_STORAGE = 0;
- private final PowerStatsDataStorage mPowerStatsDataStorage;
+ private final PowerStatsDataStorage mPowerStatsMeterStorage;
+ private final PowerStatsDataStorage mPowerStatsModelStorage;
private final IPowerStatsHALWrapper mPowerStatsHALWrapper;
@Override
@@ -51,31 +59,81 @@ public final class PowerStatsLogger extends Handler {
switch (msg.what) {
case MSG_LOG_TO_DATA_STORAGE:
if (DEBUG) Log.d(TAG, "Logging to data storage");
- PowerStatsData energyData =
- new PowerStatsData(mPowerStatsHALWrapper.readEnergyData());
- mPowerStatsDataStorage.write(energyData.getProtoBytes());
+
+ // Log power meter data.
+ EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters();
+ mPowerStatsMeterStorage.write(
+ EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
+ if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
+
+ // Log power model data.
+ EnergyConsumerResult[] energyConsumerResults =
+ mPowerStatsHALWrapper.getEnergyConsumed();
+ mPowerStatsModelStorage.write(
+ EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
+ if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);
break;
}
}
/**
- * Writes data stored in PowerStatsDataStorage to a file descriptor.
+ * Writes meter data stored in PowerStatsDataStorage to a file descriptor.
+ *
+ * @param fd FileDescriptor where meter data stored in PowerStatsDataStorage is written. Data
+ * is written in protobuf format as defined by powerstatsservice.proto.
+ */
+ public void writeMeterDataToFile(FileDescriptor fd) {
+ if (DEBUG) Log.d(TAG, "Writing meter data to file");
+
+ final ProtoOutputStream pos = new ProtoOutputStream(fd);
+
+ try {
+ ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
+ ChannelInfoUtils.packProtoMessage(channelInfo, pos);
+ if (DEBUG) ChannelInfoUtils.print(channelInfo);
+
+ mPowerStatsMeterStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+ @Override
+ public void onReadDataElement(byte[] data) {
+ try {
+ final ProtoInputStream pis =
+ new ProtoInputStream(new ByteArrayInputStream(data));
+ // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
+ // a byte array that already contains a serialized proto, so I have to
+ // deserialize, then re-serialize. This is computationally inefficient.
+ EnergyMeasurement[] energyMeasurement =
+ EnergyMeasurementUtils.unpackProtoMessage(data);
+ EnergyMeasurementUtils.packProtoMessage(energyMeasurement, pos);
+ if (DEBUG) EnergyMeasurementUtils.print(energyMeasurement);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write energy meter data to incident report.");
+ }
+ }
+ });
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write energy meter info to incident report.");
+ }
+
+ pos.flush();
+ }
+
+ /**
+ * Writes model data stored in PowerStatsDataStorage to a file descriptor.
*
- * @param fd FileDescriptor where data stored in PowerStatsDataStorage is
- * written. Data is written in protobuf format as defined by
- * powerstatsservice.proto.
+ * @param fd FileDescriptor where model data stored in PowerStatsDataStorage is written. Data
+ * is written in protobuf format as defined by powerstatsservice.proto.
*/
- public void writeToFile(FileDescriptor fd) {
- if (DEBUG) Log.d(TAG, "Writing to file");
+ public void writeModelDataToFile(FileDescriptor fd) {
+ if (DEBUG) Log.d(TAG, "Writing model data to file");
final ProtoOutputStream pos = new ProtoOutputStream(fd);
try {
- PowerStatsData railInfo = new PowerStatsData(mPowerStatsHALWrapper.readRailInfo());
- railInfo.toProto(pos);
- if (DEBUG) railInfo.print();
+ int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+ EnergyConsumerIdUtils.packProtoMessage(energyConsumerId, pos);
+ if (DEBUG) EnergyConsumerIdUtils.print(energyConsumerId);
- mPowerStatsDataStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+ mPowerStatsModelStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
@Override
public void onReadDataElement(byte[] data) {
try {
@@ -84,26 +142,29 @@ public final class PowerStatsLogger extends Handler {
// TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
// a byte array that already contains a serialized proto, so I have to
// deserialize, then re-serialize. This is computationally inefficient.
- final PowerStatsData energyData = new PowerStatsData(pis);
- energyData.toProto(pos);
- if (DEBUG) energyData.print();
+ EnergyConsumerResult[] energyConsumerResult =
+ EnergyConsumerResultUtils.unpackProtoMessage(data);
+ EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos);
+ if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
} catch (IOException e) {
- Log.e(TAG, "Failed to write energy data to incident report.");
+ Log.e(TAG, "Failed to write energy model data to incident report.");
}
}
});
} catch (IOException e) {
- Log.e(TAG, "Failed to write rail info to incident report.");
+ Log.e(TAG, "Failed to write energy model info to incident report.");
}
pos.flush();
}
- public PowerStatsLogger(Context context, File dataStoragePath, String dataStorageFilename,
- IPowerStatsHALWrapper powerStatsHALWrapper) {
+ public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
+ String modelFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
super(Looper.getMainLooper());
mPowerStatsHALWrapper = powerStatsHALWrapper;
- mPowerStatsDataStorage = new PowerStatsDataStorage(context, dataStoragePath,
- dataStorageFilename);
+ mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ meterFilename);
+ mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ modelFilename);
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 81883f3012e9..b89464fe4656 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -43,7 +43,8 @@ public class PowerStatsService extends SystemService {
private static final boolean DEBUG = false;
private static final String DATA_STORAGE_SUBDIR = "powerstats";
private static final int DATA_STORAGE_VERSION = 0;
- private static final String DATA_STORAGE_FILENAME = "log.powerstats." + DATA_STORAGE_VERSION;
+ private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION;
+ private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
private final Injector mInjector;
@@ -63,8 +64,12 @@ public class PowerStatsService extends SystemService {
DATA_STORAGE_SUBDIR);
}
- String createDataStorageFilename() {
- return DATA_STORAGE_FILENAME;
+ String createMeterFilename() {
+ return METER_FILENAME;
+ }
+
+ String createModelFilename() {
+ return MODEL_FILENAME;
}
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
@@ -72,9 +77,10 @@ public class PowerStatsService extends SystemService {
}
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
- return new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
- powerStatsHALWrapper);
+ String meterFilename, String modelFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
+ return new PowerStatsLogger(context, dataStoragePath, meterFilename,
+ modelFilename, powerStatsHALWrapper);
}
BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -91,11 +97,15 @@ public class PowerStatsService extends SystemService {
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- if (args.length > 0 && "--proto".equals(args[0])) {
- if (mPowerStatsLogger == null) {
- Log.e(TAG, "PowerStats HAL is not initialized. No data available.");
- } else {
- mPowerStatsLogger.writeToFile(fd);
+ if (mPowerStatsLogger == null) {
+ Log.e(TAG, "PowerStats HAL is not initialized. No data available.");
+ } else {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ if ("model".equals(args[1])) {
+ mPowerStatsLogger.writeModelDataToFile(fd);
+ } else if ("meter".equals(args[1])) {
+ mPowerStatsLogger.writeMeterDataToFile(fd);
+ }
}
}
}
@@ -121,8 +131,8 @@ public class PowerStatsService extends SystemService {
// Only start logger and triggers if initialization is successful.
mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
- mInjector.createDataStoragePath(), mInjector.createDataStorageFilename(),
- mPowerStatsHALWrapper);
+ mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
+ mInjector.createModelFilename(), mPowerStatsHALWrapper);
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
} else {
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
new file mode 100644
index 000000000000..c29c5daf34a5
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2020 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.powerstats;
+
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+import android.util.proto.WireTypeMismatchException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ProtoStreamUtils provides helper functions for the PowerStats HAL objects returned from calls
+ * to the PowerStats HAL APIs. It provides functions to pack/unpack object arrays to/from protobuf
+ * format. These helper functions are required since frameworks code uses the genstream option
+ * when generating source code and therefore, getter/setter helper functions are not available. The
+ * protobufs need to be packed/unpacked in a more manual way using
+ * ProtoOutputStream/ProtoInputStream. It also provides print() functions for debugging purposes.
+ */
+public class ProtoStreamUtils {
+ private static final String TAG = ProtoStreamUtils.class.getSimpleName();
+
+ static class ChannelInfoUtils {
+ public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < channelInfo.length; i++) {
+ token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
+ pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId);
+ pos.write(ChannelInfoProto.CHANNEL_NAME, channelInfo[i].channelName);
+ pos.end(token);
+ }
+
+ }
+
+ public static void print(ChannelInfo[] channelInfo) {
+ for (int i = 0; i < channelInfo.length; i++) {
+ Log.d(TAG, "ChannelId = " + channelInfo[i].channelId
+ + ", ChannelName = " + channelInfo[i].channelName);
+ }
+ }
+ }
+
+ static class EnergyMeasurementUtils {
+ public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(energyMeasurement, pos);
+ return pos.getBytes();
+ }
+
+ public static void packProtoMessage(EnergyMeasurement[] energyMeasurement,
+ ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyMeasurement.length; i++) {
+ token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId);
+ pos.write(EnergyMeasurementProto.TIMESTAMP_MS, energyMeasurement[i].timestampMs);
+ pos.write(EnergyMeasurementProto.ENERGY_UWS, energyMeasurement[i].energyUWs);
+ pos.end(token);
+ }
+ }
+
+ public static EnergyMeasurement[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<EnergyMeasurement> energyMeasurementList = new ArrayList<EnergyMeasurement>();
+ long token;
+
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+ if (nextField == (int) PowerStatsServiceMeterProto.ENERGY_MEASUREMENT) {
+ token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ energyMeasurementList.add(unpackProtoMessage(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return energyMeasurementList.toArray(
+ new EnergyMeasurement[energyMeasurementList.size()]);
+ } else {
+ Log.e(TAG, "Unhandled field in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static EnergyMeasurement unpackProtoMessage(ProtoInputStream pis)
+ throws IOException {
+ EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyMeasurementProto.CHANNEL_ID:
+ energyMeasurement.channelId =
+ pis.readInt(EnergyMeasurementProto.CHANNEL_ID);
+ break;
+
+ case (int) EnergyMeasurementProto.TIMESTAMP_MS:
+ energyMeasurement.timestampMs =
+ pis.readLong(EnergyMeasurementProto.TIMESTAMP_MS);
+ break;
+
+ case (int) EnergyMeasurementProto.ENERGY_UWS:
+ energyMeasurement.energyUWs =
+ pis.readLong(EnergyMeasurementProto.ENERGY_UWS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyMeasurement;
+
+ default:
+ Log.e(TAG, "Unhandled field in EnergyMeasurementProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in EnergyMeasurementProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ public static void print(EnergyMeasurement[] energyMeasurement) {
+ for (int i = 0; i < energyMeasurement.length; i++) {
+ Log.d(TAG, "ChannelId = " + energyMeasurement[i].channelId
+ + ", Timestamp (ms) = " + energyMeasurement[i].timestampMs
+ + ", Energy (uWs) = " + energyMeasurement[i].energyUWs);
+ }
+ }
+ }
+
+ static class EnergyConsumerIdUtils {
+ public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyConsumerId.length; i++) {
+ token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
+ pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]);
+ pos.end(token);
+ }
+ }
+
+ public static void print(int[] energyConsumerId) {
+ for (int i = 0; i < energyConsumerId.length; i++) {
+ Log.d(TAG, "EnergyConsumerId = " + energyConsumerId[i]);
+ }
+ }
+ }
+
+ static class EnergyConsumerResultUtils {
+ public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(energyConsumerResult, pos);
+ return pos.getBytes();
+ }
+
+ public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult,
+ ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyConsumerResult.length; i++) {
+ token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID,
+ energyConsumerResult[i].energyConsumerId);
+ pos.write(EnergyConsumerResultProto.TIMESTAMP_MS,
+ energyConsumerResult[i].timestampMs);
+ pos.write(EnergyConsumerResultProto.ENERGY_UWS, energyConsumerResult[i].energyUWs);
+ pos.end(token);
+ }
+ }
+
+ public static EnergyConsumerResult[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<EnergyConsumerResult> energyConsumerResultList =
+ new ArrayList<EnergyConsumerResult>();
+ long token;
+
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+ if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT) {
+ token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ energyConsumerResultList.add(unpackProtoMessage(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return energyConsumerResultList.toArray(
+ new EnergyConsumerResult[energyConsumerResultList.size()]);
+ } else {
+ Log.e(TAG, "Unhandled field in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static EnergyConsumerResult unpackProtoMessage(ProtoInputStream pis)
+ throws IOException {
+ EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyConsumerResultProto.ENERGY_CONSUMER_ID:
+ energyConsumerResult.energyConsumerId =
+ pis.readInt(EnergyConsumerResultProto.ENERGY_CONSUMER_ID);
+ break;
+
+ case (int) EnergyConsumerResultProto.TIMESTAMP_MS:
+ energyConsumerResult.timestampMs =
+ pis.readLong(EnergyConsumerResultProto.TIMESTAMP_MS);
+ break;
+
+ case (int) EnergyConsumerResultProto.ENERGY_UWS:
+ energyConsumerResult.energyUWs =
+ pis.readLong(EnergyConsumerResultProto.ENERGY_UWS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyConsumerResult;
+
+ default:
+ Log.e(TAG, "Unhandled field in EnergyConsumerResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in EnergyConsumerResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ public static void print(EnergyConsumerResult[] energyConsumerResult) {
+ for (int i = 0; i < energyConsumerResult.length; i++) {
+ Log.d(TAG, "EnergyConsumerId = " + energyConsumerResult[i].energyConsumerId
+ + ", Timestamp (ms) = " + energyConsumerResult[i].timestampMs
+ + ", Energy (uWs) = " + energyConsumerResult[i].energyUWs);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
index 7977e931c37f..940490411b49 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -16,11 +16,6 @@
package com.android.server.soundtrigger_middleware;
-import android.media.ICaptureStateListener;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
import java.util.concurrent.Semaphore;
@@ -78,7 +73,11 @@ class ExternalCaptureStateTracker {
* @param active true when external capture is active.
*/
private void setCaptureState(boolean active) {
- mListener.accept(active);
+ try {
+ mListener.accept(active);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception caught while setting capture state", e);
+ }
}
/**
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index 531136976c81..52236a81c607 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -25,7 +25,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.debug.AdbManagerInternal;
-import android.debug.AdbTransportType;
import android.location.LocationManager;
import android.os.BatteryManager;
import android.os.Binder;
@@ -162,12 +161,11 @@ public class TestHarnessModeService extends SystemService {
private void configureSettings() {
ContentResolver cr = getContext().getContentResolver();
- // Stop ADB before we enable it, otherwise on userdebug/eng builds, the keys won't have
- // registered with adbd, and it will prompt the user to confirm the keys.
- Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 0);
- AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class);
- if (adbManager.isAdbEnabled(AdbTransportType.USB)) {
- adbManager.stopAdbdForTransport(AdbTransportType.USB);
+ // If adb is already enabled, then we need to restart the daemon to pick up the change in
+ // keys. This is only really useful for userdebug/eng builds.
+ if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 1) {
+ SystemProperties.set("ctl.restart", "adbd");
+ Slog.d(TAG, "Restarted adbd");
}
// Disable the TTL for ADB keys before enabling ADB
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 201894049b06..68f554cb2758 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -197,19 +197,6 @@ public class SystemImpl implements SystemInterface {
}
@Override
- public boolean isFallbackLogicEnabled() {
- // Note that this is enabled by default (i.e. if the setting hasn't been set).
- return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
- }
-
- @Override
- public void enableFallbackLogic(boolean enable) {
- Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
- }
-
- @Override
public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
for(UserInfo userInfo : userManager.getUsers()) {
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 743740d277ba..09c23a7229ad 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -41,9 +41,6 @@ public interface SystemInterface {
public void updateUserSetting(Context context, String newProviderName);
public void killPackageDependents(String packageName);
- public boolean isFallbackLogicEnabled();
- public void enableFallbackLogic(boolean enable);
-
public void enablePackageForAllUsers(Context context, String packageName, boolean enable);
public boolean systemIsDebuggable();
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 11fd7953e08f..4d670f1eb59c 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -15,15 +15,22 @@
*/
package com.android.server.webkit;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.util.Slog;
+import android.webkit.UserPackage;
+import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Implementation of the WebViewUpdateService.
@@ -33,18 +40,17 @@ import java.io.PrintWriter;
* This class keeps track of and prepares the current WebView implementation, and needs to keep
* track of a couple of different things such as what package is used as WebView implementation.
*
- * The public methods in this class are accessed from WebViewUpdateService either on the UI thread
- * or on one of multiple Binder threads. The WebView preparation code shares state between threads
- * meaning that code that chooses a new WebView implementation or checks which implementation is
- * being used needs to hold a lock.
+ * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI
+ * thread or on one of multiple Binder threads. The WebView preparation code shares state between
+ * threads meaning that code that chooses a new WebView implementation or checks which
+ * implementation is being used needs to hold a lock.
*
* The WebViewUpdateService can be accessed in a couple of different ways.
* 1. It is started from the SystemServer at boot - at that point we just initiate some state such
* as the WebView preparation class.
* 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot
* and the WebViewUpdateService should not have been accessed before this call. In this call we
- * migrate away from the old fallback logic if necessary and then choose WebView implementation for
- * the first time.
+ * choose WebView implementation for the first time.
* 3. The update service listens for Intents related to package installs and removals. These intents
* are received and processed on the UI thread. Each intent can result in changing WebView
* implementation.
@@ -56,37 +62,133 @@ import java.io.PrintWriter;
*
* @hide
*/
-public class WebViewUpdateServiceImpl {
+class WebViewUpdateServiceImpl {
private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
- private SystemInterface mSystemInterface;
- private WebViewUpdater mWebViewUpdater;
- final private Context mContext;
+ private static class WebViewPackageMissingException extends Exception {
+ WebViewPackageMissingException(String message) {
+ super(message);
+ }
+
+ WebViewPackageMissingException(Exception e) {
+ super(e);
+ }
+ }
+
+ private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
+ private static final long NS_PER_MS = 1000000;
+
+ private static final int VALIDITY_OK = 0;
+ private static final int VALIDITY_INCORRECT_SDK_VERSION = 1;
+ private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
+ private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
+ private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
+
+ private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
+ private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+
+ private final SystemInterface mSystemInterface;
+ private final Context mContext;
- private final static int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
- private final static int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+ private long mMinimumVersionCode = -1;
- public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
+ // Keeps track of the number of running relro creations
+ private int mNumRelroCreationsStarted = 0;
+ private int mNumRelroCreationsFinished = 0;
+ // Implies that we need to rerun relro creation because we are using an out-of-date package
+ private boolean mWebViewPackageDirty = false;
+ private boolean mAnyWebViewInstalled = false;
+
+ private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+ // The WebView package currently in use (or the one we are preparing).
+ private PackageInfo mCurrentWebViewPackage = null;
+
+ private final Object mLock = new Object();
+
+ WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
mContext = context;
mSystemInterface = systemInterface;
- mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
}
void packageStateChanged(String packageName, int changedState, int userId) {
// We don't early out here in different cases where we could potentially early-out (e.g. if
// we receive PACKAGE_CHANGED for another user than the system user) since that would
// complicate this logic further and open up for more edge cases.
- mWebViewUpdater.packageStateChanged(packageName, changedState);
+ for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+ String webviewPackage = provider.packageName;
+
+ if (webviewPackage.equals(packageName)) {
+ boolean updateWebView = false;
+ boolean removedOrChangedOldPackage = false;
+ String oldProviderName = null;
+ PackageInfo newPackage = null;
+ synchronized (mLock) {
+ try {
+ newPackage = findPreferredWebViewPackage();
+ if (mCurrentWebViewPackage != null) {
+ oldProviderName = mCurrentWebViewPackage.packageName;
+ }
+ // Only trigger update actions if the updated package is the one
+ // that will be used, or the one that was in use before the
+ // update, or if we haven't seen a valid WebView package before.
+ updateWebView =
+ provider.packageName.equals(newPackage.packageName)
+ || provider.packageName.equals(oldProviderName)
+ || mCurrentWebViewPackage == null;
+ // We removed the old package if we received an intent to remove
+ // or replace the old package.
+ removedOrChangedOldPackage =
+ provider.packageName.equals(oldProviderName);
+ if (updateWebView) {
+ onWebViewProviderChanged(newPackage);
+ }
+ } catch (WebViewPackageMissingException e) {
+ mCurrentWebViewPackage = null;
+ Slog.e(TAG, "Could not find valid WebView package to create relro with "
+ + e);
+ }
+ }
+ if (updateWebView && !removedOrChangedOldPackage
+ && oldProviderName != null) {
+ // If the provider change is the result of adding or replacing a
+ // package that was not the previous provider then we must kill
+ // packages dependent on the old package ourselves. The framework
+ // only kills dependents of packages that are being removed.
+ mSystemInterface.killPackageDependents(oldProviderName);
+ }
+ return;
+ }
+ }
}
void prepareWebViewInSystemServer() {
- migrateFallbackStateOnBoot();
- mWebViewUpdater.prepareWebViewInSystemServer();
+ try {
+ synchronized (mLock) {
+ mCurrentWebViewPackage = findPreferredWebViewPackage();
+ String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
+ if (userSetting != null
+ && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
+ // Don't persist the user-chosen setting across boots if the package being
+ // chosen is not used (could be disabled or uninstalled) so that the user won't
+ // be surprised by the device switching to using a certain webview package,
+ // that was uninstalled/disabled a long time ago, if it is installed/enabled
+ // again.
+ mSystemInterface.updateUserSetting(mContext,
+ mCurrentWebViewPackage.packageName);
+ }
+ onWebViewProviderChanged(mCurrentWebViewPackage);
+ }
+ } catch (Throwable t) {
+ // Log and discard errors at this stage as we must not crash the system server.
+ Slog.e(TAG, "error preparing webview provider from system server", t);
+ }
+
if (getCurrentWebViewPackage() == null) {
// We didn't find a valid WebView implementation. Try explicitly re-enabling the
// fallback package for all users in case it was disabled, even if we already did the
- // one-time migration before. If this actually changes the state, WebViewUpdater will
- // see the PackageManager broadcast shortly and try again.
+ // one-time migration before. If this actually changes the state, we will see the
+ // PackageManager broadcast shortly and try again.
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider != null) {
@@ -105,7 +207,7 @@ public class WebViewUpdateServiceImpl {
}
}
- void startZygoteWhenReady() {
+ private void startZygoteWhenReady() {
// Wait on a background thread for RELRO creation to be done. We ignore the return value
// because even if RELRO creation failed we still want to start the zygote.
waitForAndGetProvider();
@@ -131,23 +233,231 @@ public class WebViewUpdateServiceImpl {
*/
private void handleUserChange() {
// Potentially trigger package-changing logic.
- mWebViewUpdater.updateCurrentWebViewPackage(null);
+ updateCurrentWebViewPackage(null);
}
void notifyRelroCreationCompleted() {
- mWebViewUpdater.notifyRelroCreationCompleted();
+ synchronized (mLock) {
+ mNumRelroCreationsFinished++;
+ checkIfRelrosDoneLocked();
+ }
}
WebViewProviderResponse waitForAndGetProvider() {
- return mWebViewUpdater.waitForAndGetProvider();
+ PackageInfo webViewPackage = null;
+ final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+ boolean webViewReady = false;
+ int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
+ synchronized (mLock) {
+ webViewReady = webViewIsReadyLocked();
+ while (!webViewReady) {
+ final long timeNowMs = System.nanoTime() / NS_PER_MS;
+ if (timeNowMs >= timeoutTimeMs) break;
+ try {
+ mLock.wait(timeoutTimeMs - timeNowMs);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ webViewReady = webViewIsReadyLocked();
+ }
+ // Make sure we return the provider that was used to create the relro file
+ webViewPackage = mCurrentWebViewPackage;
+ if (webViewReady) {
+ // success
+ } else if (!mAnyWebViewInstalled) {
+ webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+ } else {
+ // Either the current relro creation isn't done yet, or the new relro creatioin
+ // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+ webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+ Slog.e(TAG, "Timed out waiting for relro creation, relros started "
+ + mNumRelroCreationsStarted
+ + " relros finished " + mNumRelroCreationsFinished
+ + " package dirty? " + mWebViewPackageDirty);
+ }
+ }
+ if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+ return new WebViewProviderResponse(webViewPackage, webViewStatus);
}
- String changeProviderAndSetting(String newProvider) {
- return mWebViewUpdater.changeProviderAndSetting(newProvider);
+ /**
+ * Change WebView provider and provider setting and kill packages using the old provider.
+ * Return the new provider (in case we are in the middle of creating relro files, or
+ * replacing that provider it will not be in use directly, but will be used when the relros
+ * or the replacement are done).
+ */
+ String changeProviderAndSetting(String newProviderName) {
+ PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
+ if (newPackage == null) return "";
+ return newPackage.packageName;
}
+ /**
+ * Update the current WebView package.
+ * @param newProviderName the package to switch to, null if no package has been explicitly
+ * chosen.
+ */
+ private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) {
+ PackageInfo oldPackage = null;
+ PackageInfo newPackage = null;
+ boolean providerChanged = false;
+ synchronized (mLock) {
+ oldPackage = mCurrentWebViewPackage;
+
+ if (newProviderName != null) {
+ mSystemInterface.updateUserSetting(mContext, newProviderName);
+ }
+
+ try {
+ newPackage = findPreferredWebViewPackage();
+ providerChanged = (oldPackage == null)
+ || !newPackage.packageName.equals(oldPackage.packageName);
+ } catch (WebViewPackageMissingException e) {
+ // If updated the Setting but don't have an installed WebView package, the
+ // Setting will be used when a package is available.
+ mCurrentWebViewPackage = null;
+ Slog.e(TAG, "Couldn't find WebView package to use " + e);
+ return null;
+ }
+ // Perform the provider change if we chose a new provider
+ if (providerChanged) {
+ onWebViewProviderChanged(newPackage);
+ }
+ }
+ // Kill apps using the old provider only if we changed provider
+ if (providerChanged && oldPackage != null) {
+ mSystemInterface.killPackageDependents(oldPackage.packageName);
+ }
+ // Return the new provider, this is not necessarily the one we were asked to switch to,
+ // but the persistent setting will now be pointing to the provider we were asked to
+ // switch to anyway.
+ return newPackage;
+ }
+
+ /**
+ * This is called when we change WebView provider, either when the current provider is
+ * updated or a new provider is chosen / takes precedence.
+ */
+ private void onWebViewProviderChanged(PackageInfo newPackage) {
+ synchronized (mLock) {
+ mAnyWebViewInstalled = true;
+ if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ mCurrentWebViewPackage = newPackage;
+
+ // The relro creations might 'finish' (not start at all) before
+ // WebViewFactory.onWebViewProviderChanged which means we might not know the
+ // number of started creations before they finish.
+ mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+ mNumRelroCreationsFinished = 0;
+ mNumRelroCreationsStarted =
+ mSystemInterface.onWebViewProviderChanged(newPackage);
+ // If the relro creations finish before we know the number of started creations
+ // we will have to do any cleanup/notifying here.
+ checkIfRelrosDoneLocked();
+ } else {
+ mWebViewPackageDirty = true;
+ }
+ }
+ }
+
+ /**
+ * Fetch only the currently valid WebView packages.
+ **/
WebViewProviderInfo[] getValidWebViewPackages() {
- return mWebViewUpdater.getValidWebViewPackages();
+ ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+ WebViewProviderInfo[] providers =
+ new WebViewProviderInfo[providersAndPackageInfos.length];
+ for (int n = 0; n < providersAndPackageInfos.length; n++) {
+ providers[n] = providersAndPackageInfos[n].provider;
+ }
+ return providers;
+ }
+
+ private static class ProviderAndPackageInfo {
+ public final WebViewProviderInfo provider;
+ public final PackageInfo packageInfo;
+
+ ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+ this.provider = provider;
+ this.packageInfo = packageInfo;
+ }
+ }
+
+ private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+ WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+ List<ProviderAndPackageInfo> providers = new ArrayList<>();
+ for (int n = 0; n < allProviders.length; n++) {
+ try {
+ PackageInfo packageInfo =
+ mSystemInterface.getPackageInfoForProvider(allProviders[n]);
+ if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) {
+ providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+ }
+ } catch (NameNotFoundException e) {
+ // Don't add non-existent packages
+ }
+ }
+ return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+ }
+
+ /**
+ * Returns either the package info of the WebView provider determined in the following way:
+ * If the user has chosen a provider then use that if it is valid,
+ * otherwise use the first package in the webview priority list that is valid.
+ *
+ */
+ private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
+ ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+
+ String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
+
+ // If the user has chosen provider, use that (if it's installed and enabled for all
+ // users).
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
+ // userPackages can contain null objects.
+ List<UserPackage> userPackages =
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+ providerAndPackage.provider);
+ if (isInstalledAndEnabledForAllUsers(userPackages)) {
+ return providerAndPackage.packageInfo;
+ }
+ }
+ }
+
+ // User did not choose, or the choice failed; use the most stable provider that is
+ // installed and enabled for all users, and available by default (not through
+ // user choice).
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.availableByDefault) {
+ // userPackages can contain null objects.
+ List<UserPackage> userPackages =
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+ providerAndPackage.provider);
+ if (isInstalledAndEnabledForAllUsers(userPackages)) {
+ return providerAndPackage.packageInfo;
+ }
+ }
+ }
+
+ // This should never happen during normal operation (only with modified system images).
+ mAnyWebViewInstalled = false;
+ throw new WebViewPackageMissingException("Could not find a loadable WebView package");
+ }
+
+ /**
+ * Return true iff {@param packageInfos} point to only installed and enabled packages.
+ * The given packages {@param packageInfos} should all be pointing to the same package, but each
+ * PackageInfo representing a different user's package.
+ */
+ private static boolean isInstalledAndEnabledForAllUsers(
+ List<UserPackage> userPackages) {
+ for (UserPackage userPackage : userPackages) {
+ if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
+ return false;
+ }
+ }
+ return true;
}
WebViewProviderInfo[] getWebViewPackages() {
@@ -155,28 +465,143 @@ public class WebViewUpdateServiceImpl {
}
PackageInfo getCurrentWebViewPackage() {
- return mWebViewUpdater.getCurrentWebViewPackage();
+ synchronized (mLock) {
+ return mCurrentWebViewPackage;
+ }
+ }
+
+ /**
+ * Returns whether WebView is ready and is not going to go through its preparation phase
+ * again directly.
+ */
+ private boolean webViewIsReadyLocked() {
+ return !mWebViewPackageDirty
+ && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+ // The current package might be replaced though we haven't received an intent
+ // declaring this yet, the following flag makes anyone loading WebView to wait in
+ // this case.
+ && mAnyWebViewInstalled;
+ }
+
+ private void checkIfRelrosDoneLocked() {
+ if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ if (mWebViewPackageDirty) {
+ mWebViewPackageDirty = false;
+ // If we have changed provider since we started the relro creation we need to
+ // redo the whole process using the new package instead.
+ try {
+ PackageInfo newPackage = findPreferredWebViewPackage();
+ onWebViewProviderChanged(newPackage);
+ } catch (WebViewPackageMissingException e) {
+ mCurrentWebViewPackage = null;
+ // If we can't find any valid WebView package we are now in a state where
+ // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
+ // should simply wait until we receive an intent declaring a new package was
+ // installed.
+ }
+ } else {
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
+ // Ensure the provider targets this framework release (or a later one).
+ if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
+ return VALIDITY_INCORRECT_SDK_VERSION;
+ }
+ if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
+ && !mSystemInterface.systemIsDebuggable()) {
+ // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
+ // minimum version code. This check is only enforced for user builds.
+ return VALIDITY_INCORRECT_VERSION_CODE;
+ }
+ if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
+ return VALIDITY_INCORRECT_SIGNATURE;
+ }
+ if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
+ return VALIDITY_NO_LIBRARY_FLAG;
+ }
+ return VALIDITY_OK;
}
/**
- * If the fallback logic is enabled, re-enable any fallback package for all users, then
- * disable the fallback logic.
+ * Both versionCodes should be from a WebView provider package implemented by Chromium.
+ * VersionCodes from other kinds of packages won't make any sense in this method.
+ *
+ * An introduction to Chromium versionCode scheme:
+ * "BBBBPPPXX"
+ * BBBB: 4 digit branch number. It monotonically increases over time.
+ * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
+ * may change their meaning in the future.
+ * XX: Digits to differentiate different APK builds of the same source version.
+ *
+ * This method takes the "BBBB" of versionCodes and compare them.
*
- * This migrates away from the old fallback mechanism to the new state where packages are never
- * automatically enableenableisabled.
+ * https://www.chromium.org/developers/version-numbers describes general Chromium versioning;
+ * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py
+ * is the canonical source for how Chromium versionCodes are calculated.
+ *
+ * @return true if versionCode1 is higher than or equal to versionCode2.
*/
- private void migrateFallbackStateOnBoot() {
- if (!mSystemInterface.isFallbackLogicEnabled()) return;
-
- WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
- WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
- if (fallbackProvider != null) {
- Slog.i(TAG, "One-time migration: enabling " + fallbackProvider.packageName);
- mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, true);
- } else {
- Slog.i(TAG, "Skipping one-time migration: no fallback provider");
+ private static boolean versionCodeGE(long versionCode1, long versionCode2) {
+ long v1 = versionCode1 / 100000;
+ long v2 = versionCode2 / 100000;
+
+ return v1 >= v2;
+ }
+
+ /**
+ * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
+ * of all available-by-default WebView provider packages. If there is no such WebView provider
+ * package on the system, then return -1, which means all positive versionCode WebView packages
+ * are accepted.
+ *
+ * Note that this is a private method that handles a variable (mMinimumVersionCode) which is
+ * shared between threads. Furthermore, this method does not hold mLock meaning that we must
+ * take extra care to ensure this method is thread-safe.
+ */
+ private long getMinimumVersionCode() {
+ if (mMinimumVersionCode > 0) {
+ return mMinimumVersionCode;
+ }
+
+ long minimumVersionCode = -1;
+ for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+ if (provider.availableByDefault) {
+ try {
+ long versionCode =
+ mSystemInterface.getFactoryPackageVersion(provider.packageName);
+ if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
+ minimumVersionCode = versionCode;
+ }
+ } catch (NameNotFoundException e) {
+ // Safe to ignore.
+ }
+ }
+ }
+
+ mMinimumVersionCode = minimumVersionCode;
+ return mMinimumVersionCode;
+ }
+
+ private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+ PackageInfo packageInfo, SystemInterface systemInterface) {
+ // Skip checking signatures on debuggable builds, for development purposes.
+ if (systemInterface.systemIsDebuggable()) return true;
+
+ // Allow system apps to be valid providers regardless of signature.
+ if (packageInfo.applicationInfo.isSystemApp()) return true;
+
+ // We don't support packages with multiple signatures.
+ if (packageInfo.signatures.length != 1) return false;
+
+ // If any of the declared signatures match the package signature, it's valid.
+ for (Signature signature : provider.signatures) {
+ if (signature.equals(packageInfo.signatures[0])) return true;
}
- mSystemInterface.enableFallbackLogic(false);
+
+ return false;
}
/**
@@ -217,9 +642,89 @@ public class WebViewUpdateServiceImpl {
*/
void dumpState(PrintWriter pw) {
pw.println("Current WebView Update Service state");
- pw.println(String.format(" Fallback logic enabled: %b",
- mSystemInterface.isFallbackLogicEnabled()));
pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled()));
- mWebViewUpdater.dumpState(pw);
+ synchronized (mLock) {
+ if (mCurrentWebViewPackage == null) {
+ pw.println(" Current WebView package is null");
+ } else {
+ pw.println(String.format(" Current WebView package (name, version): (%s, %s)",
+ mCurrentWebViewPackage.packageName,
+ mCurrentWebViewPackage.versionName));
+ }
+ pw.println(String.format(" Minimum targetSdkVersion: %d",
+ UserPackage.MINIMUM_SUPPORTED_SDK));
+ pw.println(String.format(" Minimum WebView version code: %d",
+ mMinimumVersionCode));
+ pw.println(String.format(" Number of relros started: %d",
+ mNumRelroCreationsStarted));
+ pw.println(String.format(" Number of relros finished: %d",
+ mNumRelroCreationsFinished));
+ pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty));
+ pw.println(String.format(" Any WebView package installed: %b",
+ mAnyWebViewInstalled));
+
+ try {
+ PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
+ pw.println(String.format(
+ " Preferred WebView package (name, version): (%s, %s)",
+ preferredWebViewPackage.packageName,
+ preferredWebViewPackage.versionName));
+ } catch (WebViewPackageMissingException e) {
+ pw.println(String.format(" Preferred WebView package: none"));
+ }
+
+ dumpAllPackageInformationLocked(pw);
+ }
+ }
+
+ private void dumpAllPackageInformationLocked(PrintWriter pw) {
+ WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+ pw.println(" WebView packages:");
+ for (WebViewProviderInfo provider : allProviders) {
+ List<UserPackage> userPackages =
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
+ PackageInfo systemUserPackageInfo =
+ userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
+ if (systemUserPackageInfo == null) {
+ pw.println(String.format(" %s is NOT installed.", provider.packageName));
+ continue;
+ }
+
+ int validity = validityResult(provider, systemUserPackageInfo);
+ String packageDetails = String.format(
+ "versionName: %s, versionCode: %d, targetSdkVersion: %d",
+ systemUserPackageInfo.versionName,
+ systemUserPackageInfo.getLongVersionCode(),
+ systemUserPackageInfo.applicationInfo.targetSdkVersion);
+ if (validity == VALIDITY_OK) {
+ boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
+ pw.println(String.format(
+ " Valid package %s (%s) is %s installed/enabled for all users",
+ systemUserPackageInfo.packageName,
+ packageDetails,
+ installedForAllUsers ? "" : "NOT"));
+ } else {
+ pw.println(String.format(" Invalid package %s (%s), reason: %s",
+ systemUserPackageInfo.packageName,
+ packageDetails,
+ getInvalidityReason(validity)));
+ }
+ }
+ }
+
+ private static String getInvalidityReason(int invalidityReason) {
+ switch (invalidityReason) {
+ case VALIDITY_INCORRECT_SDK_VERSION:
+ return "SDK version too low";
+ case VALIDITY_INCORRECT_VERSION_CODE:
+ return "Version code too low";
+ case VALIDITY_INCORRECT_SIGNATURE:
+ return "Incorrect signature";
+ case VALIDITY_NO_LIBRARY_FLAG:
+ return "No WebView-library manifest flag";
+ default:
+ return "Unexcepted validity-reason";
+ }
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
deleted file mode 100644
index 3b58af2a200f..000000000000
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.webkit;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.webkit.UserPackage;
-import android.webkit.WebViewFactory;
-import android.webkit.WebViewProviderInfo;
-import android.webkit.WebViewProviderResponse;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class that decides what WebView implementation to use and prepares that implementation for
- * use.
- */
-class WebViewUpdater {
- private static final String TAG = WebViewUpdater.class.getSimpleName();
-
- private static class WebViewPackageMissingException extends Exception {
- public WebViewPackageMissingException(String message) { super(message); }
- public WebViewPackageMissingException(Exception e) { super(e); }
- }
-
- private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
-
- private final static int VALIDITY_OK = 0;
- private final static int VALIDITY_INCORRECT_SDK_VERSION = 1;
- private final static int VALIDITY_INCORRECT_VERSION_CODE = 2;
- private final static int VALIDITY_INCORRECT_SIGNATURE = 3;
- private final static int VALIDITY_NO_LIBRARY_FLAG = 4;
-
- private Context mContext;
- private SystemInterface mSystemInterface;
- private long mMinimumVersionCode = -1;
-
- // Keeps track of the number of running relro creations
- private int mNumRelroCreationsStarted = 0;
- private int mNumRelroCreationsFinished = 0;
- // Implies that we need to rerun relro creation because we are using an out-of-date package
- private boolean mWebViewPackageDirty = false;
- private boolean mAnyWebViewInstalled = false;
-
- private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
-
- // The WebView package currently in use (or the one we are preparing).
- private PackageInfo mCurrentWebViewPackage = null;
-
- private final Object mLock = new Object();
-
- WebViewUpdater(Context context, SystemInterface systemInterface) {
- mContext = context;
- mSystemInterface = systemInterface;
- }
-
- void packageStateChanged(String packageName, int changedState) {
- for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
- String webviewPackage = provider.packageName;
-
- if (webviewPackage.equals(packageName)) {
- boolean updateWebView = false;
- boolean removedOrChangedOldPackage = false;
- String oldProviderName = null;
- PackageInfo newPackage = null;
- synchronized(mLock) {
- try {
- newPackage = findPreferredWebViewPackage();
- if (mCurrentWebViewPackage != null) {
- oldProviderName = mCurrentWebViewPackage.packageName;
- }
- // Only trigger update actions if the updated package is the one
- // that will be used, or the one that was in use before the
- // update, or if we haven't seen a valid WebView package before.
- updateWebView =
- provider.packageName.equals(newPackage.packageName)
- || provider.packageName.equals(oldProviderName)
- || mCurrentWebViewPackage == null;
- // We removed the old package if we received an intent to remove
- // or replace the old package.
- removedOrChangedOldPackage =
- provider.packageName.equals(oldProviderName);
- if (updateWebView) {
- onWebViewProviderChanged(newPackage);
- }
- } catch (WebViewPackageMissingException e) {
- mCurrentWebViewPackage = null;
- Slog.e(TAG, "Could not find valid WebView package to create " +
- "relro with " + e);
- }
- }
- if(updateWebView && !removedOrChangedOldPackage
- && oldProviderName != null) {
- // If the provider change is the result of adding or replacing a
- // package that was not the previous provider then we must kill
- // packages dependent on the old package ourselves. The framework
- // only kills dependents of packages that are being removed.
- mSystemInterface.killPackageDependents(oldProviderName);
- }
- return;
- }
- }
- }
-
- void prepareWebViewInSystemServer() {
- try {
- synchronized(mLock) {
- mCurrentWebViewPackage = findPreferredWebViewPackage();
- String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
- if (userSetting != null
- && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
- // Don't persist the user-chosen setting across boots if the package being
- // chosen is not used (could be disabled or uninstalled) so that the user won't
- // be surprised by the device switching to using a certain webview package,
- // that was uninstalled/disabled a long time ago, if it is installed/enabled
- // again.
- mSystemInterface.updateUserSetting(mContext,
- mCurrentWebViewPackage.packageName);
- }
- onWebViewProviderChanged(mCurrentWebViewPackage);
- }
- } catch (Throwable t) {
- // Log and discard errors at this stage as we must not crash the system server.
- Slog.e(TAG, "error preparing webview provider from system server", t);
- }
- }
-
- /**
- * Change WebView provider and provider setting and kill packages using the old provider.
- * Return the new provider (in case we are in the middle of creating relro files, or
- * replacing that provider it will not be in use directly, but will be used when the relros
- * or the replacement are done).
- */
- String changeProviderAndSetting(String newProviderName) {
- PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
- if (newPackage == null) return "";
- return newPackage.packageName;
- }
-
- /**
- * Update the current WebView package.
- * @param newProviderName the package to switch to, null if no package has been explicitly
- * chosen.
- */
- PackageInfo updateCurrentWebViewPackage(String newProviderName) {
- PackageInfo oldPackage = null;
- PackageInfo newPackage = null;
- boolean providerChanged = false;
- synchronized(mLock) {
- oldPackage = mCurrentWebViewPackage;
-
- if (newProviderName != null) {
- mSystemInterface.updateUserSetting(mContext, newProviderName);
- }
-
- try {
- newPackage = findPreferredWebViewPackage();
- providerChanged = (oldPackage == null)
- || !newPackage.packageName.equals(oldPackage.packageName);
- } catch (WebViewPackageMissingException e) {
- // If updated the Setting but don't have an installed WebView package, the
- // Setting will be used when a package is available.
- mCurrentWebViewPackage = null;
- Slog.e(TAG, "Couldn't find WebView package to use " + e);
- return null;
- }
- // Perform the provider change if we chose a new provider
- if (providerChanged) {
- onWebViewProviderChanged(newPackage);
- }
- }
- // Kill apps using the old provider only if we changed provider
- if (providerChanged && oldPackage != null) {
- mSystemInterface.killPackageDependents(oldPackage.packageName);
- }
- // Return the new provider, this is not necessarily the one we were asked to switch to,
- // but the persistent setting will now be pointing to the provider we were asked to
- // switch to anyway.
- return newPackage;
- }
-
- /**
- * This is called when we change WebView provider, either when the current provider is
- * updated or a new provider is chosen / takes precedence.
- */
- private void onWebViewProviderChanged(PackageInfo newPackage) {
- synchronized(mLock) {
- mAnyWebViewInstalled = true;
- if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- mCurrentWebViewPackage = newPackage;
-
- // The relro creations might 'finish' (not start at all) before
- // WebViewFactory.onWebViewProviderChanged which means we might not know the
- // number of started creations before they finish.
- mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
- mNumRelroCreationsFinished = 0;
- mNumRelroCreationsStarted =
- mSystemInterface.onWebViewProviderChanged(newPackage);
- // If the relro creations finish before we know the number of started creations
- // we will have to do any cleanup/notifying here.
- checkIfRelrosDoneLocked();
- } else {
- mWebViewPackageDirty = true;
- }
- }
- }
-
- /**
- * Fetch only the currently valid WebView packages.
- **/
- WebViewProviderInfo[] getValidWebViewPackages() {
- ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
- WebViewProviderInfo[] providers =
- new WebViewProviderInfo[providersAndPackageInfos.length];
- for(int n = 0; n < providersAndPackageInfos.length; n++) {
- providers[n] = providersAndPackageInfos[n].provider;
- }
- return providers;
- }
-
- private static class ProviderAndPackageInfo {
- public final WebViewProviderInfo provider;
- public final PackageInfo packageInfo;
-
- public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
- this.provider = provider;
- this.packageInfo = packageInfo;
- }
- }
-
- private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
- WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
- List<ProviderAndPackageInfo> providers = new ArrayList<>();
- for(int n = 0; n < allProviders.length; n++) {
- try {
- PackageInfo packageInfo =
- mSystemInterface.getPackageInfoForProvider(allProviders[n]);
- if (isValidProvider(allProviders[n], packageInfo)) {
- providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
- }
- } catch (NameNotFoundException e) {
- // Don't add non-existent packages
- }
- }
- return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
- }
-
- /**
- * Returns either the package info of the WebView provider determined in the following way:
- * If the user has chosen a provider then use that if it is valid,
- * otherwise use the first package in the webview priority list that is valid.
- *
- */
- private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
- ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
-
- String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
-
- // If the user has chosen provider, use that (if it's installed and enabled for all
- // users).
- for (ProviderAndPackageInfo providerAndPackage : providers) {
- if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
- // userPackages can contain null objects.
- List<UserPackage> userPackages =
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
- providerAndPackage.provider);
- if (isInstalledAndEnabledForAllUsers(userPackages)) {
- return providerAndPackage.packageInfo;
- }
- }
- }
-
- // User did not choose, or the choice failed; use the most stable provider that is
- // installed and enabled for all users, and available by default (not through
- // user choice).
- for (ProviderAndPackageInfo providerAndPackage : providers) {
- if (providerAndPackage.provider.availableByDefault) {
- // userPackages can contain null objects.
- List<UserPackage> userPackages =
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
- providerAndPackage.provider);
- if (isInstalledAndEnabledForAllUsers(userPackages)) {
- return providerAndPackage.packageInfo;
- }
- }
- }
-
- // This should never happen during normal operation (only with modified system images).
- mAnyWebViewInstalled = false;
- throw new WebViewPackageMissingException("Could not find a loadable WebView package");
- }
-
- /**
- * Return true iff {@param packageInfos} point to only installed and enabled packages.
- * The given packages {@param packageInfos} should all be pointing to the same package, but each
- * PackageInfo representing a different user's package.
- */
- static boolean isInstalledAndEnabledForAllUsers(
- List<UserPackage> userPackages) {
- for (UserPackage userPackage : userPackages) {
- if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
- return false;
- }
- }
- return true;
- }
-
- void notifyRelroCreationCompleted() {
- synchronized (mLock) {
- mNumRelroCreationsFinished++;
- checkIfRelrosDoneLocked();
- }
- }
-
- WebViewProviderResponse waitForAndGetProvider() {
- PackageInfo webViewPackage = null;
- final long NS_PER_MS = 1000000;
- final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
- boolean webViewReady = false;
- int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
- synchronized (mLock) {
- webViewReady = webViewIsReadyLocked();
- while (!webViewReady) {
- final long timeNowMs = System.nanoTime() / NS_PER_MS;
- if (timeNowMs >= timeoutTimeMs) break;
- try {
- mLock.wait(timeoutTimeMs - timeNowMs);
- } catch (InterruptedException e) {}
- webViewReady = webViewIsReadyLocked();
- }
- // Make sure we return the provider that was used to create the relro file
- webViewPackage = mCurrentWebViewPackage;
- if (webViewReady) {
- } else if (!mAnyWebViewInstalled) {
- webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
- } else {
- // Either the current relro creation isn't done yet, or the new relro creatioin
- // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
- webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
- Slog.e(TAG, "Timed out waiting for relro creation, relros started "
- + mNumRelroCreationsStarted
- + " relros finished " + mNumRelroCreationsFinished
- + " package dirty? " + mWebViewPackageDirty);
- }
- }
- if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
- return new WebViewProviderResponse(webViewPackage, webViewStatus);
- }
-
- PackageInfo getCurrentWebViewPackage() {
- synchronized(mLock) {
- return mCurrentWebViewPackage;
- }
- }
-
- /**
- * Returns whether WebView is ready and is not going to go through its preparation phase
- * again directly.
- */
- private boolean webViewIsReadyLocked() {
- return !mWebViewPackageDirty
- && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
- // The current package might be replaced though we haven't received an intent
- // declaring this yet, the following flag makes anyone loading WebView to wait in
- // this case.
- && mAnyWebViewInstalled;
- }
-
- private void checkIfRelrosDoneLocked() {
- if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- if (mWebViewPackageDirty) {
- mWebViewPackageDirty = false;
- // If we have changed provider since we started the relro creation we need to
- // redo the whole process using the new package instead.
- try {
- PackageInfo newPackage = findPreferredWebViewPackage();
- onWebViewProviderChanged(newPackage);
- } catch (WebViewPackageMissingException e) {
- mCurrentWebViewPackage = null;
- // If we can't find any valid WebView package we are now in a state where
- // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
- // should simply wait until we receive an intent declaring a new package was
- // installed.
- }
- } else {
- mLock.notifyAll();
- }
- }
- }
-
- /**
- * Returns whether this provider is valid for use as a WebView provider.
- */
- boolean isValidProvider(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
- return VALIDITY_OK == validityResult(configInfo, packageInfo);
- }
-
- private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
- // Ensure the provider targets this framework release (or a later one).
- if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
- return VALIDITY_INCORRECT_SDK_VERSION;
- }
- if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
- && !mSystemInterface.systemIsDebuggable()) {
- // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
- // minimum version code. This check is only enforced for user builds.
- return VALIDITY_INCORRECT_VERSION_CODE;
- }
- if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
- return VALIDITY_INCORRECT_SIGNATURE;
- }
- if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
- return VALIDITY_NO_LIBRARY_FLAG;
- }
- return VALIDITY_OK;
- }
-
- /**
- * Both versionCodes should be from a WebView provider package implemented by Chromium.
- * VersionCodes from other kinds of packages won't make any sense in this method.
- *
- * An introduction to Chromium versionCode scheme:
- * "BBBBPPPAX"
- * BBBB: 4 digit branch number. It monotonically increases over time.
- * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
- * may change their meaning in the future.
- * A: architecture digit.
- * X: A digit to differentiate APKs for other reasons.
- *
- * This method takes the "BBBB" of versionCodes and compare them.
- *
- * @return true if versionCode1 is higher than or equal to versionCode2.
- */
- private static boolean versionCodeGE(long versionCode1, long versionCode2) {
- long v1 = versionCode1 / 100000;
- long v2 = versionCode2 / 100000;
-
- return v1 >= v2;
- }
-
- /**
- * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
- * of all available-by-default WebView provider packages. If there is no such WebView provider
- * package on the system, then return -1, which means all positive versionCode WebView packages
- * are accepted.
- *
- * Note that this is a private method in WebViewUpdater that handles a variable
- * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not
- * hold mLock meaning that we must take extra care to ensure this method is thread-safe.
- */
- private long getMinimumVersionCode() {
- if (mMinimumVersionCode > 0) {
- return mMinimumVersionCode;
- }
-
- long minimumVersionCode = -1;
- for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
- if (provider.availableByDefault) {
- try {
- long versionCode =
- mSystemInterface.getFactoryPackageVersion(provider.packageName);
- if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
- minimumVersionCode = versionCode;
- }
- } catch (NameNotFoundException e) {
- // Safe to ignore.
- }
- }
- }
-
- mMinimumVersionCode = minimumVersionCode;
- return mMinimumVersionCode;
- }
-
- private static boolean providerHasValidSignature(WebViewProviderInfo provider,
- PackageInfo packageInfo, SystemInterface systemInterface) {
- // Skip checking signatures on debuggable builds, for development purposes.
- if (systemInterface.systemIsDebuggable()) return true;
-
- // Allow system apps to be valid providers regardless of signature.
- if (packageInfo.applicationInfo.isSystemApp()) return true;
-
- // We don't support packages with multiple signatures.
- if (packageInfo.signatures.length != 1) return false;
-
- // If any of the declared signatures match the package signature, it's valid.
- for (Signature signature : provider.signatures) {
- if (signature.equals(packageInfo.signatures[0])) return true;
- }
-
- return false;
- }
-
- void dumpState(PrintWriter pw) {
- synchronized (mLock) {
- if (mCurrentWebViewPackage == null) {
- pw.println(" Current WebView package is null");
- } else {
- pw.println(String.format(" Current WebView package (name, version): (%s, %s)",
- mCurrentWebViewPackage.packageName,
- mCurrentWebViewPackage.versionName));
- }
- pw.println(String.format(" Minimum targetSdkVersion: %d",
- UserPackage.MINIMUM_SUPPORTED_SDK));
- pw.println(String.format(" Minimum WebView version code: %d",
- mMinimumVersionCode));
- pw.println(String.format(" Number of relros started: %d",
- mNumRelroCreationsStarted));
- pw.println(String.format(" Number of relros finished: %d",
- mNumRelroCreationsFinished));
- pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty));
- pw.println(String.format(" Any WebView package installed: %b",
- mAnyWebViewInstalled));
-
- try {
- PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
- pw.println(String.format(
- " Preferred WebView package (name, version): (%s, %s)",
- preferredWebViewPackage.packageName,
- preferredWebViewPackage.versionName));
- } catch (WebViewPackageMissingException e) {
- pw.println(String.format(" Preferred WebView package: none"));
- }
-
- dumpAllPackageInformationLocked(pw);
- }
- }
-
- private void dumpAllPackageInformationLocked(PrintWriter pw) {
- WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
- pw.println(" WebView packages:");
- for (WebViewProviderInfo provider : allProviders) {
- List<UserPackage> userPackages =
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
- PackageInfo systemUserPackageInfo =
- userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
- if (systemUserPackageInfo == null) {
- pw.println(String.format(" %s is NOT installed.", provider.packageName));
- continue;
- }
-
- int validity = validityResult(provider, systemUserPackageInfo);
- String packageDetails = String.format(
- "versionName: %s, versionCode: %d, targetSdkVersion: %d",
- systemUserPackageInfo.versionName,
- systemUserPackageInfo.getLongVersionCode(),
- systemUserPackageInfo.applicationInfo.targetSdkVersion);
- if (validity == VALIDITY_OK) {
- boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
- pw.println(String.format(
- " Valid package %s (%s) is %s installed/enabled for all users",
- systemUserPackageInfo.packageName,
- packageDetails,
- installedForAllUsers ? "" : "NOT"));
- } else {
- pw.println(String.format(" Invalid package %s (%s), reason: %s",
- systemUserPackageInfo.packageName,
- packageDetails,
- getInvalidityReason(validity)));
- }
- }
- }
-
- private static String getInvalidityReason(int invalidityReason) {
- switch (invalidityReason) {
- case VALIDITY_INCORRECT_SDK_VERSION:
- return "SDK version too low";
- case VALIDITY_INCORRECT_VERSION_CODE:
- return "Version code too low";
- case VALIDITY_INCORRECT_SIGNATURE:
- return "Incorrect signature";
- case VALIDITY_NO_LIBRARY_FLAG:
- return "No WebView-library manifest flag";
- default:
- return "Unexcepted validity-reason";
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4e07d8e1f5a8..b40154e8db0c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -179,6 +179,7 @@ import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.Task.ActivityState.DESTROYED;
import static com.android.server.wm.Task.ActivityState.DESTROYING;
@@ -621,6 +622,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
*/
private float mSizeCompatScale = 1f;
+
/**
* The bounds in global coordinates for activity in size compatibility mode.
* @see ActivityRecord#hasSizeCompatBounds()
@@ -4216,7 +4218,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Note that we ignore display frozen since we want the opening / closing transition type
// can be updated correctly even display frozen, and it's safe since in applyAnimation will
// still check DC#okToAnimate again if the transition animation is fine to apply.
- if (okToAnimate(true /* ignoreFrozen */) && appTransition.isTransitionSet()) {
+ final boolean recentsAnimating = isAnimating(PARENTS, ANIMATION_TYPE_RECENTS);
+ if (okToAnimate(true /* ignoreFrozen */) && (appTransition.isTransitionSet()
+ || (recentsAnimating && !isActivityTypeHome()))) {
if (visible) {
displayContent.mOpeningApps.add(this);
mEnteringAnimation = true;
@@ -6085,7 +6089,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
void prepareSurfaces() {
- final boolean show = isVisible() || isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION);
+ final boolean show = isVisible() || isAnimating(PARENTS,
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
@@ -6401,6 +6406,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLastReportedConfiguration.setConfiguration(global, override);
}
+ boolean hasCompatDisplayInsets() {
+ return mCompatDisplayInsets != null;
+ }
+
/**
* @return {@code true} if this activity is in size compatibility mode that uses the different
* density than its parent or its bounds don't fit in parent naturally.
@@ -6491,6 +6500,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& !mAtmService.mForceResizableActivities;
}
+ @Override
boolean hasSizeCompatBounds() {
return mSizeCompatBounds != null;
}
@@ -6538,7 +6548,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mSizeCompatScale = 1f;
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
- onRequestedOverrideConfigurationChanged(EMPTY);
+
+ // Recompute from Task because letterbox can also happen on Task level.
+ task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration());
}
@Override
@@ -6647,9 +6659,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
? requestedOrientation
: newParentConfiguration.orientation;
int rotation = newParentConfiguration.windowConfiguration.getRotation();
- final boolean canChangeOrientation = handlesOrientationChangeFromDescendant();
- if (canChangeOrientation && !mCompatDisplayInsets.mIsFloating) {
- // Use parent rotation because the original display can rotate by requested orientation.
+ final boolean isFixedToUserRotation = mDisplayContent == null
+ || mDisplayContent.getDisplayRotation().isFixedToUserRotation();
+ if (!isFixedToUserRotation && !mCompatDisplayInsets.mIsFloating) {
+ // Use parent rotation because the original display can be rotated.
resolvedConfig.windowConfiguration.setRotation(rotation);
} else {
final int overrideRotation = resolvedConfig.windowConfiguration.getRotation();
@@ -6665,7 +6678,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect containingAppBounds = new Rect();
final Rect containingBounds = mTmpBounds;
mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
- orientation, orientationRequested, canChangeOrientation);
+ orientation, orientationRequested, isFixedToUserRotation);
resolvedBounds.set(containingBounds);
// The size of floating task is fixed (only swap), so the aspect ratio is already correct.
if (!mCompatDisplayInsets.mIsFloating) {
@@ -7734,7 +7747,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect[] mStableInsets = new Rect[4];
/** Constructs the environment to simulate the bounds behavior of the given container. */
- CompatDisplayInsets(DisplayContent display, WindowContainer container) {
+ CompatDisplayInsets(DisplayContent display, ActivityRecord container) {
mIsFloating = container.getWindowConfiguration().tasksAreFloating();
if (mIsFloating) {
final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -7750,16 +7763,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
- // If the activity is not floating, assume it fills the display.
- mWidth = display.mBaseDisplayWidth;
- mHeight = display.mBaseDisplayHeight;
+ if (container.getTask().isTaskLetterboxed()) {
+ // For apps in Task letterbox, it should fill the task bounds.
+ final Rect taskBounds = container.getTask().getBounds();
+ mWidth = taskBounds.width();
+ mHeight = taskBounds.height();
+ } else {
+ // If the activity is not floating nor letterboxed, assume it fills the display.
+ mWidth = display.mBaseDisplayWidth;
+ mHeight = display.mBaseDisplayHeight;
+ }
final DisplayPolicy policy = display.getDisplayPolicy();
for (int rotation = 0; rotation < 4; rotation++) {
mNonDecorInsets[rotation] = new Rect();
mStableInsets[rotation] = new Rect();
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final int dw = rotated ? mHeight : mWidth;
- final int dh = rotated ? mWidth : mHeight;
+ final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
+ final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
final DisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation)
.getDisplayCutout();
policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
@@ -7785,7 +7805,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Gets the horizontal centered container bounds for size compatibility mode. */
void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation,
- boolean orientationRequested, boolean canChangeOrientation) {
+ boolean orientationRequested, boolean isFixedToUserRotation) {
getFrameByOrientation(outBounds, orientation);
if (mIsFloating) {
outAppBounds.set(outBounds);
@@ -7798,7 +7818,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean isOrientationMismatched =
((outBounds.width() > outBounds.height()) != (dW > dH));
- if (isOrientationMismatched && !canChangeOrientation && orientationRequested) {
+ if (isOrientationMismatched && isFixedToUserRotation && orientationRequested) {
// The orientation is mismatched but the display cannot rotate. The bounds will fit
// to the short side of container.
if (orientation == ORIENTATION_LANDSCAPE) {
@@ -7876,4 +7896,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pictureInPictureArgs.copyOnlySet(p);
getTask().getRootTask().onPictureInPictureParamsChanged();
}
+
+ @Override
+ boolean isSyncFinished() {
+ if (!super.isSyncFinished()) return false;
+ if (!isVisibleRequested()) return true;
+ // If visibleRequested, wait for at-least one visible child.
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).isVisibleRequested()) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 9ed68b8041db..a8079cfa1c85 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2040,6 +2040,11 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
if (topStack == null || topStack.mResumedActivity == prevTopActivity) {
+ if (mService.isSleepingLocked()) {
+ // There won't be a next resumed activity. The top process should still be updated
+ // according to the current top focused activity.
+ mService.updateTopApp(null /* topResumedActivity */);
+ }
return;
}
@@ -2159,21 +2164,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
final DisplayContent preferredDisplay = preferredTaskDisplayArea.mDisplayContent;
-
- final boolean singleTaskInstance = preferredDisplay != null
- && preferredDisplay.isSingleTaskInstance();
-
if (preferredDisplay != task.getDisplayContent()) {
- // Suppress the warning toast if the preferredDisplay was set to singleTask.
- // The singleTaskInstance displays will only contain one task and any attempt to
- // launch new task will re-route to the default display.
- if (singleTaskInstance) {
- mService.getTaskChangeNotificationController()
- .notifyActivityLaunchOnSecondaryDisplayRerouted(task.getTaskInfo(),
- preferredDisplay.mDisplayId);
- return;
- }
-
Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplay.mDisplayId);
// Display a warning toast that we failed to put a task on a secondary display.
mService.getTaskChangeNotificationController()
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 96740128879b..c7d716d8290a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -173,13 +173,6 @@ public abstract class ActivityTaskManagerInternal {
public abstract boolean hasResumedActivity(int uid);
/**
- * Notify listeners that contents are drawn for the first time on a single task display.
- *
- * @param displayId An ID of the display on which contents are drawn.
- */
- public abstract void notifySingleTaskDisplayDrawn(int displayId);
-
- /**
* Start activity {@code intents} as if {@code packageName/featureId} on user {@code userId} did
* it.
*
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 99b017f0277f..49d9e9559ad9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2507,13 +2507,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (taskOrgController.handleInterceptBackPressedOnTaskRoot(stack)) {
// This task is handled by a task organizer that has requested the back pressed
// callback
- } else if (stack != null && (stack.isSingleTaskInstance())) {
- // Single-task stacks are used for activities which are presented in floating
- // windows above full screen activities. A task change listener is used to notify
- // SystemUI so the back action can be handled specially.
- final Task task = r.getTask();
- mTaskChangeNotificationController
- .notifyBackPressedOnTaskRoot(task.getTaskInfo());
} else {
moveActivityTaskToBack(token, false /* nonRoot */);
}
@@ -4782,26 +4775,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/**
- * Makes the display with the given id a single task instance display. I.e the display can only
- * contain one task.
- */
- @Override
- public void setDisplayToSingleTaskInstance(int displayId) {
- mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS,
- "setDisplayToSingleTaskInstance");
- final long origId = Binder.clearCallingIdentity();
- try {
- final DisplayContent display =
- mRootWindowContainer.getDisplayContentOrCreate(displayId);
- if (display != null) {
- display.setDisplayToSingleTaskInstance();
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- /**
* Requests that an activity should enter picture-in-picture mode if possible.
*/
@Override
@@ -5136,7 +5109,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
deferResume);
}
- kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ if (!deferResume) {
+ kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ }
} finally {
continueWindowLayout();
}
@@ -5572,11 +5547,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
void updateTopApp(ActivityRecord topResumedActivity) {
- // If system is sleeping, use the given record (it should be null) because there won't be
- // the next resumed activity. Otherwise the process of pausing activity will keep with top
- // state even the activity has paused and stopped.
- final ActivityRecord top = mSleeping || topResumedActivity != null ? topResumedActivity
- // If there is no resumed activity, it will choose the pausing activity.
+ final ActivityRecord top = topResumedActivity != null ? topResumedActivity
+ // If there is no resumed activity, it will choose the pausing or focused activity.
: mRootWindowContainer.getTopResumedActivity();
mTopApp = top != null ? top.app : null;
}
@@ -6033,10 +6005,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return allUids.contains(uid);
}
- void notifySingleTaskDisplayEmpty(int displayId) {
- mTaskChangeNotificationController.notifySingleTaskDisplayEmpty(displayId);
- }
-
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
@@ -6104,11 +6072,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void notifySingleTaskDisplayDrawn(int displayId) {
- mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
- }
-
- @Override
public List<IBinder> getTopVisibleActivities() {
synchronized (mGlobalLock) {
return mRootWindowContainer.getTopVisibleActivities();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 6caf9162b7a8..26c701b99150 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -29,7 +29,6 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPE
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -2040,9 +2039,6 @@ public class AppTransition implements Dump {
case TRANSIT_CRASHING_ACTIVITY_CLOSE: {
return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
}
- case TRANSIT_SHOW_SINGLE_TASK_DISPLAY: {
- return "TRANSIT_SHOW_SINGLE_TASK_DISPLAY";
- }
default: {
return "<UNKNOWN: " + transition + ">";
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6b8a3e22795e..14eedf786f4c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_W
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
@@ -215,12 +214,6 @@ public class AppTransitionController {
mService.mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
mTempTransitionReasons);
- if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
- mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
- mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
- });
- }
-
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mDisplayContent.pendingLayoutChanges |=
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 958a7a8f07f8..301783c155e1 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -18,13 +18,13 @@ package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
-import android.util.ArrayMap;
+import android.annotation.NonNull;
import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
import com.android.internal.protolog.common.ProtoLog;
-import java.util.Set;
-
/**
* Utility class for collecting WindowContainers that will merge transactions.
* For example to use to synchronously resize all the children of a window container
@@ -45,94 +45,122 @@ import java.util.Set;
* 5. If there were no sub windows anywhere in the hierarchy to wait on, then
* transactionReady is immediately invoked, otherwise all the windows are poked
* to redraw and to deliver a buffer to {@link WindowState#finishDrawing}.
- * Once all this drawing is complete the WindowContainer that's ready will be added to the
- * set of ready WindowContainers. When the final onTransactionReady is called, it will merge
- * the transactions of the all the WindowContainers and will be delivered to the
- * TransactionReadyListener
+ * Once all this drawing is complete, all the transactions will be merged and delivered
+ * to TransactionReadyListener.
+ *
+ * This works primarily by setting-up state and then watching/waiting for the registered subtrees
+ * to enter into a "finished" state (either by receiving drawn content or by disappearing). This
+ * checks the subtrees during surface-placement.
*/
class BLASTSyncEngine {
private static final String TAG = "BLASTSyncEngine";
interface TransactionReadyListener {
- void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady);
- };
-
- // Holds state associated with a single synchronous set of operations.
- class SyncState implements TransactionReadyListener {
- int mSyncId;
- int mRemainingTransactions;
- TransactionReadyListener mListener;
+ void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
+ }
+
+ /**
+ * Holds state associated with a single synchronous set of operations.
+ */
+ class SyncGroup {
+ final int mSyncId;
+ final TransactionReadyListener mListener;
boolean mReady = false;
- Set<WindowContainer> mWindowContainersReady = new ArraySet<>();
-
- private void tryFinish() {
- if (mRemainingTransactions == 0 && mReady) {
- ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Finished. Reporting %d "
- + "containers to %s", BLASTSyncEngine.this.hashCode(), mSyncId,
- mWindowContainersReady.size(), mListener);
- mListener.onTransactionReady(mSyncId, mWindowContainersReady);
- mPendingSyncs.remove(mSyncId);
+ final ArraySet<WindowContainer> mRootMembers = new ArraySet<>();
+ private SurfaceControl.Transaction mOrphanTransaction = null;
+
+ private SyncGroup(TransactionReadyListener listener, int id) {
+ mSyncId = id;
+ mListener = listener;
+ }
+
+ /**
+ * Gets a transaction to dump orphaned operations into. Orphaned operations are operations
+ * that were on the mSyncTransactions of "root" subtrees which have been removed during the
+ * sync period.
+ */
+ @NonNull
+ SurfaceControl.Transaction getOrphanTransaction() {
+ if (mOrphanTransaction == null) {
+ // Lazy since this isn't common
+ mOrphanTransaction = mWm.mTransactionFactory.get();
}
+ return mOrphanTransaction;
}
- public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
- mRemainingTransactions--;
- ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Child ready, now ready=%b"
- + " and waiting on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId,
- mReady, mRemainingTransactions);
- mWindowContainersReady.addAll(windowContainersReady);
- tryFinish();
+ private void onSurfacePlacement() {
+ if (!mReady) return;
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: onSurfacePlacement checking %s",
+ mSyncId, mRootMembers);
+ for (int i = mRootMembers.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = mRootMembers.valueAt(i);
+ if (!wc.isSyncFinished()) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Unfinished container: %s",
+ mSyncId, wc);
+ return;
+ }
+ }
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Finished!", mSyncId);
+ SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
+ if (mOrphanTransaction != null) {
+ merged.merge(mOrphanTransaction);
+ }
+ for (WindowContainer wc : mRootMembers) {
+ wc.finishSync(merged, false /* cancel */);
+ }
+ mListener.onTransactionReady(mSyncId, merged);
+ mActiveSyncs.remove(mSyncId);
}
- void setReady() {
- ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Set ready",
- BLASTSyncEngine.this.hashCode(), mSyncId);
+ private void setReady() {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
mReady = true;
- tryFinish();
+ mWm.mWindowPlacerLocked.requestTraversal();
}
- boolean addToSync(WindowContainer wc) {
- ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Trying to add %s",
- BLASTSyncEngine.this.hashCode(), mSyncId, wc);
- if (wc.prepareForSync(this, mSyncId)) {
- mRemainingTransactions++;
- ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Added %s. now waiting "
- + "on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId, wc,
- mRemainingTransactions);
- return true;
+ private void addToSync(WindowContainer wc) {
+ if (!mRootMembers.add(wc)) {
+ return;
}
- return false;
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Adding to group: %s", mSyncId, wc);
+ wc.setSyncGroup(this);
+ wc.prepareSync();
+ mWm.mWindowPlacerLocked.requestTraversal();
}
- SyncState(TransactionReadyListener l, int id) {
- mListener = l;
- mSyncId = id;
- mRemainingTransactions = 0;
+ void onCancelSync(WindowContainer wc) {
+ mRootMembers.remove(wc);
}
- };
+ }
+ private final WindowManagerService mWm;
private int mNextSyncId = 0;
+ private final SparseArray<SyncGroup> mActiveSyncs = new SparseArray<>();
- private final ArrayMap<Integer, SyncState> mPendingSyncs = new ArrayMap<>();
-
- BLASTSyncEngine() {
+ BLASTSyncEngine(WindowManagerService wms) {
+ mWm = wms;
}
int startSyncSet(TransactionReadyListener listener) {
final int id = mNextSyncId++;
- final SyncState s = new SyncState(listener, id);
- mPendingSyncs.put(id, s);
- ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Start for %s", hashCode(), id, listener);
+ final SyncGroup s = new SyncGroup(listener, id);
+ mActiveSyncs.put(id, s);
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s", id, listener);
return id;
}
- boolean addToSyncSet(int id, WindowContainer wc) {
- final SyncState st = mPendingSyncs.get(id);
- return st.addToSync(wc);
+ void addToSyncSet(int id, WindowContainer wc) {
+ mActiveSyncs.get(id).addToSync(wc);
}
void setReady(int id) {
- final SyncState st = mPendingSyncs.get(id);
- st.setReady();
+ mActiveSyncs.get(id).setReady();
+ }
+
+ void onSurfacePlacement() {
+ // backwards since each state can remove itself if finished
+ for (int i = mActiveSyncs.size() - 1; i >= 0; --i) {
+ mActiveSyncs.valueAt(i).onSurfacePlacement();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 38ad0706d949..bb838bae2aff 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -145,6 +145,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return super.getOrientation(candidate);
}
+ @Override
+ boolean handlesOrientationChangeFromDescendant() {
+ return !mIgnoreOrientationRequest && super.handlesOrientationChangeFromDescendant();
+ }
+
/**
* Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and
* windows below it.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4a30ca96abb6..98a1bacf27e0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -109,7 +109,6 @@ import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -605,9 +604,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private boolean mRemoved;
- /** The display can only contain one task. */
- boolean mSingleTaskInstance;
-
/**
* Non-null if the last size compatibility mode activity is using non-native screen
* configuration. The activity is not able to put in multi-window mode, so it exists only one
@@ -1313,7 +1309,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// If display rotation class tells us that it doesn't consider app requested orientation,
// this display won't rotate just because of an app changes its requested orientation. Thus
// it indicates that this display chooses not to handle this request.
- final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+ final boolean handled = handlesOrientationChangeFromDescendant();
if (config == null) {
return handled;
}
@@ -1337,7 +1333,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
boolean handlesOrientationChangeFromDescendant() {
- return getDisplayRotation().respectAppRequestedOrientation();
+ return !mIgnoreOrientationRequest && !getDisplayRotation().isFixedToUserRotation();
}
/**
@@ -2349,7 +2345,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
int getOrientation() {
mLastOrientationSource = null;
- if (mIgnoreOrientationRequest) {
+ if (!handlesOrientationChangeFromDescendant()) {
// Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Display id=%d is ignoring all orientation requests, return %d",
@@ -2890,7 +2886,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
}
- proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
final Task focusedStack = getFocusedStack();
if (focusedStack != null) {
proto.write(FOCUSED_ROOT_TASK_ID, focusedStack.getRootTaskId());
@@ -2935,8 +2930,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
pw.print(prefix);
- pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getStackCount() + (
- mSingleTaskInstance ? " mSingleTaskInstance" : ""));
+ pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getStackCount());
final String subPrefix = " " + prefix;
pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
@@ -5077,7 +5071,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ if (!deferResume) {
+ kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ }
} finally {
mAtmService.continueWindowLayout();
}
@@ -5303,29 +5299,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mSleeping = asleep;
}
- void setDisplayToSingleTaskInstance() {
- int tdaCount = reduceOnAllTaskDisplayAreas((taskDisplayArea, count) -> ++count,
- 0 /* initValue */);
- if (tdaCount > 1) {
- throw new IllegalArgumentException(
- "Display already has multiple task display areas. display=" + this);
- }
- final int stackCount = getDefaultTaskDisplayArea().getStackCount();
- if (stackCount > 1) {
- throw new IllegalArgumentException("Display already has multiple stacks. display="
- + this);
- }
- if (stackCount > 0) {
- final Task stack = getDefaultTaskDisplayArea().getStackAt(0);
- if (stack.getChildCount() > 1) {
- throw new IllegalArgumentException("Display stack already has multiple tasks."
- + " display=" + this + " stack=" + stack);
- }
- }
-
- mSingleTaskInstance = true;
- }
-
/**
* Check if the display has {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
*/
@@ -5334,11 +5307,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
}
- /** Returns true if the display can only contain one task */
- boolean isSingleTaskInstance() {
- return mSingleTaskInstance;
- }
-
@VisibleForTesting
void removeAllTasks() {
forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0801f68681ab..b0ddb61d34bd 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -188,7 +188,6 @@ import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
-import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -1439,25 +1438,15 @@ public class DisplayPolicy {
* @param attrs The LayoutParams of the window.
* @param windowToken The token of the window.
* @param outFrame The frame of the window.
- * @param outContentInsets The areas covered by system windows, expressed as positive insets.
- * @param outStableInsets The areas covered by stable system windows irrespective of their
- * current visibility. Expressed as positive insets.
* @param outDisplayCutout The area that has been cut away from the display.
+ * @param outInsetsState The insets state of this display from the client's perspective.
+ * @param localClient Whether the client is from the our process.
* @return Whether to always consume the system bars.
* See {@link #areSystemBarsForcedShownLw(WindowState)}.
*/
boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout) {
- final int fl = attrs.flags;
- final int pfl = attrs.privateFlags;
- final int sysUiVis = attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
-
- final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
- final boolean layoutInScreenAndInsetDecor = layoutInScreen
- && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
- final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
-
+ DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState,
+ boolean localClient) {
final boolean isFixedRotationTransforming =
windowToken != null && windowToken.isFixedRotationTransforming();
final ActivityRecord activity = windowToken != null ? windowToken.asActivityRecord() : null;
@@ -1466,56 +1455,36 @@ public class DisplayPolicy {
// Use token (activity) bounds if it is rotated because its task is not rotated.
? windowToken.getBounds()
: (task != null ? task.getBounds() : null);
+ final InsetsState state =
+ mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
+ computeWindowBounds(attrs, state, outFrame);
+ if (taskBounds != null) {
+ outFrame.intersect(taskBounds);
+ }
+
+ final int fl = attrs.flags;
+ final int pfl = attrs.privateFlags;
+ final boolean layoutInScreenAndInsetDecor = (fl & FLAG_LAYOUT_IN_SCREEN) != 0
+ && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
+ final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
final DisplayFrames displayFrames = isFixedRotationTransforming
? windowToken.getFixedRotationTransformDisplayFrames()
: mDisplayContent.mDisplayFrames;
-
if (layoutInScreenAndInsetDecor && !screenDecor) {
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- || (attrs.getFitInsetsTypes() & Type.navigationBars()) == 0) {
- outFrame.set(displayFrames.mUnrestricted);
- } else {
- outFrame.set(displayFrames.mRestricted);
- }
-
- final boolean isFloatingTask = task != null && task.isFloating();
- final Rect sf = isFloatingTask ? null : displayFrames.mStable;
- final Rect cf;
- if (isFloatingTask) {
- cf = null;
- } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
- if ((fl & FLAG_FULLSCREEN) != 0) {
- cf = displayFrames.mStableFullscreen;
- } else {
- cf = displayFrames.mStable;
- }
- } else if ((fl & FLAG_FULLSCREEN) != 0) {
- cf = displayFrames.mUnrestricted;
- } else {
- cf = displayFrames.mCurrent;
- }
-
- if (taskBounds != null) {
- outFrame.intersect(taskBounds);
- }
- InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
- InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
- outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
- .getDisplayCutout());
+ outDisplayCutout.set(
+ displayFrames.mDisplayCutout.calculateRelativeTo(outFrame).getDisplayCutout());
} else {
- if (layoutInScreen) {
- outFrame.set(displayFrames.mUnrestricted);
- } else {
- outFrame.set(displayFrames.mStable);
- }
- if (taskBounds != null) {
- outFrame.intersect(taskBounds);
- }
-
- outContentInsets.setEmpty();
- outStableInsets.setEmpty();
outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
}
+
+ final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
+ outInsetsState.set(state, inSizeCompatMode || localClient);
+ if (inSizeCompatMode) {
+ final float compatScale = windowToken != null
+ ? windowToken.getSizeCompatScale()
+ : mDisplayContent.mCompatibleScreenScale;
+ outInsetsState.scale(1f / compatScale);
+ }
return mForceShowSystemBars;
}
@@ -1616,8 +1585,9 @@ public class DisplayPolicy {
*/
public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
displayFrames.onBeginLayout();
- updateInsetsStateForDisplayCutout(displayFrames,
- mDisplayContent.getInsetsStateController().getRawInsetsState());
+ final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+ updateInsetsStateForDisplayCutout(displayFrames, state);
+ state.setDisplayFrame(displayFrames.mUnrestricted);
mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
@@ -1981,6 +1951,28 @@ public class DisplayPolicy {
return !notFocusableForIm;
}
+ private void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
+ Rect outBounds) {
+ final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
+ final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
+ final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
+ final Rect dfu = state.getDisplayFrame();
+ Insets insets = Insets.of(0, 0, 0, 0);
+ for (int i = types.size() - 1; i >= 0; i--) {
+ final InsetsSource source = state.peekSource(types.valueAt(i));
+ if (source == null) {
+ continue;
+ }
+ insets = Insets.max(insets, source.calculateInsets(
+ dfu, attrs.isFitInsetsIgnoringVisibility()));
+ }
+ final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
+ final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
+ final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
+ final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
+ outBounds.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
+ }
+
/**
* Called for each window attached to the window manager as layout is proceeding. The
* implementation of this function must take care of setting the window's frame, either here or
@@ -2027,31 +2019,12 @@ public class DisplayPolicy {
sf.set(displayFrames.mStable);
- final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
- final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
- final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
- getRotatedWindowBounds(displayFrames, win, sTmpRect);
- final Rect dfu = sTmpRect;
- Insets insets = Insets.of(0, 0, 0, 0);
- for (int i = types.size() - 1; i >= 0; i--) {
- final InsetsSource source = mDisplayContent.getInsetsPolicy()
- .getInsetsForDispatch(win).peekSource(types.valueAt(i));
- if (source == null) {
- continue;
- }
- insets = Insets.max(insets, source.calculateInsets(
- dfu, attrs.isFitInsetsIgnoringVisibility()));
- }
- final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
- final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
- final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
- final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
- df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
+ final InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(win);
+ computeWindowBounds(attrs, state, df);
if (attached == null) {
pf.set(df);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
- final InsetsSource source = mDisplayContent.getInsetsPolicy()
- .getInsetsForDispatch(win).peekSource(ITYPE_IME);
+ final InsetsSource source = state.peekSource(ITYPE_IME);
if (source != null) {
pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */));
}
@@ -2126,6 +2099,7 @@ public class DisplayPolicy {
// They will later be cropped or shifted using the displayFrame in WindowState,
// which prevents overlap with the DisplayCutout.
if (!attachedInParent && !floatingInScreenWindow) {
+ getRotatedWindowBounds(displayFrames, win, sTmpRect);
sTmpRect.set(pf);
pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index c5034316c101..c4aaf7c8a935 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -857,15 +857,6 @@ public class DisplayRotation {
return mFixedToUserRotation;
}
- /**
- * Returns {@code true} if this display rotation takes app requested orientation into
- * consideration; {@code false} otherwise. For the time being the only case where this is {@code
- * false} is when {@link #isFixedToUserRotation()} is {@code true}.
- */
- boolean respectAppRequestedOrientation() {
- return !isFixedToUserRotation();
- }
-
public int getLandscapeRotation() {
return mLandscapeRotation;
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bd05da9fe50a..a2b9cf9c5b3c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -28,6 +28,7 @@ import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.util.IntArray;
@@ -202,10 +203,22 @@ class InsetsPolicy {
}
/**
- * @see InsetsStateController#getInsetsForDispatch
+ * @see InsetsStateController#getInsetsForWindow
*/
- InsetsState getInsetsForDispatch(WindowState target) {
- final InsetsState originalState = mStateController.getInsetsForDispatch(target);
+ InsetsState getInsetsForWindow(WindowState target) {
+ final InsetsState originalState = mStateController.getInsetsForWindow(target);
+ return adjustVisibilityForTransientTypes(originalState);
+ }
+
+ /**
+ * @see InsetsStateController#getInsetsForWindowMetrics
+ */
+ InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
+ final InsetsState originalState = mStateController.getInsetsForWindowMetrics(attrs);
+ return adjustVisibilityForTransientTypes(originalState);
+ }
+
+ private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) {
InsetsState state = originalState;
for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
final @InternalInsetsType int type = mShowingTransientTypes.get(i);
@@ -481,7 +494,7 @@ class InsetsPolicy {
mFocusedWin.getDisplayContent().getBounds(), mFocusedWin.getInsetsState(),
mListener, typesReady, this, mListener.getDurationMs(),
InsetsController.SYSTEM_BARS_INTERPOLATOR,
- show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
+ show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, null /* translator */);
SurfaceAnimationThread.getHandler().post(
() -> mListener.onReady(mAnimationControl, typesReady));
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index b9c2093fe435..e7f140f989cc 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -97,14 +97,16 @@ class InsetsStateController {
}
/**
- * When dispatching window state to the client, we'll need to exclude the source that represents
- * the window that is being dispatched. We also need to exclude certain types of insets source
- * for client within specific windowing modes.
+ * Gets the insets state from the perspective of the target. When performing layout of the
+ * target or dispatching insets to the target, we need to exclude sources which should not be
+ * visible to the target. e.g., the source which represents the target window itself, and the
+ * IME source when the target is above IME. We also need to exclude certain types of insets
+ * source for client within specific windowing modes.
*
- * @param target The client we dispatch the state to.
+ * @param target The window associate with the perspective.
* @return The state stripped of the necessary information.
*/
- InsetsState getInsetsForDispatch(@NonNull WindowState target) {
+ InsetsState getInsetsForWindow(@NonNull WindowState target) {
final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState();
if (rotatedState != null) {
return rotatedState;
@@ -112,17 +114,23 @@ class InsetsStateController {
final InsetsSourceProvider provider = target.getControllableInsetProvider();
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
- return getInsetsForDispatchInner(type, target.getWindowingMode(), target.isAlwaysOnTop(),
+ return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
isAboveIme(target));
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
+ if (token != null) {
+ final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
+ if (rotatedState != null) {
+ return rotatedState;
+ }
+ }
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
- return getInsetsForDispatchInner(type, windowingMode, alwaysOnTop, isAboveIme(token));
+ return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token));
}
private boolean isAboveIme(WindowContainer target) {
@@ -165,8 +173,11 @@ class InsetsStateController {
return ITYPE_INVALID;
}
- /** @see #getInsetsForDispatch */
- private InsetsState getInsetsForDispatchInner(@InternalInsetsType int type,
+ /**
+ * @see #getInsetsForWindow
+ * @see #getInsetsForWindowMetrics
+ */
+ private InsetsState getInsetsForTarget(@InternalInsetsType int type,
@WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
InsetsState state = mState;
@@ -270,7 +281,6 @@ class InsetsStateController {
* Called when a layout pass has occurred.
*/
void onPostLayout() {
- mState.setDisplayFrame(mDisplayContent.getBounds());
for (int i = mProviders.size() - 1; i >= 0; i--) {
mProviders.valueAt(i).onPostLayout();
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index d29258049acc..0503c0de5195 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1378,14 +1378,6 @@ class RecentTasks {
break;
}
- // Tasks managed by/associated with an ActivityView should be excluded from recents.
- // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
- // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
- final Task rootTask = task.getRootTask();
- if (rootTask != null && rootTask.isSingleTaskInstance()) {
- return false;
- }
-
// If we're in lock task mode, ignore the root task
if (task == mService.getLockTaskController().getRootTask()) {
return false;
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 9181a0fb0734..e89e59cb8350 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -231,11 +231,6 @@ class ResetTargetTaskHelper {
final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
TaskDisplayArea taskDisplayArea = mTargetStack.getDisplayArea();
- final boolean singleTaskInstanceDisplay =
- taskDisplayArea.mDisplayContent.isSingleTaskInstance();
- if (singleTaskInstanceDisplay) {
- taskDisplayArea = atmService.mRootWindowContainer.getDefaultTaskDisplayArea();
- }
final int windowingMode = mTargetStack.getWindowingMode();
final int activityType = mTargetStack.getActivityType();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 7e76e7553a3b..d5ad791046d7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -38,7 +38,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PER
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -512,6 +511,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
void onChildPositionChanged(WindowContainer child) {
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
!mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
+ mStackSupervisor.updateTopResumedActivityIfNeeded();
}
/**
@@ -864,6 +864,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
+ mWmService.mSyncEngine.onSurfacePlacement();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
@@ -2131,13 +2132,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
+ displayId);
}
- if (displayContent.isSingleTaskInstance() && displayContent.getStackCount() > 0) {
- // We don't allow moving stacks to single instance display that already has a child.
- Slog.e(TAG, "Can not move stackId=" + stackId
- + " to single task instance display=" + displayContent);
- return;
- }
-
moveStackToTaskDisplayArea(stackId, displayContent.getDefaultTaskDisplayArea(), onTop);
}
@@ -2418,21 +2412,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (displayShouldSleep) {
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
- // When the display which can only contain one task turns on, start a
- // special transition.
- // {@link AppTransitionController#handleAppTransitionReady} later picks up
- // the transition, and schedules
- // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
- // triggered after contents are drawn on the display.
- if (display.isSingleTaskInstance()) {
- display.mDisplayContent.prepareAppTransition(
- TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false,
- 0 /* flags */, true /* forceOverride*/);
- }
stack.awakeFromSleepingLocked();
- if (display.isSingleTaskInstance()) {
- display.executeAppTransition();
- }
if (stack.isFocusedStackOnDisplay()
&& !mStackSupervisor.getKeyguardController()
.isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2647,12 +2627,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
+ " in=" + taskDisplayArea);
}
- @Override
- void positionChildAt(int position, DisplayContent child, boolean includingParents) {
- super.positionChildAt(position, child, includingParents);
- mStackSupervisor.updateTopResumedActivityIfNeeded();
- }
-
Configuration getDisplayOverrideConfiguration(int displayId) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
if (displayContent == null) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1b887a7c9172..70dbf683d6ec 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -165,36 +165,32 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
- UserHandle.getUserId(mUid), requestedVisibility, outFrame,
- outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
- outInsetsState, outActiveControls);
+ UserHandle.getUserId(mUid), requestedVisibility, outFrame, outDisplayCutout,
+ outInputChannel, outInsetsState, outActiveControls);
}
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
- Rect outFrame, Rect outContentInsets, Rect outStableInsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
- InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
+ Rect outFrame, DisplayCutout.ParcelableWrapper outDisplayCutout,
+ InputChannel outInputChannel, InsetsState outInsetsState,
+ InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
- requestedVisibility, outFrame, outContentInsets, outStableInsets, outDisplayCutout,
- outInputChannel, outInsetsState, outActiveControls);
+ requestedVisibility, outFrame, outDisplayCutout, outInputChannel, outInsetsState,
+ outActiveControls);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
- InsetsState outInsetsState) {
+ int viewVisibility, int displayId, InsetsState outInsetsState) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
UserHandle.getUserId(mUid), mDummyRequestedVisibility,
- new Rect() /* outFrame */, outContentInsets, outStableInsets,
- new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
- outInsetsState, mDummyControls);
+ new Rect() /* outFrame */, new DisplayCutout.ParcelableWrapper() /* cutout */,
+ null /* outInputChannel */, outInsetsState, mDummyControls);
}
@Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ecee46e895d0..4b12fe1c5e38 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -67,7 +67,6 @@ 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;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -2906,6 +2905,11 @@ class Task extends WindowContainer<WindowContainer> {
return;
}
+ if (refActivity != null && refActivity.hasCompatDisplayInsets()) {
+ // App prefers to keep its original size.
+ return;
+ }
+
final int parentWidth = parentBounds.width();
final int parentHeight = parentBounds.height();
final float aspect = ((float) parentHeight) / parentWidth;
@@ -3204,10 +3208,6 @@ class Task extends WindowContainer<WindowContainer> {
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask");
- if (mDisplayContent != null && mDisplayContent.isSingleTaskInstance()) {
- mAtmService.notifySingleTaskDisplayEmpty(mDisplayContent.mDisplayId);
- }
-
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
@@ -3799,6 +3799,10 @@ class Task extends WindowContainer<WindowContainer> {
|| activityType == ACTIVITY_TYPE_ASSISTANT;
}
+ boolean isTaskLetterboxed() {
+ return getWindowingMode() == WINDOWING_MODE_FULLSCREEN && !matchParentBounds();
+ }
+
@Override
boolean fillsParent() {
// From the perspective of policy, we still want to report that this task fills parent
@@ -3945,7 +3949,7 @@ class Task extends WindowContainer<WindowContainer> {
if (control != null) {
// We let the transition to be controlled by RecentsAnimation, and callback task's
// RemoteAnimationTarget for remote runner to animate.
- if (enter) {
+ if (enter && !isHomeOrRecentsStack()) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"applyAnimationUnchecked, control: %s, task: %s, transit: %s",
control, asTask(), AppTransition.appTransitionToString(transit));
@@ -5196,11 +5200,6 @@ class Task extends WindowContainer<WindowContainer> {
!PRESERVE_WINDOWS);
}
- /** @return true if the stack can only contain one task */
- boolean isSingleTaskInstance() {
- return mDisplayContent != null && mDisplayContent.isSingleTaskInstance();
- }
-
final boolean isHomeOrRecentsStack() {
return isActivityTypeHome() || isActivityTypeRecents();
}
@@ -6354,13 +6353,6 @@ class Task extends WindowContainer<WindowContainer> {
if (newTask) {
if (r.mLaunchTaskBehind) {
transit = TRANSIT_TASK_OPEN_BEHIND;
- } else if (dc.isSingleTaskInstance()) {
- // If a new task is being launched in a single task display, we don't need
- // to play normal animation, but need to trigger a callback when an app
- // transition is actually handled. So ignore already prepared activity, and
- // override it.
- transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
- keepCurTransition = false;
} else {
// If a new task is being launched, then mark the existing top activity as
// supporting picture-in-picture while pausing only if the starting activity
@@ -6781,15 +6773,6 @@ class Task extends WindowContainer<WindowContainer> {
// nothing to do!
if (noAnimation) {
ActivityOptions.abort(options);
- } else if (isSingleTaskInstance()) {
- // When a task is moved front on the display which can only contain one task, start
- // a special transition.
- // {@link AppTransitionController#handleAppTransitionReady} later picks up the
- // transition, and schedules
- // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered
- // after contents are drawn on the display.
- updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
- true /* forceOverride */);
} else {
updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
}
@@ -6837,9 +6820,6 @@ class Task extends WindowContainer<WindowContainer> {
mStackSupervisor.mNoAnimActivities.add(r);
}
ActivityOptions.abort(options);
- } else if (isSingleTaskInstance()) {
- updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
- true /* forceOverride */);
} else {
updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
}
@@ -7205,10 +7185,6 @@ class Task extends WindowContainer<WindowContainer> {
}
void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
- if (isSingleTaskInstance() && hasChild()) {
- throw new IllegalStateException("Can only have one child on stack=" + this);
- }
-
Task task = child.asTask();
try {
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 2a241183ff1f..5364f9c58657 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -54,14 +54,12 @@ class TaskChangeNotificationController {
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
- private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
- private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 23;
- private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24;
- private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25;
- private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
- private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27;
- private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 28;
- private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 29;
+ private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 22;
+ private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 23;
+ private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 24;
+ private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 25;
+ private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 26;
+ private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 27;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -156,14 +154,6 @@ class TaskChangeNotificationController {
l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
};
- private final TaskStackConsumer mNotifySingleTaskDisplayDrawn = (l, m) -> {
- l.onSingleTaskDisplayDrawn(m.arg1);
- };
-
- private final TaskStackConsumer mNotifySingleTaskDisplayEmpty = (l, m) -> {
- l.onSingleTaskDisplayEmpty(m.arg1);
- };
-
private final TaskStackConsumer mNotifyTaskDisplayChanged = (l, m) -> {
l.onTaskDisplayChanged(m.arg1, m.arg2);
};
@@ -261,12 +251,6 @@ class TaskChangeNotificationController {
case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
break;
- case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
- forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
- break;
- case NOTIFY_SINGLE_TASK_DISPLAY_EMPTY:
- forAllRemoteListeners(mNotifySingleTaskDisplayEmpty, msg);
- break;
case NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskDisplayChanged, msg);
break;
@@ -520,27 +504,6 @@ class TaskChangeNotificationController {
}
/**
- * Notify listeners that contents are drawn for the first time on a single task display.
- */
- void notifySingleTaskDisplayDrawn(int displayId) {
- final Message msg = mHandler.obtainMessage(NOTIFY_SINGLE_TASK_DISPLAY_DRAWN,
- displayId, 0 /* unused */);
- forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
- msg.sendToTarget();
- }
-
- /**
- * Notify listeners that the last task is removed from a single task display.
- */
- void notifySingleTaskDisplayEmpty(int displayId) {
- final Message msg = mHandler.obtainMessage(
- NOTIFY_SINGLE_TASK_DISPLAY_EMPTY,
- displayId, 0 /* unused */);
- forAllLocalListeners(mNotifySingleTaskDisplayEmpty, msg);
- msg.sendToTarget();
- }
-
- /**
* Notify listeners that a task is reparented to another display.
*/
void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 830ad5d6043b..c6b9e48f6a57 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -311,9 +311,6 @@ final class TaskDisplayArea extends DisplayArea<Task> {
@Override
void addChild(Task task, int position) {
if (DEBUG_STACK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
- if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1) {
- throw new IllegalStateException("addChild: Can only have one task on display=" + this);
- }
addStackReferenceIfNeeded(task);
position = findPositionForStack(position, task, true /* adding */);
@@ -1020,14 +1017,6 @@ final class TaskDisplayArea extends DisplayArea<Task> {
*/
Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
Intent intent, boolean createdByOrganizer) {
- if (mDisplayContent.mSingleTaskInstance && getStackCount() > 0) {
- // Create stack on default display instead since this display can only contain 1 stack.
- // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
- // this goes away once ActivityView is no longer using virtual displays.
- return mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
- windowingMode, activityType, onTop, info, intent, createdByOrganizer);
- }
-
if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
// Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
// anything else should be passing it in anyways...except for the task organizer.
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 6486b78eb601..6504f00905e7 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -389,10 +389,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
- // Don't send onTaskAppeared signal for task created by organizer since we will return it in
- // the creation call.
- if (task.mCreatedByOrganizer) return;
-
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
state.addTask(task);
}
@@ -405,7 +401,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
@Override
- public TaskAppearedInfo createRootTask(int displayId, int windowingMode) {
+ public RunningTaskInfo createRootTask(int displayId, int windowingMode) {
enforceStackPermission("createRootTask()");
final long origId = Binder.clearCallingIdentity();
try {
@@ -422,11 +418,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
true /* createdByOrganizer */);
RunningTaskInfo out = task.getTaskInfo();
mLastSentTaskInfos.put(task, out);
- final TaskOrganizerState state =
- mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder());
- final SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
- "TaskOrganizerController.createRootTask");
- return new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl);
+ return out;
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index e8c4491d3677..aab5da6de214 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -242,9 +242,8 @@ class TaskSnapshotSurface implements StartingSurface {
try {
final int res = session.addToDisplay(window, layoutParams,
View.GONE, activity.getDisplayContent().getDisplayId(), mTmpInsetsState,
- tmpFrames.frame, tmpFrames.contentInsets, tmpFrames.stableInsets,
- tmpFrames.displayCutout, null /* outInputChannel */, mTmpInsetsState,
- mTempControls);
+ tmpFrames.frame, tmpFrames.displayCutout, null /* outInputChannel */,
+ mTmpInsetsState, mTempControls);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return null;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index fc67cd22ee69..ac86698c630f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -31,7 +32,6 @@ import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -43,7 +43,6 @@ import com.android.internal.protolog.common.ProtoLog;
import java.util.ArrayList;
import java.util.Map;
-import java.util.Set;
/**
* Represents a logical transition.
@@ -70,6 +69,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private int mSyncId;
private @WindowManager.TransitionFlags int mFlags;
private final TransitionController mController;
+ private final BLASTSyncEngine mSyncEngine;
final ArrayMap<WindowContainer, ChangeInfo> mParticipants = new ArrayMap<>();
private int mState = STATE_COLLECTING;
private boolean mReadyCalled = false;
@@ -79,7 +79,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mType = type;
mFlags = flags;
mController = controller;
- mSyncId = mController.mSyncEngine.startSyncSet(this);
+ mSyncEngine = mController.mAtm.mWindowManager.mSyncEngine;
+ mSyncId = mSyncEngine.startSyncSet(this);
}
/**
@@ -104,10 +105,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (mSyncId < 0) return;
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Collecting in transition %d: %s",
mSyncId, wc);
- // Add to sync set before checking contains because it may not have added it at other
- // times (eg. if wc was previously invisible).
- mController.mSyncEngine.addToSyncSet(mSyncId, wc);
if (mParticipants.containsKey(wc)) return;
+ mSyncEngine.addToSyncSet(mSyncId, wc);
mParticipants.put(wc, new ChangeInfo());
}
@@ -125,8 +124,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Finish collecting in transition %d", mSyncId);
- mController.mSyncEngine.setReady(mSyncId);
- mController.mAtm.mWindowManager.mWindowPlacerLocked.requestTraversal();
+ mSyncEngine.setReady(mSyncId);
}
/** The transition has finished animating and is ready to finalize WM state */
@@ -146,7 +144,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
@Override
- public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
+ public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
if (syncId != mSyncId) {
Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
return;
@@ -155,10 +153,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mController.moveToPlaying(this);
final TransitionInfo info = calculateTransitionInfo(mType, mParticipants);
- SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
- int displayId = Display.DEFAULT_DISPLAY;
- for (WindowContainer container : windowContainersReady) {
- container.mergeBlastSyncTransaction(mergedTransaction);
+ int displayId = DEFAULT_DISPLAY;
+ for (WindowContainer container : mParticipants.keySet()) {
displayId = container.mDisplayContent.getDisplayId();
}
@@ -168,14 +164,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
- mController.getTransitionPlayer().onTransitionReady(this, info, mergedTransaction);
+ mController.getTransitionPlayer().onTransitionReady(this, info, transaction);
} catch (RemoteException e) {
// If there's an exception when trying to send the mergedTransaction to the
// client, we should immediately apply it here so the transactions aren't lost.
- mergedTransaction.apply();
+ transaction.apply();
}
} else {
- mergedTransaction.apply();
+ transaction.apply();
}
mSyncId = -1;
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index d102c19bfff9..7cdc177e1b9b 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -51,7 +51,6 @@ class TransitionController {
Arrays.sort(SUPPORTED_LEGACY_TRANSIT_TYPES);
}
- final BLASTSyncEngine mSyncEngine = new BLASTSyncEngine();
private ITransitionPlayer mTransitionPlayer;
private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> mTransitionPlayer = null;
final ActivityTaskManagerService mAtm;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0edaa1d821df..da3a92856fe5 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -58,6 +58,7 @@ import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
@@ -96,7 +97,6 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
-import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -109,8 +109,7 @@ import java.util.function.Predicate;
* changes are made to this class.
*/
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
- implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
- BLASTSyncEngine.TransactionReadyListener {
+ implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
@@ -290,16 +289,35 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
RemoteToken mRemoteToken = null;
- BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
- SurfaceControl.Transaction mBLASTSyncTransaction;
- boolean mUsingBLASTSyncTransaction = false;
- BLASTSyncEngine.TransactionReadyListener mWaitingListener;
- int mWaitingSyncId;
+ /** This isn't participating in a sync. */
+ public static final int SYNC_STATE_NONE = 0;
+
+ /** This is currently waiting for itself to finish drawing. */
+ public static final int SYNC_STATE_WAITING_FOR_DRAW = 1;
+
+ /** This container is ready, but it might still have unfinished children. */
+ public static final int SYNC_STATE_READY = 2;
+
+ @IntDef(prefix = { "SYNC_STATE_" }, value = {
+ SYNC_STATE_NONE,
+ SYNC_STATE_WAITING_FOR_DRAW,
+ SYNC_STATE_READY,
+ })
+ @interface SyncState {}
+
+ /**
+ * If non-null, references the sync-group directly waiting on this container. Otherwise, this
+ * container is only being waited-on by its parents (if in a sync-group). This has implications
+ * on how this container is handled during parent changes.
+ */
+ BLASTSyncEngine.SyncGroup mSyncGroup = null;
+ final SurfaceControl.Transaction mSyncTransaction;
+ @SyncState int mSyncState = SYNC_STATE_NONE;
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
- mBLASTSyncTransaction = wms.mTransactionFactory.get();
+ mSyncTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
@@ -357,6 +375,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// Send onParentChanged notification here is we disabled sending it in setParent for
// reparenting case.
onParentChanged(newParent, oldParent);
+ onSyncReparent(oldParent, newParent);
}
final protected void setParent(WindowContainer<WindowContainer> parent) {
@@ -372,6 +391,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
onDisplayChanged(mParent.mDisplayContent);
}
onParentChanged(mParent, oldParent);
+ onSyncReparent(oldParent, mParent);
}
}
@@ -612,6 +632,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
scheduleAnimation();
}
+ // This must happen after updating the surface so that sync transactions can be handled
+ // properly.
if (mParent != null) {
mParent.removeChild(this);
}
@@ -2247,8 +2269,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* {@link #getPendingTransaction()}
*/
public Transaction getSyncTransaction() {
- if (mUsingBLASTSyncTransaction) {
- return mBLASTSyncTransaction;
+ if (mSyncState != SYNC_STATE_NONE) {
+ return mSyncTransaction;
}
return getPendingTransaction();
@@ -2895,69 +2917,140 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
- @Override
- public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
- if (mWaitingListener == null) {
- return;
- }
-
- windowContainersReady.add(this);
- mWaitingListener.onTransactionReady(mWaitingSyncId, windowContainersReady);
-
- mWaitingListener = null;
- mWaitingSyncId = -1;
- }
-
/**
- * Returns true if any of the children elected to participate in the Sync
+ * Call this when this container finishes drawing content.
+ *
+ * @return {@code true} if consumed (this container is part of a sync group).
*/
- boolean addChildrenToSyncSet(int localId) {
- boolean willSync = false;
+ boolean onSyncFinishedDrawing() {
+ if (mSyncState == SYNC_STATE_NONE) return false;
+ mSyncState = SYNC_STATE_READY;
+ mWmService.mWindowPlacerLocked.requestTraversal();
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "onSyncFinishedDrawing %s", this);
+ return true;
+ }
- for (int i = 0; i < mChildren.size(); i++) {
- final WindowContainer child = mChildren.get(i);
- willSync |= mBLASTSyncEngine.addToSyncSet(localId, child);
+ void setSyncGroup(@NonNull BLASTSyncEngine.SyncGroup group) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "setSyncGroup #%d on %s", group.mSyncId, this);
+ if (group != null) {
+ if (mSyncGroup != null && mSyncGroup != group) {
+ throw new IllegalStateException("Can't sync on 2 engines simultaneously");
+ }
}
- return willSync;
+ mSyncGroup = group;
}
- boolean setPendingListener(BLASTSyncEngine.TransactionReadyListener waitingListener,
- int waitingId) {
- // If we are invisible, no need to sync, likewise if we are already engaged in a sync,
- // we can't support overlapping syncs on a single container yet.
- if (!isVisible() || mWaitingListener != null) {
- ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "- NOT adding to sync: visible=%b "
- + "hasListener=%b", isVisible(), mWaitingListener != null);
+ /**
+ * Prepares this container for participation in a sync-group. This includes preparing all its
+ * children.
+ *
+ * @return {@code true} if something changed (eg. this wasn't already in the sync group).
+ */
+ boolean prepareSync() {
+ if (mSyncState != SYNC_STATE_NONE) {
+ // Already part of sync
return false;
}
- mUsingBLASTSyncTransaction = true;
-
- // Make sure to set these before we call setReady in case the sync was a no-op
- mWaitingSyncId = waitingId;
- mWaitingListener = waitingListener;
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final WindowContainer child = getChildAt(i);
+ child.prepareSync();
+ }
+ mSyncState = SYNC_STATE_READY;
return true;
}
- boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
- int waitingId) {
- boolean willSync = setPendingListener(waitingListener, waitingId);
- if (!willSync) {
- return false;
- }
-
- int localId = mBLASTSyncEngine.startSyncSet(this);
- willSync |= addChildrenToSyncSet(localId);
- mBLASTSyncEngine.setReady(localId);
+ boolean useBLASTSync() {
+ return mSyncState != SYNC_STATE_NONE;
+ }
- return willSync;
+ /**
+ * Recursively finishes/cleans-up sync state of this subtree and collects all the sync
+ * transactions into `outMergedTransaction`.
+ * @param outMergedTransaction A transaction to merge all the recorded sync operations into.
+ * @param cancel If true, this is being finished because it is leaving the sync group rather
+ * than due to the sync group completing.
+ */
+ void finishSync(Transaction outMergedTransaction, boolean cancel) {
+ if (mSyncState == SYNC_STATE_NONE) return;
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
+ outMergedTransaction.merge(mSyncTransaction);
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ mChildren.get(i).finishSync(outMergedTransaction, cancel);
+ }
+ mSyncState = SYNC_STATE_NONE;
+ if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
+ mSyncGroup = null;
}
- boolean useBLASTSync() {
- return mUsingBLASTSyncTransaction;
+ /**
+ * Checks if the subtree rooted at this container is finished syncing (everything is ready or
+ * not visible). NOTE, this is not const: it will cancel/prepare itself depending on its state
+ * in the hierarchy.
+ *
+ * @return {@code true} if this subtree is finished waiting for sync participants.
+ */
+ boolean isSyncFinished() {
+ if (!isVisibleRequested()) {
+ return true;
+ }
+ if (mSyncState == SYNC_STATE_NONE) {
+ prepareSync();
+ }
+ if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW) {
+ return false;
+ }
+ // READY
+ // Loop from top-down.
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ final boolean childFinished = child.isSyncFinished();
+ if (childFinished && child.isVisibleRequested() && child.fillsParent()) {
+ // Any lower children will be covered-up, so we can consider this finished.
+ return true;
+ }
+ if (!childFinished) {
+ return false;
+ }
+ }
+ return true;
}
- void mergeBlastSyncTransaction(Transaction t) {
- t.merge(mBLASTSyncTransaction);
- mUsingBLASTSyncTransaction = false;
+ /**
+ * Called during reparent to handle sync state when the hierarchy changes.
+ * If this is in a sync group and gets reparented out, it will cancel syncing.
+ * If this is not in a sync group and gets parented into one, it will prepare itself.
+ * If its moving around within a sync-group, it needs to restart its syncing since a
+ * hierarchy change implies a configuration change.
+ */
+ private void onSyncReparent(WindowContainer oldParent, WindowContainer newParent) {
+ if (newParent == null || newParent.mSyncState == SYNC_STATE_NONE) {
+ if (mSyncState == SYNC_STATE_NONE) {
+ return;
+ }
+ if (newParent == null) {
+ // This is getting removed.
+ if (oldParent.mSyncState != SYNC_STATE_NONE) {
+ // In order to keep the transaction in sync, merge it into the parent.
+ finishSync(oldParent.mSyncTransaction, true /* cancel */);
+ } else if (mSyncGroup != null) {
+ // This is watched directly by the sync-group, so merge this transaction into
+ // into the sync-group so it isn't lost
+ finishSync(mSyncGroup.getOrphanTransaction(), true /* cancel */);
+ } else {
+ throw new IllegalStateException("This container is in sync mode without a sync"
+ + " group: " + this);
+ }
+ return;
+ } else if (mSyncGroup == null) {
+ // This is being reparented out of the sync-group. To prevent ordering issues on
+ // this container, immediately apply/cancel sync on it.
+ finishSync(getPendingTransaction(), true /* cancel */);
+ return;
+ }
+ // Otherwise this is the "root" of a synced subtree, so continue on to preparation.
+ }
+ // This container's situation has changed so we need to restart its sync.
+ mSyncState = SYNC_STATE_NONE;
+ prepareSync();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index d96b6457f9db..259dee48fb39 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -207,28 +207,6 @@ public class WindowFrames {
return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
}
- // TODO(b/118118435): Remove after migration.
- /**
- * Calculate the insets for the type
- * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
- *
- * @param cutoutInsets The insets for the cutout.
- */
- void calculateDockedDividerInsets(Rect cutoutInsets) {
- // For the docked divider, we calculate the stable insets like a full-screen window
- // so it can use it to calculate the snap positions.
- mTmpRect.set(mDisplayFrame);
- mTmpRect.inset(cutoutInsets);
- mTmpRect.intersectUnchecked(mStableFrame);
- InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets);
-
- // The divider doesn't care about insets in any case, so set it to empty so we don't
- // trigger a relayout when moving it.
- mContentInsets.setEmpty();
- mVisibleInsets.setEmpty();
- mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
- }
-
/**
* Calculate the insets for a window.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ad624d50f7fd..66f2e4d8dc2c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -667,6 +667,8 @@ public class WindowManagerService extends IWindowManager.Stub
// Whether to enable BLASTSyncEngine Transaction passing.
final boolean mUseBLASTSync = false;
+ final BLASTSyncEngine mSyncEngine;
+
int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
Rect mDockedStackCreateBounds;
@@ -1191,6 +1193,8 @@ public class WindowManagerService extends IWindowManager.Stub
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
WM_USE_BLAST_ADAPTER_FLAG, false);
+ mSyncEngine = new BLASTSyncEngine(this);
+
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
@@ -1376,7 +1380,6 @@ public class WindowManagerService extends IWindowManager.Stub
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
Arrays.fill(outActiveControls, null);
@@ -1691,11 +1694,10 @@ public class WindowManagerService extends IWindowManager.Stub
prepareNoneTransitionForRelaunching(activity);
}
- if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outContentInsets,
- outStableInsets, outDisplayCutout)) {
+ if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outDisplayCutout,
+ outInsetsState, win.isClientLocal())) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
- outInsetsState.set(win.getInsetsState(), win.isClientLocal());
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -8245,9 +8247,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean getWindowInsets(WindowManager.LayoutParams attrs,
- int displayId, Rect outContentInsets, Rect outStableInsets,
+ public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId,
DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState) {
+ final boolean fromLocal = Binder.getCallingPid() == myPid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -8257,13 +8259,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ "could not be found!");
}
final WindowToken windowToken = dc.getWindowToken(attrs.token);
- final InsetsStateController insetsStateController =
- dc.getInsetsStateController();
- outInsetsState.set(insetsStateController.getInsetsForWindowMetrics(attrs));
-
return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken,
- mTmpRect /* outFrame */, outContentInsets, outStableInsets,
- outDisplayCutout);
+ mTmpRect /* outFrame */, outDisplayCutout, outInsetsState, fromLocal);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 3011f256bac6..fe312e6b4c46 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -111,6 +111,8 @@ public class WindowManagerShellCommand extends ShellCommand {
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
+ case "reset":
+ return runReset(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -507,6 +509,34 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runReset(PrintWriter pw) throws RemoteException {
+ int displayId = getDisplayId(getNextArg());
+
+ // size
+ mInterface.clearForcedDisplaySize(displayId);
+
+ // density
+ mInterface.clearForcedDisplayDensityForUser(displayId, UserHandle.USER_CURRENT);
+
+ // folded-area
+ mInternal.setOverrideFoldedArea(new Rect());
+
+ // scaling
+ mInterface.setForcedDisplayScalingMode(displayId, DisplayContent.FORCE_SCALING_MODE_AUTO);
+
+ // user-rotation
+ mInternal.thawDisplayRotation(displayId);
+
+ // fixed-to-user-rotation
+ mInterface.setFixedToUserRotation(displayId, IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT);
+
+ // set-ignore-orientation-request
+ mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
+
+ pw.println("Reset all settings for displayId=" + displayId);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -533,6 +563,8 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
+ pw.println(" reset [-d DISPLAY_ID]");
+ pw.println(" Reset all override settings.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5e07f5187c54..ae152d253d17 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -57,7 +57,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Server side implementation for the interface for organizing windows
@@ -84,7 +83,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private final ActivityTaskManagerService mService;
private final WindowManagerGlobalLock mGlobalLock;
- private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
private final HashMap<Integer, IWindowContainerTransactionCallback>
mTransactionCallbacksByPendingSyncId = new HashMap();
@@ -505,7 +503,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
@VisibleForTesting
int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
- int id = mBLASTSyncEngine.startSyncSet(this);
+ int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
mTransactionCallbacksByPendingSyncId.put(id, callback);
return id;
}
@@ -513,31 +511,26 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
@VisibleForTesting
void setSyncReady(int id) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id);
- mBLASTSyncEngine.setReady(id);
+ mService.mWindowManager.mSyncEngine.setReady(id);
}
@VisibleForTesting
void addToSyncSet(int syncId, WindowContainer wc) {
- mBLASTSyncEngine.addToSyncSet(syncId, wc);
+ mService.mWindowManager.mSyncEngine.addToSyncSet(syncId, wc);
}
@Override
- public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
+ public void onTransactionReady(int syncId, SurfaceControl.Transaction t) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
final IWindowContainerTransactionCallback callback =
mTransactionCallbacksByPendingSyncId.get(syncId);
- SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
- for (WindowContainer container : windowContainersReady) {
- container.mergeBlastSyncTransaction(mergedTransaction);
- }
-
try {
- callback.onTransactionReady(syncId, mergedTransaction);
+ callback.onTransactionReady(syncId, t);
} catch (RemoteException e) {
// If there's an exception when trying to send the mergedTransaction to the client, we
// should immediately apply it here so the transactions aren't lost.
- mergedTransaction.apply();
+ t.apply();
}
mTransactionCallbacksByPendingSyncId.remove(syncId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d4b6d00c1679..a4b4fcb79656 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -251,10 +251,8 @@ import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import java.util.Set;
import java.util.function.Predicate;
/** A window in the window manager. */
@@ -658,15 +656,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private static final StringBuilder sTmpSB = new StringBuilder();
/**
- * Whether the next surfacePlacement call should notify that the blast sync is ready.
- * This is set to true when {@link #finishDrawing(Transaction)} is called so
- * {@link #onTransactionReady(int, Set)} is called after the next surfacePlacement. This allows
- * Transactions to get flushed into the syncTransaction before notifying {@link BLASTSyncEngine}
- * that this WindowState is ready.
- */
- private boolean mNotifyBlastOnSurfacePlacement;
-
- /**
* Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
*/
@@ -706,15 +695,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
- /**
- * BLASTSyncEngine ID corresponding to a sync-set for all
- * our children. We add our children to this set in Sync,
- * but we save it and don't mark it as ready until finishDrawing
- * this way we have a two way latch between all our children finishing
- * and drawing ourselves.
- */
- private int mLocalSyncId = -1;
-
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
private final WindowProcessController mWpcForDisplayConfigChanges;
@@ -977,16 +957,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSession.windowAddedLocked(mAttrs.packageName);
}
+ boolean inSizeCompatMode() {
+ return inSizeCompatMode(mAttrs, mActivityRecord);
+ }
+
/**
* @return {@code true} if the application runs in size compatibility mode.
* @see android.content.res.CompatibilityInfo#supportsScreen
- * @see ActivityRecord#inSizeCompatMode
+ * @see ActivityRecord#inSizeCompatMode()
*/
- boolean inSizeCompatMode() {
- return (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
- || (mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
- // Exclude starting window because it is not displayed by the application.
- && mAttrs.type != TYPE_APPLICATION_STARTING);
+ static boolean inSizeCompatMode(WindowManager.LayoutParams attrs, WindowToken windowToken) {
+ return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
+ || (windowToken != null && windowToken.hasSizeCompatBounds()
+ // Exclude starting window because it is not displayed by the application.
+ && attrs.type != TYPE_APPLICATION_STARTING);
}
/**
@@ -1224,14 +1208,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
Math.min(windowFrames.mStableFrame.bottom, windowFrames.mFrame.bottom));
}
- if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
- windowFrames.mDisplayFrame);
- windowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
- } else {
- windowFrames.calculateInsets(windowsAreFloating, isFullscreenAndFillsDisplay,
- getDisplayFrames(dc.mDisplayFrames).mUnrestricted);
- }
+ windowFrames.calculateInsets(windowsAreFloating, isFullscreenAndFillsDisplay,
+ getDisplayFrames(dc.mDisplayFrames).mUnrestricted);
windowFrames.setDisplayCutout(
windowFrames.mDisplayCutout.calculateRelativeTo(windowFrames.mFrame));
@@ -1545,7 +1523,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* modification according to the state of transient bars.
*/
InsetsState getInsetsState() {
- return getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
+ InsetsState state = getDisplayContent().getInsetsPolicy().getInsetsForWindow(this);
+ if (inSizeCompatMode()) {
+ state = new InsetsState(state, true);
+ state.scale(mInvGlobalScale);
+ }
+ return state;
}
@Override
@@ -2228,7 +2211,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
- immediatelyNotifyBlastSync();
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
@@ -3607,11 +3589,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
backdropFrame.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
}
outFrames.displayCutout.set(mWindowFrames.mDisplayCutout.getDisplayCutout());
-
- // TODO(b/149813814): Remove legacy insets.
- outFrames.contentInsets.set(mWindowFrames.mLastContentInsets);
- outFrames.visibleInsets.set(mWindowFrames.mLastVisibleInsets);
- outFrames.stableInsets.set(mWindowFrames.mLastStableInsets);
}
void reportResized() {
@@ -5330,7 +5307,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
updateFrameRateSelectionPriorityIfNeeded();
mWinAnimator.prepareSurfaceLocked(SurfaceControl.getGlobalTransaction(), true);
- notifyBlastSyncTransaction();
super.prepareSurfaces();
}
@@ -5644,7 +5620,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* into the state of the control target.
*
* @param insetProvider the provider which should not be visible to the client.
- * @see InsetsStateController#getInsetsForDispatch(WindowState)
+ * @see InsetsStateController#getInsetsForWindow(WindowState)
*/
void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
mControllableInsetProvider = insetProvider;
@@ -5761,13 +5737,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void setViewVisibility(int viewVisibility) {
mViewVisibility = viewVisibility;
- // The viewVisibility is set to GONE with a client request to relayout. If this occurs and
- // there's a blast sync transaction waiting, finishDrawing will never be called since the
- // client will not render when visibility is GONE. Therefore, call finishDrawing here to
- // prevent system server from blocking on a window that will not draw.
- if (viewVisibility == View.GONE && mUsingBLASTSyncTransaction) {
- immediatelyNotifyBlastSync();
- }
}
SurfaceControl getClientViewRootSurface() {
@@ -5775,86 +5744,57 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
- int waitingId) {
- // If the window is goneForLayout, relayout won't be called so we'd just wait forever.
- if (isGoneForLayout()) {
- return false;
- }
- boolean willSync = setPendingListener(waitingListener, waitingId);
- if (!willSync) {
+ boolean prepareSync() {
+ if (!super.prepareSync()) {
return false;
}
- requestRedrawForSync();
-
- mLocalSyncId = mBLASTSyncEngine.startSyncSet(this);
- addChildrenToSyncSet(mLocalSyncId);
-
// In the WindowContainer implementation we immediately mark ready
// since a generic WindowContainer only needs to wait for its
// children to finish and is immediately ready from its own
// perspective but at the WindowState level we need to wait for ourselves
- // to draw even if the children draw first our don't need to sync, so we omit
- // the set ready call until later in finishDrawing()
+ // to draw even if the children draw first our don't need to sync, so we start
+ // in WAITING state rather than READY.
+ mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
+ requestRedrawForSync();
+
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
BLAST_TIMEOUT_DURATION);
-
return true;
}
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
- if (!mUsingBLASTSyncTransaction) {
+ if (!onSyncFinishedDrawing()) {
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
if (postDrawTransaction != null) {
- mBLASTSyncTransaction.merge(postDrawTransaction);
+ mSyncTransaction.merge(postDrawTransaction);
}
- mNotifyBlastOnSurfacePlacement = true;
mWinAnimator.finishDrawingLocked(null);
// We always want to force a traversal after a finish draw for blast sync.
return true;
}
- private void notifyBlastSyncTransaction() {
+ void immediatelyNotifyBlastSync() {
+ finishDrawing(null);
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
-
- if (!mNotifyBlastOnSurfacePlacement || mWaitingListener == null) {
- mNotifyBlastOnSurfacePlacement = false;
- return;
- }
+ if (!useBLASTSync()) return;
final Task task = getTask();
if (task != null) {
final SurfaceControl.Transaction t = task.getMainWindowSizeChangeTransaction();
if (t != null) {
- mBLASTSyncTransaction.merge(t);
+ mSyncTransaction.merge(t);
}
task.setMainWindowSizeChangeTransaction(null);
}
-
- // If localSyncId is >0 then we are syncing with children and will
- // invoke transaction ready from our own #transactionReady callback
- // we just need to signal our side of the sync (setReady). But if we
- // have no sync operation at this level transactionReady will never
- // be invoked and we need to invoke it ourself.
- if (mLocalSyncId >= 0) {
- mBLASTSyncEngine.setReady(mLocalSyncId);
- return;
- }
-
- mWaitingListener.onTransactionReady(mWaitingSyncId, Collections.singleton(this));
-
- mWaitingSyncId = 0;
- mWaitingListener = null;
- mNotifyBlastOnSurfacePlacement = false;
}
- void immediatelyNotifyBlastSync() {
- finishDrawing(null);
- notifyBlastSyncTransaction();
+ @Override
+ boolean fillsParent() {
+ return mAttrs.type == TYPE_APPLICATION_STARTING;
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fa0b8cce5f70..5ced6a52050b 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -335,6 +335,13 @@ class WindowToken extends WindowContainer<WindowState> {
}
/**
+ * @return {@code true} if this window token has bounds for size compatibility mode.
+ */
+ boolean hasSizeCompatBounds() {
+ return false;
+ }
+
+ /**
* Returns true if the new window is considered greater than the existing window in terms of
* z-order.
*/
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d6a56ba531d0..9f83bafbed75 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -38,7 +38,6 @@ cc_library_static {
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
"com_android_server_net_NetworkStatsService.cpp",
"com_android_server_power_PowerManagerService.cpp",
- "com_android_server_powerstats_PowerStatsService.cpp",
"com_android_server_security_VerityUtils.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 76b171337bb9..43f50bfc33d5 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -23,6 +23,7 @@
#include <jni.h>
#include <nativehelper/JNIHelp.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
#include <binder/IServiceManager.h>
#include <hidl/HidlTransportSupport.h>
#include <incremental_service.h>
@@ -64,6 +65,7 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /
using ::android::frameworks::stats::V1_0::IStats;
using ::android::frameworks::stats::V1_0::implementation::StatsHal;
using ::android::hardware::configureRpcThreadpool;
+ using ::android::hidl::manager::V1_0::IServiceManager;
status_t err;
@@ -74,15 +76,22 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /
sp<ISensorManager> sensorService = new SensorManager(vm);
err = sensorService->registerAsService();
- ALOGE_IF(err != OK, "Cannot register %s: %d", ISensorManager::descriptor, err);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d", ISensorManager::descriptor, err);
sp<ISchedulingPolicyService> schedulingService = new SchedulingPolicyService();
- err = schedulingService->registerAsService();
- ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err);
+ if (IServiceManager::Transport::HWBINDER ==
+ hardware::defaultServiceManager1_2()->getTransport(ISchedulingPolicyService::descriptor,
+ "default")) {
+ err = schedulingService->registerAsService("default");
+ LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d",
+ ISchedulingPolicyService::descriptor, err);
+ } else {
+ ALOGW("%s is deprecated. Skipping registration.", ISchedulingPolicyService::descriptor);
+ }
sp<IStats> statsHal = new StatsHal();
err = statsHal->registerAsService();
- ALOGE_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err);
}
static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
deleted file mode 100644
index 5eb6b7343945..000000000000
--- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2020 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 "PowerStatsService"
-
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-
-#include <log/log.h>
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::power::stats::V1_0::EnergyData;
-using android::hardware::power::stats::V1_0::RailInfo;
-using android::hardware::power::stats::V1_0::Status;
-
-static jclass class_railInfo;
-static jmethodID method_railInfoInit;
-static jclass class_energyData;
-static jmethodID method_energyDataInit;
-
-namespace android {
-
-static std::mutex gPowerStatsHalMutex;
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr;
-
-static void deinitPowerStats() {
- gPowerStatsHalV1_0_ptr = nullptr;
-}
-
-struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase> &who) override {
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
- deinitPowerStats();
- }
-};
-
-sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient();
-
-static bool connectToPowerStatsHal() {
- if (gPowerStatsHalV1_0_ptr == nullptr) {
- gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService();
-
- if (gPowerStatsHalV1_0_ptr == nullptr) {
- ALOGE("Unable to get power.stats HAL service.");
- return false;
- }
-
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked =
- gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- deinitPowerStats();
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- return false;
- }
- }
- return true;
-}
-
-static bool checkResult(const Return<void> &ret, const char *function) {
- if (!ret.isOk()) {
- ALOGE("%s failed: requested HAL service not available. Description: %s", function,
- ret.description().c_str());
- if (ret.isDeadObject()) {
- deinitPowerStats();
- }
- return false;
- }
- return true;
-}
-
-static jobjectArray nativeGetRailInfo(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeGetRailInfo failed to connect to power.stats HAL");
- return nullptr;
- }
-
- hidl_vec<RailInfo> list;
- Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&list](auto rails, auto status) {
- if (status != Status::SUCCESS) {
- ALOGW("Rail information is not available");
- } else {
- list = std::move(rails);
- }
- });
-
- if (!checkResult(ret, __func__)) {
- ALOGE("getRailInfo failed");
- return nullptr;
- } else {
- jobjectArray railInfoArray = env->NewObjectArray(list.size(), class_railInfo, nullptr);
- for (int i = 0; i < list.size(); i++) {
- jstring railName = env->NewStringUTF(list[i].railName.c_str());
- jstring subsysName = env->NewStringUTF(list[i].subsysName.c_str());
- jobject railInfo = env->NewObject(class_railInfo, method_railInfoInit, list[i].index,
- railName, subsysName, list[i].samplingRate);
- env->SetObjectArrayElement(railInfoArray, i, railInfo);
- env->DeleteLocalRef(railName);
- env->DeleteLocalRef(subsysName);
- env->DeleteLocalRef(railInfo);
- }
- return railInfoArray;
- }
-}
-
-static jobjectArray nativeGetEnergyData(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeGetEnergy failed to connect to power.stats HAL");
- }
-
- hidl_vec<EnergyData> list;
- Return<void> ret =
- gPowerStatsHalV1_0_ptr->getEnergyData({}, [&list](auto energyData, auto status) {
- if (status != Status::SUCCESS) {
- ALOGW("getEnergyData is not supported");
- } else {
- list = std::move(energyData);
- }
- });
-
- if (!checkResult(ret, __func__)) {
- ALOGE("getEnergyData failed");
- return nullptr;
- } else {
- jobjectArray energyDataArray = env->NewObjectArray(list.size(), class_energyData, nullptr);
- for (int i = 0; i < list.size(); i++) {
- jobject energyData = env->NewObject(class_energyData, method_energyDataInit,
- list[i].index, list[i].timestamp, list[i].energy);
- env->SetObjectArrayElement(energyDataArray, i, energyData);
- env->DeleteLocalRef(energyData);
- }
- return energyDataArray;
- }
-}
-
-static jboolean nativeInit(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- jclass temp = env->FindClass("com/android/server/powerstats/PowerStatsData$RailInfo");
- if (temp == nullptr) return false;
-
- class_railInfo = (jclass)env->NewGlobalRef(temp);
- if (class_railInfo == nullptr) return false;
-
- method_railInfoInit =
- env->GetMethodID(class_railInfo, "<init>", "(JLjava/lang/String;Ljava/lang/String;J)V");
- if (method_railInfoInit == nullptr) return false;
-
- temp = env->FindClass("com/android/server/powerstats/PowerStatsData$EnergyData");
- if (temp == nullptr) return false;
-
- class_energyData = (jclass)env->NewGlobalRef(temp);
- if (class_energyData == nullptr) return false;
-
- method_energyDataInit = env->GetMethodID(class_energyData, "<init>", "(JJJ)V");
- if (method_energyDataInit == nullptr) return false;
-
- bool rv = true;
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeInit failed to connect to power.stats HAL");
- rv = false;
- } else {
- Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&rv](auto rails, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("nativeInit RailInfo is unavailable");
- rv = false;
- }
- });
-
- ret = gPowerStatsHalV1_0_ptr->getEnergyData({}, [&rv](auto energyData, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("nativeInit EnergyData is unavailable");
- rv = false;
- }
- });
- }
-
- return rv;
-}
-
-static const JNINativeMethod method_table[] = {
- {"nativeInit", "()Z", (void *)nativeInit},
- {"nativeGetRailInfo", "()[Lcom/android/server/powerstats/PowerStatsData$RailInfo;",
- (void *)nativeGetRailInfo},
- {"nativeGetEnergyData", "()[Lcom/android/server/powerstats/PowerStatsData$EnergyData;",
- (void *)nativeGetEnergyData},
-};
-
-int register_android_server_PowerStatsService(JNIEnv *env) {
- return jniRegisterNativeMethods(env,
- "com/android/server/powerstats/"
- "PowerStatsHALWrapper$PowerStatsHALWrapperImpl",
- method_table, NELEM(method_table));
-}
-
-}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 48d524433741..0ffa5c39bacc 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -29,7 +29,6 @@ int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
-int register_android_server_PowerStatsService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
@@ -84,7 +83,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_broadcastradio_BroadcastRadioService(env);
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
- register_android_server_PowerStatsService(env);
register_android_server_SerialService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index b7d6424450f3..fb55e75b9ac4 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -20,3 +20,11 @@ xsd_config {
api_dir: "display-device-config/schema",
package_name: "com.android.server.display.config",
}
+
+
+xsd_config {
+ name: "cec-config",
+ srcs: ["cec-config/cec-config.xsd"],
+ api_dir: "cec-config/schema",
+ package_name: "com.android.server.hdmi.cec.config",
+}
diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd
new file mode 100644
index 000000000000..0801c88c54ad
--- /dev/null
+++ b/services/core/xsd/cec-config/cec-config.xsd
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="cec-settings">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="setting" type="setting" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="setting">
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="user-configurable" type="xs:boolean"/>
+ <xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/>
+ </xs:complexType>
+ <xs:complexType name="value-list">
+ <xs:sequence>
+ <xs:element name="value" type="value" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="value">
+ <xs:attribute name="string-value" type="xs:string"/>
+ </xs:complexType>
+</xs:schema>
diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt
new file mode 100644
index 000000000000..34faf459d251
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/current.txt
@@ -0,0 +1,40 @@
+// Signature format: 2.0
+package com.android.server.hdmi.cec.config {
+
+ public class CecSettings {
+ ctor public CecSettings();
+ method public java.util.List<com.android.server.hdmi.cec.config.Setting> getSetting();
+ }
+
+ public class Setting {
+ ctor public Setting();
+ method public com.android.server.hdmi.cec.config.ValueList getAllowedValues();
+ method public com.android.server.hdmi.cec.config.Value getDefaultValue();
+ method public String getName();
+ method public boolean getUserConfigurable();
+ method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList);
+ method public void setDefaultValue(com.android.server.hdmi.cec.config.Value);
+ method public void setName(String);
+ method public void setUserConfigurable(boolean);
+ }
+
+ public class Value {
+ ctor public Value();
+ method public String getStringValue();
+ method public void setStringValue(String);
+ }
+
+ public class ValueList {
+ ctor public ValueList();
+ method public java.util.List<com.android.server.hdmi.cec.config.Value> getValue();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.hdmi.cec.config.CecSettings read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/services/core/xsd/cec-config/schema/last_current.txt b/services/core/xsd/cec-config/schema/last_current.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/last_current.txt
diff --git a/services/core/xsd/cec-config/schema/last_removed.txt b/services/core/xsd/cec-config/schema/last_removed.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/last_removed.txt
diff --git a/services/core/xsd/cec-config/schema/removed.txt b/services/core/xsd/cec-config/schema/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 866d6503960e..e8f12767d5c6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2180,9 +2180,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle, boolean parent) {
ensureLocked();
if (parent) {
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
+ Preconditions.checkCallAuthorization(isManagedProfile(userHandle),
"You can not call APIs on the parent profile outside a managed profile, "
- + "userId = %d", userHandle));
+ + "userId = %d", userHandle);
}
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
if (admin != null && parent) {
@@ -2201,23 +2201,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ensureLocked();
ComponentName doComponent = mOwners.getDeviceOwnerComponent();
Preconditions.checkState(doComponent != null,
- String.format("No device owner for user %d", caller.getUid()));
+ "No device owner for user %d", caller.getUid());
// Use the user ID of the caller instead of mOwners.getDeviceOwnerUserId() because
// secondary, affiliated users will have their own admin.
ActiveAdmin doAdmin = getUserData(caller.getUserId()).mAdminMap.get(doComponent);
Preconditions.checkState(doAdmin != null,
- String.format("Device owner %s for user %d not found", doComponent,
- caller.getUid()));
+ "Device owner %s for user %d not found", doComponent,
+ caller.getUid());
Preconditions.checkCallAuthorization(doAdmin.getUid() == caller.getUid(),
- String.format("Admin %s is not owned by uid %d, but uid %d", doComponent,
- caller.getUid(), doAdmin.getUid()));
+ "Admin %s is not owned by uid %d, but uid %d", doComponent,
+ caller.getUid(), doAdmin.getUid());
Preconditions.checkCallAuthorization(
- doAdmin.info.getComponent().equals(caller.getComponentName()),
- String.format("Caller component %s is not device owner",
- caller.getComponentName()));
+ !caller.hasAdminComponent()
+ || doAdmin.info.getComponent().equals(caller.getComponentName()),
+ "Caller component %s is not device owner",
+ caller.getComponentName());
return doAdmin;
}
@@ -2227,20 +2228,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(caller.getUserId());
Preconditions.checkState(poAdminComponent != null,
- String.format("No profile owner for user %d", caller.getUid()));
+ "No profile owner for user %d", caller.getUid());
ActiveAdmin poAdmin = getUserData(caller.getUserId()).mAdminMap.get(poAdminComponent);
Preconditions.checkState(poAdmin != null,
- String.format("No device profile owner for caller %d", caller.getUid()));
+ "No device profile owner for caller %d", caller.getUid());
Preconditions.checkCallAuthorization(poAdmin.getUid() == caller.getUid(),
- String.format("Admin %s is not owned by uid %d", poAdminComponent,
- caller.getUid()));
+ "Admin %s is not owned by uid %d", poAdminComponent,
+ caller.getUid());
Preconditions.checkCallAuthorization(
- poAdmin.info.getComponent().equals(caller.getComponentName()),
- String.format("Caller component %s is not profile owner",
- caller.getComponentName()));
+ !caller.hasAdminComponent()
+ || poAdmin.info.getComponent().equals(caller.getComponentName()),
+ "Caller component %s is not profile owner",
+ caller.getComponentName());
return poAdmin;
}
@@ -2250,8 +2252,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(
mOwners.isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()),
- String.format("Admin %s is not of an org-owned device",
- profileOwner.info.getComponent()));
+ "Admin %s is not of an org-owned device",
+ profileOwner.info.getComponent());
return profileOwner;
}
@@ -3379,7 +3381,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordQualityCacheForUserGroup(userId);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
});
}
DevicePolicyEventLogger
@@ -3593,7 +3595,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LENGTH)
@@ -3871,7 +3873,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_UPPER_CASE)
@@ -3901,7 +3903,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LOWER_CASE)
@@ -3933,7 +3935,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LETTERS)
@@ -3965,7 +3967,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NUMERIC)
@@ -3997,7 +3999,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_SYMBOLS)
@@ -4030,7 +4032,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+ logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NON_LETTER)
@@ -4160,9 +4162,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
+ Preconditions.checkCallAuthorization(isManagedProfile(userHandle),
"can not call APIs refering to the parent profile outside a managed profile, "
- + "userId = %d", userHandle));
+ + "userId = %d", userHandle);
synchronized (getLockObject()) {
final int targetUser = getProfileParentId(userHandle);
@@ -4184,9 +4186,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
+ Preconditions.checkCallAuthorization(!isManagedProfile(userHandle),
"You can not check password sufficiency for a managed profile, userId = %d",
- userHandle));
+ userHandle);
enforceUserUnlocked(userHandle);
synchronized (getLockObject()) {
@@ -4423,23 +4425,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
// If caller has PO (or DO) throw or fail silently depending on its target SDK level.
- Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller),
- String.format("UID %d is not a device or profile owner", caller.getUid()));
-
- synchronized (getLockObject()) {
- ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
- if (admin != null) {
+ if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
Slog.e(LOG_TAG, "DPC can no longer call resetPassword()");
return false;
}
throw new SecurityException("Device admin can no longer call resetPassword()");
}
+ }
+ // Caller is not DO or PO, could either be unauthorized or Device Admin.
+ synchronized (getLockObject()) {
// Legacy device admin cannot call resetPassword either
- admin = getActiveAdminForCallerLocked(
+ ActiveAdmin admin = getActiveAdminForCallerLocked(
null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, false);
+ Preconditions.checkCallAuthorization(admin != null,
+ "Unauthorized caller cannot call resetPassword.");
if (getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M) {
Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()");
@@ -5017,7 +5020,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ApplicationInfo ai = mInjector.getIPackageManager().getApplicationInfo(
packageName, 0, caller.getUserId());
Preconditions.checkArgument(ai != null,
- String.format("Provided package %s is not installed", packageName));
+ "Provided package %s is not installed", packageName);
granteeUid = ai.uid;
} catch (RemoteException e) {
throw new IllegalStateException("Failure getting grantee uid", e);
@@ -5635,7 +5638,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private boolean isCallerDelegate(CallerIdentity caller, String scope) {
Objects.requireNonNull(caller.getPackageName(), "callerPackage is null");
Preconditions.checkArgument(Arrays.asList(DELEGATIONS).contains(scope),
- String.format("Unexpected delegation scope: %s", scope));
+ "Unexpected delegation scope: %s", scope);
synchronized (getLockObject()) {
// Retrieve user policy data.
@@ -5885,7 +5888,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA);
}
Preconditions.checkCallAuthorization(admin != null,
- String.format("No active admin for user %d", caller.getUserId()));
+ "No active admin for user %d", caller.getUserId());
if (TextUtils.isEmpty(wipeReasonForUser)) {
if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) {
@@ -6111,8 +6114,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(isSystemUid(caller));
// Managed Profile password can only be changed when it has a separate challenge.
if (!isSeparateProfileChallengeEnabled(userId)) {
- Preconditions.checkCallAuthorization(!isManagedProfile(userId), String.format("You can "
- + "not set the active password for a managed profile, userId = %d", userId));
+ Preconditions.checkCallAuthorization(!isManagedProfile(userId), "You can "
+ + "not set the active password for a managed profile, userId = %d", userId);
}
DevicePolicyData policy = getUserData(userId);
@@ -6165,9 +6168,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
if (!isSeparateProfileChallengeEnabled(userHandle)) {
- Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
+ Preconditions.checkCallAuthorization(!isManagedProfile(userHandle),
"You can not report failed password attempt if separate profile challenge is "
- + "not in place for a managed profile, userId = %d", userHandle));
+ + "not in place for a managed profile, userId = %d", userHandle);
}
boolean wipeData = false;
@@ -7888,7 +7891,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Current user has a managed-profile, but current user is not managed, so
// rather than moving to finalized state, go back to unmanaged once
// profile provisioning is complete.
- if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
+ if (newState == DevicePolicyManager.STATE_USER_PROFILE_FINALIZED) {
return;
}
break;
@@ -10003,9 +10006,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean result;
synchronized (getLockObject()) {
Preconditions.checkCallAuthorization(
- isUserAffiliatedWithDeviceLocked(caller.getUserId()), String.format(
+ isUserAffiliatedWithDeviceLocked(caller.getUserId()),
"Admin %s is neither the device owner or "
- + "affiliated user's profile owner.", who));
+ + "affiliated user's profile owner.", who);
final long id = mInjector.binderClearCallingIdentity();
try {
if (VERBOSE_LOG) {
@@ -10387,12 +10390,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
- String.format("User %d is not allowed to call setSecondaryLockscreenEnabled",
- caller.getUserId()));
+ "User %d is not allowed to call setSecondaryLockscreenEnabled",
+ caller.getUserId());
// Allow testOnly admins to bypass supervision config requirement.
Preconditions.checkCallAuthorization(isAdminTestOnlyLocked(who, caller.getUserId())
- || isDefaultSupervisor(caller), String.format("Admin %s is not the "
- + "default supervision component", caller.getComponentName()));
+ || isDefaultSupervisor(caller), "Admin %s is not the "
+ + "default supervision component", caller.getComponentName());
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(caller.getUserId());
@@ -12383,8 +12386,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
Preconditions.checkCallAuthorization(canManageUsers(caller));
- Preconditions.checkCallAuthorization(isManagedProfile(userId), String.format("You can not "
- + "set organization color outside a managed profile, userId = %d", userId));
+ Preconditions.checkCallAuthorization(isManagedProfile(userId), "You can not "
+ + "set organization color outside a managed profile, userId = %d", userId);
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
@@ -12418,8 +12421,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format("You can "
- + "not get organization color outside a managed profile, userId = %d", userHandle));
+ Preconditions.checkCallAuthorization(isManagedProfile(userHandle), "You can "
+ + "not get organization color outside a managed profile, userId = %d", userHandle);
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12484,9 +12487,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
+ Preconditions.checkCallAuthorization(isManagedProfile(userHandle),
"You can not get organization name outside a managed profile, userId = %d",
- userHandle));
+ userHandle);
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12502,7 +12505,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(packageNames);
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
- String.format("Admin %s does not own the profile", caller.getComponentName()));
+ "Admin %s does not own the profile", caller.getComponentName());
if (!mHasFeature) {
return packageNames;
@@ -12553,7 +12556,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
- String.format("Admin %s does not own the profile", caller.getComponentName()));
+ "Admin %s does not own the profile", caller.getComponentName());
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -14223,8 +14226,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
parametersFile.delete();
}
- private void maybeLogPasswordComplexitySet(ComponentName who, int userId, boolean parent,
- PasswordPolicy passwordPolicy) {
+ private void logPasswordQualitySetIfSecurityLogEnabled(ComponentName who, int userId,
+ boolean parent, PasswordPolicy passwordPolicy) {
if (SecurityLog.isLoggingEnabled()) {
final int affectedUserId = parent ? getProfileParentId(userId) : userId;
SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_SET, who.getPackageName(),
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
index 8a22a2f5d92f..044bdbadb946 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -16,10 +16,16 @@
package com.android.server;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.CombinedVibrationEffect;
+import android.os.Process;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -40,9 +46,16 @@ import org.mockito.junit.MockitoRule;
@Presubmit
public class VibratorManagerServiceTest {
- @Rule public MockitoRule rule = MockitoJUnit.rule();
+ private static final int UID = Process.ROOT_UID;
+ private static final String PACKAGE_NAME = "package";
+ private static final VibrationAttributes ALARM_ATTRS =
+ new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
- @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
+ @Mock
+ private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@Before
public void setUp() throws Exception {
@@ -72,7 +85,26 @@ public class VibratorManagerServiceTest {
@Test
public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() {
- when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{ 1, 2 });
- assertArrayEquals(new int[]{ 1, 2 }, createService().getVibratorIds());
+ when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{1, 2});
+ assertArrayEquals(new int[]{1, 2}, createService().getVibratorIds());
+ }
+
+ @Test
+ public void vibrate_isUnsupported() {
+ VibratorManagerService service = createService();
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ assertExpectException(UnsupportedOperationException.class,
+ "Not implemented",
+ () -> service.vibrate(UID, PACKAGE_NAME, effect, ALARM_ATTRS, "reason", service));
+ }
+
+ @Test
+ public void cancelVibrate_isUnsupported() {
+ VibratorManagerService service = createService();
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ assertExpectException(UnsupportedOperationException.class,
+ "Not implemented", () -> service.cancelVibrate(service));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index d57fd4b467db..3b4699e70aec 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -62,6 +62,8 @@ public class ActivityManagerInternalTest {
.getContext();
doReturn(mServiceThreadRule.getThread().getThreadHandler()).when(mMockInjector)
.getUiHandler(any());
+ final ProcessList dummyList = new ProcessList();
+ doReturn(dummyList).when(mMockInjector).getProcessList(any());
mAms = new ActivityManagerService(mMockInjector, mServiceThreadRule.getThread());
mAmi = mAms.new LocalService();
}
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index 4221575a9922..693bc7dc4a42 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -23,7 +23,7 @@ import static com.android.server.am.ActivityManagerService.Injector;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -68,6 +68,7 @@ public class CoreSettingsObserverTest {
private ActivityManagerService mAms;
@Mock private Context mContext;
+ @Mock private Resources mResources;
private MockContentResolver mContentResolver;
private CoreSettingsObserver mCoreSettingsObserver;
@@ -94,7 +95,10 @@ public class CoreSettingsObserverTest {
mContentResolver = new MockContentResolver(mContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
- when(mContext.getResources()).thenReturn(mock(Resources.class));
+ when(mContext.getResources()).thenReturn(mResources);
+ // To prevent NullPointerException at the constructor of ActivityManagerConstants.
+ when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
+ when(mResources.getIntArray(anyInt())).thenReturn(new int[0]);
mAms = new ActivityManagerService(new TestInjector(mContext),
mServiceThreadRule.getThread());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 30b1b3e78ad3..c4f7b9547277 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2814,7 +2814,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
exerciseUserProvisioningTransitions(CALLER_USER_HANDLE,
DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
- DevicePolicyManager.STATE_USER_UNMANAGED);
+ DevicePolicyManager.STATE_USER_PROFILE_FINALIZED);
}
public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
new file mode 100644
index 000000000000..bbda1684f8a5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
+import android.util.Slog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public final class HdmiCecConfigTest {
+ private static final String TAG = "HdmiCecConfigTest";
+
+ private Context mContext;
+
+ @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void getAllCecSettings_Empty() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
+ }
+
+ @Test
+ public void getAllCecSettings_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"0\" />"
+ + " <value string-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"1\" />"
+ + " </setting>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllSettings())
+ .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ }
+
+ @Test
+ public void getUserCecSettings_Empty() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
+ }
+
+ @Test
+ public void getUserCecSettings_OnlyMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"0\" />"
+ + " <value string-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"1\" />"
+ + " </setting>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getUserSettings())
+ .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ }
+
+ @Test
+ public void getUserCecSettings_WithOverride() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"0\" />"
+ + " <value string-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"1\" />"
+ + " </setting>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>",
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>");
+ assertThat(hdmiCecConfig.getUserSettings())
+ .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+ }
+
+ @Test
+ public void getAllowedValues_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getAllowedValues("foo"));
+ }
+
+ @Test
+ public void getAllowedValues_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllowedValues(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+ .containsExactly(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+ }
+
+ @Test
+ public void getDefaultValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getDefaultValue("foo"));
+ }
+
+ @Test
+ public void getDefaultValue_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getDefaultValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+ .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ }
+
+ @Test
+ public void getValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getValue(mContext, "foo"));
+ }
+
+ @Test
+ public void getValue_GlobalSetting_BasicSanity() {
+ when(mStorageAdapter.retrieveGlobalSetting(mContext,
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV))
+ .thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getValue(mContext,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+ .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ }
+
+ @Test
+ public void getValue_SystemProperty_BasicSanity() {
+ when(mStorageAdapter.retrieveSystemProperty(
+ HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiProperties.power_state_change_on_active_source_lost_values
+ .NONE.name().toLowerCase()))
+ .thenReturn(HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"power_state_change_on_active_source_lost\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"none\" />"
+ + " <value string-value=\"standby_now\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"none\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getValue(mContext,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
+ .isEqualTo(HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ }
+
+ @Test
+ public void setValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setValue(mContext, "foo", "bar"));
+ }
+
+ @Test
+ public void setValue_NotConfigurable() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setValue(mContext,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST));
+ }
+
+ @Test
+ public void setValue_InvalidValue() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setValue(mContext,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ "bar"));
+ }
+
+ @Test
+ public void setValue_GlobalSetting_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ hdmiCecConfig.setValue(mContext,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ verify(mStorageAdapter).storeGlobalSetting(mContext,
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ }
+
+ @Test
+ public void setValue_SystemProperty_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"power_state_change_on_active_source_lost\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"none\" />"
+ + " <value string-value=\"standby_now\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"none\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ hdmiCecConfig.setValue(mContext,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ verify(mStorageAdapter).storeSystemProperty(
+ HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ }
+
+ private HdmiCecConfig createHdmiCecConfig(String productConfigXml, String vendorOverrideXml) {
+ CecSettings productConfig = null;
+ CecSettings vendorOverride = null;
+ try {
+ productConfig = XmlParser.read(new ByteArrayInputStream(productConfigXml.getBytes()));
+ if (vendorOverrideXml != null) {
+ vendorOverride = XmlParser.read(
+ new ByteArrayInputStream(vendorOverrideXml.getBytes()));
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+ }
+ return new HdmiCecConfig(productConfig, vendorOverride, mStorageAdapter);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index e4acdfe93fd4..f78c01a8f281 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -177,6 +177,12 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
}
@Override
+ public Context createContextAsUser(UserHandle user, int flags) {
+ when(mMockPackageManager.getUserId()).thenReturn(user.getIdentifier());
+ return this;
+ }
+
+ @Override
public void unregisterReceiver(BroadcastReceiver receiver) {
// ignore.
}
@@ -939,7 +945,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
final String packageName = (String) pmInvocation.getArguments()[0];
- final int userId = (Integer) pmInvocation.getArguments()[1];
+ final int userId = mMockPackageManager.getUserId();
final Resources res = mock(Resources.class);
@@ -971,7 +977,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
return Integer.parseInt(entryName.substring(1)) + ressIdOffset;
}).when(res).getIdentifier(anyStringOrNull(), anyStringOrNull(), anyStringOrNull());
return res;
- }).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
+ }).when(mMockPackageManager).getResourcesForApplication(anyString());
}
protected static UserInfo withProfileGroupId(UserInfo in, int groupId) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 35d6f470a504..d54a40e58af1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -16,12 +16,21 @@
package com.android.server.pm;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static java.lang.reflect.Modifier.isFinal;
+import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
+
import android.content.IIntentReceiver;
+import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.util.SparseArray;
import androidx.test.runner.AndroidJUnit4;
+import com.google.android.collect.Lists;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -29,6 +38,12 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
// runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services
// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
@@ -133,4 +148,51 @@ public class PackageManagerServiceTest {
}
}
}
+
+ @Test
+ public void testKnownPackageToString_shouldNotGetUnknown() {
+ final List<String> packageNames = new ArrayList<>();
+ for (int i = 0; i <= PackageManagerInternal.LAST_KNOWN_PACKAGE; i++) {
+ packageNames.add(PackageManagerInternal.knownPackageToString(i));
+ }
+ assertWithMessage(
+ "The Ids of KnownPackage should be continuous and the string representation "
+ + "should not be unknown.").that(
+ packageNames).containsNoneIn(Lists.newArrayList("Unknown"));
+ }
+
+ @Test
+ public void testKnownPackage_lastKnownPackageIsTheLast() throws Exception {
+ final List<Integer> knownPackageIds = getKnownPackageIdsList();
+ assertWithMessage(
+ "The last KnownPackage Id should be assigned to PackageManagerInternal"
+ + ".LAST_KNOWN_PACKAGE.").that(
+ knownPackageIds.get(knownPackageIds.size() - 1)).isEqualTo(
+ PackageManagerInternal.LAST_KNOWN_PACKAGE);
+ }
+
+ @Test
+ public void testKnownPackage_IdsShouldBeUniqueAndContinuous() throws Exception {
+ final List<Integer> knownPackageIds = getKnownPackageIdsList();
+ for (int i = 0, size = knownPackageIds.size(); i < size - 1; i++) {
+ assertWithMessage(
+ "The KnownPackage Ids should be unique and continuous. KnownPackageIds = "
+ + Arrays.toString(knownPackageIds.toArray())).that(
+ knownPackageIds.get(i) + 1).isEqualTo(knownPackageIds.get(i + 1));
+ }
+ }
+
+ private List<Integer> getKnownPackageIdsList() throws IllegalAccessException {
+ final ArrayList<Integer> knownPackageIds = new ArrayList<>();
+ final Field[] allFields = PackageManagerInternal.class.getDeclaredFields();
+ for (Field field : allFields) {
+ final int modifier = field.getModifiers();
+ if (isPublic(modifier) && isStatic(modifier) && isFinal(modifier)
+ && Pattern.matches("PACKAGE(_[A-Z]+)+", field.getName())) {
+ knownPackageIds.add(field.getInt(null));
+ }
+ }
+ Collections.sort(knownPackageIds);
+ return knownPackageIds;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 3221a4d4f12c..59aff8d43755 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -20,12 +20,16 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import androidx.test.InstrumentationRegistry;
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
-import com.android.server.powerstats.nano.PowerStatsServiceProto;
+import com.android.server.powerstats.nano.PowerStatsServiceMeterProto;
+import com.android.server.powerstats.nano.PowerStatsServiceModelProto;
import org.junit.Before;
import org.junit.Test;
@@ -48,11 +52,12 @@ import java.util.Random;
public class PowerStatsServiceTest {
private static final String TAG = PowerStatsServiceTest.class.getSimpleName();
private static final String DATA_STORAGE_SUBDIR = "powerstatstest";
- private static final String DATA_STORAGE_FILENAME = "test";
+ private static final String METER_FILENAME = "metertest";
+ private static final String MODEL_FILENAME = "modeltest";
private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto";
- private static final String RAIL_NAME = "railname";
- private static final String SUBSYS_NAME = "subsysname";
- private static final int POWER_RAIL_COUNT = 8;
+ private static final String CHANNEL_NAME = "channelname";
+ private static final int ENERGY_METER_COUNT = 8;
+ private static final int ENERGY_CONSUMER_COUNT = 2;
private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
private PowerStatsService mService;
@@ -75,8 +80,13 @@ public class PowerStatsServiceTest {
}
@Override
- String createDataStorageFilename() {
- return DATA_STORAGE_FILENAME;
+ String createMeterFilename() {
+ return METER_FILENAME;
+ }
+
+ @Override
+ String createModelFilename() {
+ return MODEL_FILENAME;
}
@Override
@@ -86,9 +96,10 @@ public class PowerStatsServiceTest {
@Override
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
- mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
- powerStatsHALWrapper);
+ String meterFilename, String modelFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
+ mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, meterFilename,
+ modelFilename, powerStatsHALWrapper);
return mPowerStatsLogger;
}
@@ -107,23 +118,48 @@ public class PowerStatsServiceTest {
public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper {
@Override
- public PowerStatsData.RailInfo[] readRailInfo() {
- PowerStatsData.RailInfo[] railInfoArray = new PowerStatsData.RailInfo[POWER_RAIL_COUNT];
- for (int i = 0; i < POWER_RAIL_COUNT; i++) {
- railInfoArray[i] = new PowerStatsData.RailInfo(i, RAIL_NAME + i, SUBSYS_NAME + i,
- i);
+ public int[] getEnergyConsumerInfo() {
+ int[] energyConsumerInfoList = new int[ENERGY_CONSUMER_COUNT];
+ for (int i = 0; i < energyConsumerInfoList.length; i++) {
+ energyConsumerInfoList[i] = i;
+ }
+ return energyConsumerInfoList;
+ }
+
+ @Override
+ public EnergyConsumerResult[] getEnergyConsumed() {
+ EnergyConsumerResult[] energyConsumedList =
+ new EnergyConsumerResult[ENERGY_CONSUMER_COUNT];
+ for (int i = 0; i < energyConsumedList.length; i++) {
+ energyConsumedList[i] = new EnergyConsumerResult();
+ energyConsumedList[i].energyConsumerId = i;
+ energyConsumedList[i].timestampMs = i;
+ energyConsumedList[i].energyUWs = i;
}
- return railInfoArray;
+ return energyConsumedList;
}
@Override
- public PowerStatsData.EnergyData[] readEnergyData() {
- PowerStatsData.EnergyData[] energyDataArray =
- new PowerStatsData.EnergyData[POWER_RAIL_COUNT];
- for (int i = 0; i < POWER_RAIL_COUNT; i++) {
- energyDataArray[i] = new PowerStatsData.EnergyData(i, i, i);
+ public ChannelInfo[] getEnergyMeterInfo() {
+ ChannelInfo[] energyMeterInfoList = new ChannelInfo[ENERGY_METER_COUNT];
+ for (int i = 0; i < energyMeterInfoList.length; i++) {
+ energyMeterInfoList[i] = new ChannelInfo();
+ energyMeterInfoList[i].channelId = i;
+ energyMeterInfoList[i].channelName = new String(CHANNEL_NAME + i);
}
- return energyDataArray;
+ return energyMeterInfoList;
+ }
+
+ @Override
+ public EnergyMeasurement[] readEnergyMeters() {
+ EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT];
+ for (int i = 0; i < energyMeasurementList.length; i++) {
+ energyMeasurementList[i] = new EnergyMeasurement();
+ energyMeasurementList[i].channelId = i;
+ energyMeasurementList[i].timestampMs = i;
+ energyMeasurementList[i].energyUWs = i;
+ }
+ return energyMeasurementList;
}
@Override
@@ -138,7 +174,7 @@ public class PowerStatsServiceTest {
}
@Test
- public void testWrittenPowerStatsHALDataMatchesReadIncidentReportData()
+ public void testWrittenMeterDataMatchesReadIncidentReportData()
throws InterruptedException, IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -152,36 +188,116 @@ public class PowerStatsServiceTest {
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream fos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(fos.getFD());
+ mPowerStatsLogger.writeMeterDataToFile(fos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
+
+ // Validate the channelInfo array matches what was written to on-device storage.
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
+ }
- // Validate the railInfo array matches what was written to on-device storage.
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Validate the energyMeasurement array matches what was written to on-device storage.
+ assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.energyMeasurement.length; i++) {
+ assertTrue(pssProto.energyMeasurement[i].channelId == i);
+ assertTrue(pssProto.energyMeasurement[i].timestampMs == i);
+ assertTrue(pssProto.energyMeasurement[i].energyUws == i);
}
+ }
+
+ @Test
+ public void testWrittenModelDataMatchesReadIncidentReportData()
+ throws InterruptedException, IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Write data to on-device storage.
+ mTimerTrigger.logPowerStatsData();
+
+ // The above call puts a message on a handler. Wait for
+ // it to be processed.
+ Thread.sleep(100);
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream fos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeModelDataToFile(fos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Validate the energyConsumerId array matches what was written to on-device storage.
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+ }
+
+ // Validate the energyConsumerResult array matches what was written to on-device storage.
+ assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerResult.length; i++) {
+ assertTrue(pssProto.energyConsumerResult[i].energyConsumerId == i);
+ assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
+ assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
+ }
+ }
+
+ @Test
+ public void testCorruptOnDeviceMeterStorage() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Generate random array of bytes to emulate corrupt data.
+ Random rd = new Random();
+ byte[] bytes = new byte[100];
+ rd.nextBytes(bytes);
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
- // Validate the energyData array matches what was written to on-device storage.
- assertTrue(pssProto.energyData.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.energyData.length; i++) {
- assertTrue(pssProto.energyData[i].index == i);
- assertTrue(pssProto.energyData[i].timestampMs == i);
- assertTrue(pssProto.energyData[i].energyUws == i);
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
+
+ // Valid channelInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeMeterDataToFile().
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
}
+
+ // No energyMeasurements should be written to the incident report since it
+ // is all corrupt (random bytes generated above).
+ assertTrue(pssProto.energyMeasurement.length == 0);
}
@Test
- public void testCorruptOnDeviceStorage() throws IOException {
+ public void testCorruptOnDeviceModelStorage() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Generate random array of bytes to emulate corrupt data.
@@ -191,7 +307,7 @@ public class PowerStatsServiceTest {
// Store corrupt data in on-device storage. Add fake timestamp to filename
// to match format expected by FileRotator.
- File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+ File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
onDeviceStorageFos.write(bytes);
onDeviceStorageFos.close();
@@ -199,33 +315,30 @@ public class PowerStatsServiceTest {
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+ mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
-
- // Valid railInfo data is written to the incident report in the call to
- // mPowerStatsLogger.writeToFile().
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Valid energyConsumerId data is written to the incident report in the call to
+ // mPowerStatsLogger.writeModelDataToFile().
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
}
- // No energyData should be written to the incident report since it
+ // No energyConsumerResults should be written to the incident report since it
// is all corrupt (random bytes generated above).
- assertTrue(pssProto.energyData.length == 0);
+ assertTrue(pssProto.energyConsumerResult.length == 0);
}
@Test
- public void testNotEnoughBytesAfterLengthField() throws IOException {
+ public void testNotEnoughBytesAfterMeterLengthField() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Create corrupt data.
@@ -236,7 +349,7 @@ public class PowerStatsServiceTest {
// Store corrupt data in on-device storage. Add fake timestamp to filename
// to match format expected by FileRotator.
- File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+ File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
onDeviceStorageFos.write(data.toByteArray());
onDeviceStorageFos.close();
@@ -244,28 +357,68 @@ public class PowerStatsServiceTest {
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+ mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
-
- // Valid railInfo data is written to the incident report in the call to
- // mPowerStatsLogger.writeToFile().
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
+
+ // Valid channelInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeMeterDataToFile().
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
+ }
+
+ // No energyMeasurements should be written to the incident report since the
+ // input buffer had only length and no data.
+ assertTrue(pssProto.energyMeasurement.length == 0);
+ }
+
+ @Test
+ public void testNotEnoughBytesAfterModelLengthField() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Create corrupt data.
+ // Length field is correct, but there is no data following the length.
+ ByteArrayOutputStream data = new ByteArrayOutputStream();
+ data.write(ByteBuffer.allocate(4).putInt(50).array());
+ byte[] test = data.toByteArray();
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(data.toByteArray());
+ onDeviceStorageFos.close();
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Valid energyConsumerId data is written to the incident report in the call to
+ // mPowerStatsLogger.writeModelDataToFile().
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
}
- // No energyData should be written to the incident report since the
+ // No energyConsumerResults should be written to the incident report since the
// input buffer had only length and no data.
- assertTrue(pssProto.energyData.length == 0);
+ assertTrue(pssProto.energyConsumerResult.length == 0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index 8cba69f6ddcb..3530e38ef67c 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -34,7 +34,6 @@ public class TestSystemImpl implements SystemInterface {
List<Integer> mUsers = new ArrayList<>();
// Package -> [user, package]
Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
- private boolean mFallbackLogicEnabled;
private final int mNumRelros;
private final boolean mIsDebuggable;
private int mMultiProcessSetting;
@@ -42,10 +41,9 @@ public class TestSystemImpl implements SystemInterface {
public static final int PRIMARY_USER_ID = 0;
- public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
- int numRelros, boolean isDebuggable, boolean multiProcessDefault) {
+ public TestSystemImpl(WebViewProviderInfo[] packageConfigs, int numRelros, boolean isDebuggable,
+ boolean multiProcessDefault) {
mPackageConfigs = packageConfigs;
- mFallbackLogicEnabled = fallbackLogicEnabled;
mNumRelros = numRelros;
mIsDebuggable = isDebuggable;
mUsers.add(PRIMARY_USER_ID);
@@ -78,16 +76,6 @@ public class TestSystemImpl implements SystemInterface {
public void killPackageDependents(String packageName) {}
@Override
- public boolean isFallbackLogicEnabled() {
- return mFallbackLogicEnabled;
- }
-
- @Override
- public void enableFallbackLogic(boolean enable) {
- mFallbackLogicEnabled = enable;
- }
-
- @Override
public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
for(int userId : mUsers) {
enablePackageForUser(packageName, enable, userId);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index bbfc5ab42d61..ebe45a6fa1e8 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -67,36 +67,30 @@ public class WebViewUpdateServiceTest {
}
private void setupWithPackages(WebViewProviderInfo[] packages) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
- true /* isDebuggable */, false /* multiProcessDefault */);
- }
-
- private void setupWithPackagesAndFallbackLogic(WebViewProviderInfo[] packages) {
- setupWithAllParameters(packages, true /* fallbackLogicEnabled */, 1 /* numRelros */,
- true /* isDebuggable */, false /* multiProcessDefault */);
+ setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
+ false /* multiProcessDefault */);
}
private void setupWithPackagesAndRelroCount(WebViewProviderInfo[] packages, int numRelros) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, numRelros,
- true /* isDebuggable */, false /* multiProcessDefault */);
+ setupWithAllParameters(packages, numRelros, true /* isDebuggable */,
+ false /* multiProcessDefault */);
}
private void setupWithPackagesNonDebuggable(WebViewProviderInfo[] packages) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
- false /* isDebuggable */, false /* multiProcessDefault */);
+ setupWithAllParameters(packages, 1 /* numRelros */, false /* isDebuggable */,
+ false /* multiProcessDefault */);
}
private void setupWithPackagesAndMultiProcess(WebViewProviderInfo[] packages,
boolean multiProcessDefault) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
- true /* isDebuggable */, multiProcessDefault);
+ setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
+ multiProcessDefault);
}
- private void setupWithAllParameters(WebViewProviderInfo[] packages,
- boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable,
- boolean multiProcessDefault) {
- TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
- isDebuggable, multiProcessDefault);
+ private void setupWithAllParameters(WebViewProviderInfo[] packages, int numRelros,
+ boolean isDebuggable, boolean multiProcessDefault) {
+ TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable,
+ multiProcessDefault);
mTestSystemImpl = Mockito.spy(testing);
mWebViewUpdateServiceImpl =
new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
@@ -514,49 +508,29 @@ public class WebViewUpdateServiceTest {
}
/**
- * Scenario for testing migrating away from the fallback logic.
- * We start with a primary package that's a disabled fallback, and an enabled secondary,
- * so that the fallback being re-enabled will cause a provider switch, as that covers
- * the most complex case.
+ * Scenario for testing re-enabling a fallback package.
*/
@Test
- public void testFallbackLogicMigration() {
- String primaryPackage = "primary";
- String secondaryPackage = "secondary";
+ public void testFallbackPackageEnabling() {
+ String testPackage = "testFallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(
- primaryPackage, "", true /* default available */, true /* fallback */, null),
- new WebViewProviderInfo(
- secondaryPackage, "", true /* default available */, false /* fallback */,
- null)};
- setupWithPackagesAndFallbackLogic(packages);
- mTestSystemImpl.setPackageInfo(
- createPackageInfo(primaryPackage, false /* enabled */ , true /* valid */,
- true /* installed */));
+ testPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages);
mTestSystemImpl.setPackageInfo(
- createPackageInfo(secondaryPackage, true /* enabled */ , true /* valid */,
+ createPackageInfo(testPackage, false /* enabled */ , true /* valid */,
true /* installed */));
- // Check that the boot time logic re-enables and chooses the primary, and disables the
- // fallback logic.
+ // Check that the boot time logic re-enables the fallback package.
runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
- Matchers.anyObject(), Mockito.eq(primaryPackage), Mockito.eq(true));
- checkPreparationPhasesForPackage(primaryPackage, 1);
- assertFalse(mTestSystemImpl.isFallbackLogicEnabled());
+ Matchers.anyObject(), Mockito.eq(testPackage), Mockito.eq(true));
- // Disable primary again
- mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, false /* enabled */,
- true /* valid */, true /* installed */));
- mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
- WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
- checkPreparationPhasesForPackage(secondaryPackage, 1);
-
- // Run boot logic again and check that we didn't re-enable the primary a second time.
- runWebViewBootPreparationOnMainSync();
- Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForAllUsers(
- Matchers.anyObject(), Mockito.eq(primaryPackage), Mockito.eq(true));
- checkPreparationPhasesForPackage(secondaryPackage, 2);
+ // Fake the message about the enabling having changed the package state,
+ // and check we now use that package.
+ mWebViewUpdateServiceImpl.packageStateChanged(
+ testPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
+ checkPreparationPhasesForPackage(testPackage, 1);
}
/**
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 ad9692f404e9..dd4d718084ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1525,7 +1525,6 @@ public class ActivityRecordTests extends WindowTestsBase {
any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */,
any() /* requestedVisibility */, any() /* outFrame */,
- any() /* outContentInsets */, any() /* outStableInsets */,
any() /* outDisplayCutout */, any() /* outInputChannel */,
any() /* outInsetsState */, any() /* outActiveControls */);
TaskSnapshotSurface.create(mAtm.mWindowManager, mActivity, snapshot);
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 3dc258c51954..caf8a720e26c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -70,8 +70,6 @@ import android.content.pm.ActivityInfo;
import android.os.Binder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl;
-import android.window.ITaskOrganizer;
import androidx.test.filters.SmallTest;
@@ -242,24 +240,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testRemoveOrganizedTask_UpdateStackReference() {
- ITaskOrganizer listener = new ITaskOrganizer.Stub() {
- @Override
- public void onTaskAppeared(
- ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo container) { }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
- }
-
- @Override
- public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
- }
- };
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
-
final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
.setStack(rootHomeTask)
@@ -267,7 +247,7 @@ public class ActivityStackTests extends WindowTestsBase {
.build();
final Task secondaryStack = (Task) WindowContainer.fromBinder(
mAtm.mTaskOrganizerController.createRootTask(rootHomeTask.getDisplayId(),
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo().token.asBinder());
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token.asBinder());
rootHomeTask.reparent(secondaryStack, POSITION_TOP);
assertEquals(secondaryStack, rootHomeTask.getParent());
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 8292420c9e0a..3d31824e5aae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -54,6 +54,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.MockitoSession;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Tests for the {@link ActivityTaskManagerService} class.
@@ -261,15 +262,17 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
public void testUpdateSleep() {
doCallRealMethod().when(mWm.mRoot).hasAwakeDisplay();
mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class);
+ final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
+ .setTask(mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()).build();
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
topActivity.setState(Task.ActivityState.RESUMED, "test");
- final Runnable assertTopNonSleeping = () -> {
+ final Consumer<ActivityRecord> assertTopNonSleeping = activity -> {
assertFalse(mAtm.mInternal.isSleeping());
assertEquals(ActivityManager.PROCESS_STATE_TOP, mAtm.mInternal.getTopProcessState());
- assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+ assertEquals(activity.app, mAtm.mInternal.getTopApp());
};
- assertTopNonSleeping.run();
+ assertTopNonSleeping.accept(topActivity);
// Sleep all displays.
mWm.mRoot.forAllDisplays(display -> doReturn(true).when(display).shouldSleep());
@@ -279,13 +282,18 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
assertTrue(mAtm.mInternal.isSleeping());
assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
mAtm.mInternal.getTopProcessState());
- assertNull(mAtm.mInternal.getTopApp());
+ // The top app should not change while sleeping.
+ assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+
+ // Move the current top to back, the top app should update to the next activity.
+ topActivity.getRootTask().moveToBack("test", null /* self */);
+ assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
// Wake all displays.
mWm.mRoot.forAllDisplays(display -> doReturn(false).when(display).shouldSleep());
mAtm.updateSleepIfNeededLocked();
- assertTopNonSleeping.run();
+ assertTopNonSleeping.accept(homeActivity);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 5a14a249e78f..3598cd8d841d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -157,6 +157,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mDisplayBounds.set(0, 0, mFrames.mDisplayWidth, mFrames.mDisplayHeight);
mDisplayContent.mDisplayFrames = mFrames;
mDisplayContent.setBounds(mDisplayBounds);
+ mDisplayContent.getInsetsStateController().getRawInsetsState().setDisplayFrame(
+ mDisplayBounds);
}
private DisplayFrames createDisplayFrames() {
@@ -339,7 +341,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitInsetsIgnoringVisibility() {
final InsetsState state =
- mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
+ mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
@@ -359,7 +361,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
final InsetsState state =
- mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
+ mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
@@ -520,7 +522,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
+ mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow)
.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
@@ -544,7 +546,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
+ mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow)
.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
@@ -774,31 +776,30 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutHint_appWindow() {
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.setFitInsetsTypes(0);
// Initialize DisplayFrames
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
final DisplayCutout.ParcelableWrapper outDisplayCutout =
new DisplayCutout.ParcelableWrapper();
+ final InsetsState outState = new InsetsState();
mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outFrame,
- outContentInsets, outStableInsets, outDisplayCutout);
+ outDisplayCutout, outState, true /* localClient */);
- assertThat(outFrame, is(mFrames.mUnrestricted));
- assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
- assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+ assertThat(outFrame, is(outState.getDisplayFrame()));
assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
+ is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
+ assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
+ is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
}
@Test
public void layoutHint_appWindowInTask() {
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.setFitInsetsTypes(0);
// Initialize DisplayFrames
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -809,24 +810,24 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
task.getWindowConfiguration().setBounds(taskBounds);
final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
final DisplayCutout.ParcelableWrapper outDisplayCutout =
new DisplayCutout.ParcelableWrapper();
+ final InsetsState outState = new InsetsState();
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame,
- outContentInsets, outStableInsets, outDisplayCutout);
+ mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outDisplayCutout,
+ outState, true /* localClient */);
assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
+ is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
+ assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
+ is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
}
@Test
public void layoutHint_appWindowInTask_outsideContentFrame() {
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.setFitInsetsTypes(0);
// Initialize DisplayFrames
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -843,18 +844,19 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
task.getWindowConfiguration().setBounds(taskBounds);
final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
final DisplayCutout.ParcelableWrapper outDisplayCutout =
new DisplayCutout.ParcelableWrapper();
+ final InsetsState outState = new InsetsState();
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outContentInsets,
- outStableInsets, outDisplayCutout);
+ mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outDisplayCutout,
+ outState, true /* localClient */);
assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
+ is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
+ assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
+ is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 9b2a2db5d3a8..e1aca55762d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -664,20 +664,20 @@ public class DisplayRotationTests {
// Non-rotation API Tests
// ========================
@Test
- public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+ public void testIsNotFixedToUserRotationByDefault() throws Exception {
mBuilder.build();
- assertTrue("Display rotation should respect app requested orientation by"
- + " default.", mTarget.respectAppRequestedOrientation());
+ assertFalse("Display rotation should respect app requested orientation by"
+ + " default.", mTarget.isFixedToUserRotation());
}
@Test
- public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+ public void testIsFixedToUserRotation() throws Exception {
mBuilder.build();
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- assertFalse("Display rotation shouldn't respect app requested orientation if"
- + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+ assertTrue("Display rotation shouldn't respect app requested orientation if"
+ + " fixed to user rotation.", mTarget.isFixedToUserRotation());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index d67120f53917..a1606d3502ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -321,7 +321,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertNull(controls[i].getLeash());
}
- final InsetsState state = policy.getInsetsForDispatch(mAppWindow);
+ final InsetsState state = policy.getInsetsForWindow(mAppWindow);
state.setSourceVisible(ITYPE_STATUS_BAR, true);
state.setSourceVisible(ITYPE_NAVIGATION_BAR, true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index c14df676f525..90caf35e2936 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -63,7 +63,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
- assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+ assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
}
@Test
@@ -72,7 +72,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
.setWindow(statusBar, null, null);
statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
- final InsetsState state = getController().getInsetsForDispatch(statusBar);
+ final InsetsState state = getController().getInsetsForWindow(statusBar);
assertNull(state.peekSource(ITYPE_STATUS_BAR));
}
@@ -88,8 +88,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
- assertNull(getController().getInsetsForDispatch(navBar).peekSource(ITYPE_IME));
- assertNull(getController().getInsetsForDispatch(navBar).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_IME));
+ assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_STATUS_BAR));
}
@Test
@@ -102,8 +102,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
app.setWindowingMode(WINDOWING_MODE_PINNED);
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
}
@Test
@@ -116,8 +116,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
app.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
}
@Test
@@ -131,8 +131,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
app.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
app.setAlwaysOnTop(true);
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -147,8 +147,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
app2.mBehindIme = false;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForDispatch(app2).getSource(ITYPE_IME).isVisible());
- assertTrue(getController().getInsetsForDispatch(app1).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible());
+ assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -160,7 +160,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
app.mBehindIme = true;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -172,7 +172,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
app.mBehindIme = false;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -207,7 +207,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
// app won't get visible IME insets while above IME even when IME is visible.
assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
- assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
// Reset invocation counter.
clearInvocations(app);
@@ -221,7 +221,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
verify(app, atLeast(1)).notifyInsetsChanged();
// app will get visible IME insets while below IME.
- assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -238,8 +238,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
mDisplayContent.applySurfaceChangesTransaction();
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
+ assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -257,8 +257,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
mDisplayContent.applySurfaceChangesTransaction();
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
+ assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
}
@Test
@@ -278,7 +278,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
statusBarProvider.onPostLayout();
- final InsetsState state = getController().getInsetsForDispatch(ime);
+ final InsetsState state = getController().getInsetsForWindow(ime);
assertEquals(new Rect(0, 1, 2, 3), state.getSource(ITYPE_STATUS_BAR).getFrame());
}
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 1bf83aced590..bea0d8e60c40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -736,7 +736,7 @@ public class RecentTasksTest extends WindowTestsBase {
}
/**
- * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed.
+ * Tests that tasks on always on top multi-window tasks are not visible and not trimmed/removed.
*/
@Test
public void testVisibleTasks_alwaysOnTop() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index f1540731da8b..7a1b17dfead7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -584,6 +584,95 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(statusBarController.isTransparentAllowed(w));
}
+ @Test
+ public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedInTaskLetterbox() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect displayBounds = mActivity.mDisplayContent.getBounds();
+ final Rect taskBounds = mTask.getBounds();
+ final Rect activityBounds = mActivity.getBounds();
+
+ // Display shouldn't be rotated.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+ mActivity.mDisplayContent.getLastOrientation());
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should launch in task level letterboxing.
+ assertTrue(mTask.isTaskLetterboxed());
+ assertFalse(mActivity.inSizeCompatMode());
+ assertEquals(taskBounds, activityBounds);
+
+ // Task bounds should be 700x1400 with the ratio as the display.
+ assertEquals(displayBounds.height(), taskBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ taskBounds.width());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_taskLetterboxBecameSizeCompatAfterRotate() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect activityBounds = mActivity.getBounds();
+
+ // Rotate display to portrait.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ final Rect displayBounds = mActivity.mDisplayContent.getBounds();
+ final Rect newTaskBounds = mTask.getBounds();
+ final Rect newActivityBounds = mActivity.getBounds();
+ assertTrue(displayBounds.width() < displayBounds.height());
+
+ // App should be in size compat.
+ assertFalse(mTask.isTaskLetterboxed());
+ assertScaled();
+ assertEquals(activityBounds.width(), newActivityBounds.width());
+ assertEquals(activityBounds.height(), newActivityBounds.height());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_sizeCompatAfterRotate() {
+ // Set up a display in portrait and ignoring orientation request.
+ setUpDisplaySizeWithApp(1400, 2800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ Rect displayBounds = mActivity.mDisplayContent.getBounds();
+ Rect activityBounds = mActivity.getBounds();
+
+ // App should launch in fullscreen.
+ assertFalse(mTask.isTaskLetterboxed());
+ assertFalse(mActivity.inSizeCompatMode());
+ assertEquals(displayBounds, activityBounds);
+
+ // Rotate display to landscape.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ displayBounds = mActivity.mDisplayContent.getBounds();
+ activityBounds = mActivity.getBounds();
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should be in size compat.
+ assertFalse(mTask.isTaskLetterboxed());
+ assertScaled();
+
+ // App bounds should be 700x1400 with the ratio as the display.
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ activityBounds.width());
+ }
+
private static WindowState addWindowToActivity(ActivityRecord activity) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
new file mode 100644
index 000000000000..7bac3e7b8679
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.spy;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Test class for {@link BLASTSyncEngine}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:SyncEngineTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class SyncEngineTests extends WindowTestsBase {
+
+ @Before
+ public void setUp() {
+ spyOn(mWm.mWindowPlacerLocked);
+ }
+
+ @Test
+ public void testTrivialSyncCallback() {
+ TestWindowContainer mockWC = new TestWindowContainer(mWm, false /* waiter */);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, mockWC);
+ // Make sure a traversal is requested
+ verify(mWm.mWindowPlacerLocked, times(1)).requestTraversal();
+
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ bse.setReady(id);
+ // Make sure a traversal is requested
+ verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+
+ // make sure it was cleaned-up (no second callback)
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(anyInt(), any());
+ }
+
+ @Test
+ public void testWaitingSyncCallback() {
+ TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, mockWC);
+ bse.setReady(id);
+ // Make sure traversals requested (one for add and another for setReady)
+ verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ mockWC.onSyncFinishedDrawing();
+ // Make sure a (third) traversal is requested.
+ verify(mWm.mWindowPlacerLocked, times(3)).requestTraversal();
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+ }
+
+ @Test
+ public void testInvisibleSyncCallback() {
+ TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, mockWC);
+ bse.setReady(id);
+ // Make sure traversals requested (one for add and another for setReady)
+ verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ // Finish sync if invisible.
+ mockWC.mVisibleRequested = false;
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+ assertEquals(SYNC_STATE_NONE, mockWC.mSyncState);
+ }
+
+ @Test
+ public void testWaitForChildrenCallback() {
+ TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer childWC2 = new TestWindowContainer(mWm, true /* waiter */);
+ parentWC.addChild(childWC, POSITION_TOP);
+ parentWC.addChild(childWC2, POSITION_TOP);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, parentWC);
+ bse.setReady(id);
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ parentWC.onSyncFinishedDrawing();
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ childWC.onSyncFinishedDrawing();
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ childWC2.onSyncFinishedDrawing();
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+ assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, childWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, childWC2.mSyncState);
+ }
+
+ @Test
+ public void testWaitForParentCallback() {
+ TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */);
+ parentWC.addChild(childWC, POSITION_TOP);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, parentWC);
+ bse.setReady(id);
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ childWC.onSyncFinishedDrawing();
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ parentWC.onSyncFinishedDrawing();
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+ assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, childWC.mSyncState);
+ }
+
+ @Test
+ public void testFillsParent() {
+ TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ topChildWC.mFillsParent = botChildWC.mFillsParent = true;
+ parentWC.addChild(topChildWC, POSITION_TOP);
+ parentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, parentWC);
+ bse.setReady(id);
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ parentWC.onSyncFinishedDrawing();
+ topChildWC.onSyncFinishedDrawing();
+ // Even though bottom isn't finished, we should see callback because it is occluded by top.
+ assertFalse(botChildWC.isSyncFinished());
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+
+ assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+ }
+
+ @Test
+ public void testReparentOut() {
+ TestWindowContainer nonMemberParentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ parentWC.addChild(topChildWC, POSITION_TOP);
+ parentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, parentWC);
+ bse.setReady(id);
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ parentWC.onSyncFinishedDrawing();
+ topChildWC.onSyncFinishedDrawing();
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ // reparent out cancels
+ botChildWC.reparent(nonMemberParentWC, POSITION_TOP);
+ assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+ assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+ }
+
+ @Test
+ public void testReparentIn() {
+ TestWindowContainer nonMemberParentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ parentWC.addChild(topChildWC, POSITION_TOP);
+ nonMemberParentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, parentWC);
+ bse.setReady(id);
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ parentWC.onSyncFinishedDrawing();
+ topChildWC.onSyncFinishedDrawing();
+
+ // No-longer finished because new child
+ botChildWC.reparent(parentWC, POSITION_BOTTOM);
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ botChildWC.onSyncFinishedDrawing();
+ bse.onSurfacePlacement();
+ verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+ assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+ }
+
+ @Test
+ public void testRemoval() {
+ // Need different transactions to verify stuff
+ mWm.mTransactionFactory = () -> spy(new StubTransaction());
+ TestWindowContainer rootWC = new TestWindowContainer(mWm, false /* waiter */);
+ TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+ rootWC.addChild(parentWC, POSITION_TOP);
+ parentWC.addChild(topChildWC, POSITION_TOP);
+ parentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(listener);
+ bse.addToSyncSet(id, parentWC);
+ final BLASTSyncEngine.SyncGroup syncGroup = parentWC.mSyncGroup;
+ bse.setReady(id);
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ parentWC.onSyncFinishedDrawing();
+ topChildWC.removeImmediately();
+ bse.onSurfacePlacement();
+ verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+ // Removal should merge transaction into parent
+ verify(parentWC.mSyncTransaction, times(1)).merge(eq(topChildWC.mSyncTransaction));
+ assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+
+ // Removal of a sync-root should merge transaction into orphan
+ parentWC.removeImmediately();
+ final SurfaceControl.Transaction orphan = syncGroup.getOrphanTransaction();
+ verify(orphan, times(1)).merge(eq(parentWC.mSyncTransaction));
+
+ // Then the orphan transaction should be merged into sync
+ bse.onSurfacePlacement();
+ final ArgumentCaptor<SurfaceControl.Transaction> merged =
+ ArgumentCaptor.forClass(SurfaceControl.Transaction.class);
+ verify(listener, times(1)).onTransactionReady(eq(id), merged.capture());
+ final SurfaceControl.Transaction mergedTransaction = merged.getValue();
+ verify(mergedTransaction, times(1)).merge(eq(orphan));
+
+ assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+ assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+ }
+
+ static class TestWindowContainer extends WindowContainer {
+ final boolean mWaiter;
+ boolean mVisibleRequested = true;
+ boolean mFillsParent = false;
+
+ TestWindowContainer(WindowManagerService wms, boolean waiter) {
+ super(wms);
+ mWaiter = waiter;
+ mDisplayContent = wms.getDefaultDisplayContentLocked();
+ }
+
+ @Override
+ boolean prepareSync() {
+ if (!super.prepareSync()) {
+ return false;
+ }
+ if (mWaiter) {
+ mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
+ }
+ return true;
+ }
+
+ @Override
+ void createSurfaceControl(boolean force) {
+ // nothing
+ }
+
+ @Override
+ boolean isVisibleRequested() {
+ return mVisibleRequested;
+ }
+
+ @Override
+ boolean fillsParent() {
+ return mFillsParent;
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 7975899dacbc..2fa7589ed719 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -420,8 +420,9 @@ public class TaskRecordTests extends WindowTestsBase {
// Without limiting to be inside the parent bounds, the out screen size should keep relative
// to the input bounds.
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
final ActivityRecord.CompatDisplayInsets compatIntsets =
- new ActivityRecord.CompatDisplayInsets(display, task);
+ new ActivityRecord.CompatDisplayInsets(display, activity);
task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index a54bbaf7f6e1..788ef5ad2ec4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -242,40 +242,6 @@ public class TaskStackChangedListenerTest {
assertTrue(activity.mOnDetachedFromWindowCalled);
}
- @Test
- public void testTaskOnSingleTaskDisplayDrawn() throws Exception {
- final Instrumentation instrumentation = getInstrumentation();
-
- final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
- final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
- registerTaskStackChangedListener(new TaskStackListener() {
- @Override
- public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
- singleTaskDisplayDrawnLatch.countDown();
- }
- });
- final ActivityViewTestActivity activity =
- (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
- final ActivityView activityView = activity.getActivityView();
- activityView.setCallback(new ActivityView.StateCallback() {
- @Override
- public void onActivityViewReady(ActivityView view) {
- activityViewReadyLatch.countDown();
- }
-
- @Override
- public void onActivityViewDestroyed(ActivityView view) {
- }
- });
- waitForCallback(activityViewReadyLatch);
-
- final Context context = instrumentation.getContext();
- Intent intent = new Intent(context, ActivityInActivityView.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- SystemUtil.runWithShellPermissionIdentity(() -> activityView.startActivity(intent));
- waitForCallback(singleTaskDisplayDrawnLatch);
- }
-
public static class ActivityLaunchesNewActivityInActivityView extends TestActivity {
private boolean mActivityBLaunched = false;
@@ -291,61 +257,6 @@ public class TaskStackChangedListenerTest {
}
@Test
- public void testSingleTaskDisplayEmpty() throws Exception {
- final Instrumentation instrumentation = getInstrumentation();
-
- final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
- final CountDownLatch activityViewDestroyedLatch = new CountDownLatch(1);
- final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
- final CountDownLatch singleTaskDisplayEmptyLatch = new CountDownLatch(1);
-
- registerTaskStackChangedListener(new TaskStackListener() {
- @Override
- public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
- singleTaskDisplayDrawnLatch.countDown();
- }
- @Override
- public void onSingleTaskDisplayEmpty(int displayId)
- throws RemoteException {
- singleTaskDisplayEmptyLatch.countDown();
- }
- });
- final ActivityViewTestActivity activity =
- (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
- final ActivityView activityView = activity.getActivityView();
- activityView.setCallback(new ActivityView.StateCallback() {
- @Override
- public void onActivityViewReady(ActivityView view) {
- activityViewReadyLatch.countDown();
- }
-
- @Override
- public void onActivityViewDestroyed(ActivityView view) {
- activityViewDestroyedLatch.countDown();
- }
- });
- waitForCallback(activityViewReadyLatch);
-
- // 1. start ActivityLaunchesNewActivityInActivityView in an ActivityView
- // 2. ActivityLaunchesNewActivityInActivityView launches ActivityB
- // 3. ActivityB finishes self.
- // 4. Verify ITaskStackListener#onSingleTaskDisplayEmpty is not called yet.
- final Context context = instrumentation.getContext();
- Intent intent = new Intent(context, ActivityLaunchesNewActivityInActivityView.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- SystemUtil.runWithShellPermissionIdentity(() -> activityView.startActivity(intent));
- waitForCallback(singleTaskDisplayDrawnLatch);
- UiDevice.getInstance(getInstrumentation()).waitForIdle();
- assertEquals(1, singleTaskDisplayEmptyLatch.getCount());
-
- // 5. Release the container, and ActivityLaunchesNewActivityInActivityView finishes.
- // 6. Verify ITaskStackListener#onSingleTaskDisplayEmpty is called.
- activityView.release();
- waitForCallback(activityViewDestroyedLatch);
- waitForCallback(singleTaskDisplayEmptyLatch);
- }
-
- @Test
public void testTaskDisplayChanged() throws Exception {
final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
final ActivityViewTestActivity activity =
@@ -603,7 +514,7 @@ public class TaskStackChangedListenerTest {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mActivityView = new ActivityView.Builder(this).setSingleInstance(true).build();
+ mActivityView = new ActivityView.Builder(this).build();
setContentView(mActivityView);
ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ee16a76bcff8..e95efe785e8c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -150,7 +150,7 @@ class TestDisplayContent extends DisplayContent {
newDisplay.onRequestedOverrideConfigurationChanged(c);
if (!mCanRotate) {
final DisplayRotation displayRotation = newDisplay.getDisplayRotation();
- doReturn(false).when(displayRotation).respectAppRequestedOrientation();
+ doReturn(true).when(displayRotation).isFixedToUserRotation();
}
// Please add stubbing before this line. Services will start using this display in other
// threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index bd586a5f6c76..7a1f65a3b62c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -37,13 +37,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
import static com.google.common.truth.Truth.assertThat;
@@ -479,33 +479,16 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testCreateDeleteRootTasks() {
- ITaskOrganizer listener = new ITaskOrganizer.Stub() {
- @Override
- public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
-
- @Override
- public void onTaskVanished(RunningTaskInfo container) { }
-
- @Override
- public void onTaskInfoChanged(RunningTaskInfo info) {
- }
-
- @Override
- public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
- }
- };
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
-
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
Display.DEFAULT_DISPLAY,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo();
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
info1.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
Display.DEFAULT_DISPLAY,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
info2.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
@@ -539,7 +522,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
@@ -597,7 +580,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
lastReportedTiles.clear();
called[0] = false;
@@ -658,9 +641,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo();
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
mDisplayContent.mDisplayId, null /* activityTypes */).size();
@@ -741,110 +724,15 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
- public void testTrivialBLASTCallback() throws RemoteException {
- final Task stackController1 = createStack();
- final Task task = createTask(stackController1);
- final ITaskOrganizer organizer = registerMockOrganizer();
-
- spyOn(task);
- doReturn(true).when(task).isVisible();
-
- BLASTSyncEngine bse = new BLASTSyncEngine();
-
- BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
-
- int id = bse.startSyncSet(transactionListener);
- bse.addToSyncSet(id, task);
- bse.setReady(id);
- // Since this task has no windows the sync is trivial and completes immediately.
- verify(transactionListener)
- .onTransactionReady(anyInt(), any());
- }
-
- @Test
- public void testOverlappingBLASTCallback() throws RemoteException {
- final Task stackController1 = createStack();
- final Task task = createTask(stackController1);
- final ITaskOrganizer organizer = registerMockOrganizer();
-
- spyOn(task);
- doReturn(true).when(task).isVisible();
- final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
- makeWindowVisible(w);
-
- BLASTSyncEngine bse = new BLASTSyncEngine();
-
- BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
-
- int id = bse.startSyncSet(transactionListener);
- assertEquals(true, bse.addToSyncSet(id, task));
- bse.setReady(id);
-
- int id2 = bse.startSyncSet(transactionListener);
- // We should be rejected from the second sync since we are already
- // in one.
- assertEquals(false, bse.addToSyncSet(id2, task));
- w.immediatelyNotifyBlastSync();
- assertEquals(true, bse.addToSyncSet(id2, task));
- bse.setReady(id2);
- }
-
- @Test
- public void testBLASTCallbackWithWindow() {
+ public void testBLASTCallbackWithActivityChildren() {
final Task stackController1 = createStack();
final Task task = createTask(stackController1);
- final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
- makeWindowVisible(w);
-
- BLASTSyncEngine bse = new BLASTSyncEngine();
-
- BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(transactionListener);
- bse.addToSyncSet(id, task);
- bse.setReady(id);
- // Since we have a window we have to wait for it to draw to finish sync.
- verify(transactionListener, never())
- .onTransactionReady(anyInt(), any());
- w.immediatelyNotifyBlastSync();
- verify(transactionListener)
- .onTransactionReady(anyInt(), any());
- }
-
- @Test
- public void testBLASTCallbackNoDoubleAdd() {
- final Task stackController1 = createStack();
- final Task task = createTask(stackController1);
- final ITaskOrganizer organizer = registerMockOrganizer();
- final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
- makeWindowVisible(w);
-
- BLASTSyncEngine bse = new BLASTSyncEngine();
-
- BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
-
- int id = bse.startSyncSet(transactionListener);
- assertTrue(bse.addToSyncSet(id, w));
- assertFalse(bse.addToSyncSet(id, w));
-
- // Clean-up
- bse.setReady(id);
- }
-
-
- @Test
- public void testBLASTCallbackWithInvisibleWindow() {
- final Task stackController1 = createStack();
- final Task task = createTask(stackController1);
- final ITaskOrganizer organizer = registerMockOrganizer();
- final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+ w.mActivityRecord.mVisibleRequested = true;
+ w.mActivityRecord.setVisible(true);
- BLASTSyncEngine bse = new BLASTSyncEngine();
+ BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
BLASTSyncEngine.TransactionReadyListener transactionListener =
mock(BLASTSyncEngine.TransactionReadyListener.class);
@@ -852,43 +740,19 @@ public class WindowOrganizerTests extends WindowTestsBase {
int id = bse.startSyncSet(transactionListener);
bse.addToSyncSet(id, task);
bse.setReady(id);
+ bse.onSurfacePlacement();
- // Since the window was invisible, the Task had no visible leaves and the sync should
- // complete as soon as we call setReady.
- verify(transactionListener)
- .onTransactionReady(anyInt(), any());
- }
-
- @Test
- public void testBLASTCallbackWithChildWindow() {
- final Task stackController1 = createStack();
- final Task task = createTask(stackController1);
- final ITaskOrganizer organizer = registerMockOrganizer();
- final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
- final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window");
-
- w.mActivityRecord.setVisible(true);
- makeWindowVisible(w, child);
-
- BLASTSyncEngine bse = new BLASTSyncEngine();
-
- BLASTSyncEngine.TransactionReadyListener transactionListener =
- mock(BLASTSyncEngine.TransactionReadyListener.class);
+ // Even though w is invisible (and thus activity isn't waiting on it), activity will
+ // continue to wait until it has at-least 1 visible window.
+ // Since we have a child window we still shouldn't be done.
+ verify(transactionListener, never()).onTransactionReady(anyInt(), any());
- int id = bse.startSyncSet(transactionListener);
- assertEquals(true, bse.addToSyncSet(id, task));
- bse.setReady(id);
+ makeWindowVisible(w);
+ bse.onSurfacePlacement();
w.immediatelyNotifyBlastSync();
+ bse.onSurfacePlacement();
- // Since we have a child window we still shouldn't be done.
- verify(transactionListener, never())
- .onTransactionReady(anyInt(), any());
- reset(transactionListener);
-
- child.immediatelyNotifyBlastSync();
- // Ah finally! Done
- verify(transactionListener)
- .onTransactionReady(anyInt(), any());
+ verify(transactionListener).onTransactionReady(anyInt(), any());
}
class StubOrganizer extends ITaskOrganizer.Stub {
@@ -1045,8 +909,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
- public void testBLASTCallbackWithMultipleWindows() throws Exception {
- final ITaskOrganizer organizer = registerMockOrganizer();
+ public void testBLASTCallbackWithWindows() throws Exception {
final Task stackController = createStack();
final Task task = createTask(stackController);
final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
@@ -1065,13 +928,20 @@ public class WindowOrganizerTests extends WindowTestsBase {
verify(mockCallback, never()).onTransactionReady(anyInt(), any());
assertTrue(w1.useBLASTSync());
assertTrue(w2.useBLASTSync());
- w1.immediatelyNotifyBlastSync();
+ // Make second (bottom) ready. If we started with the top, since activities fillsParent
+ // by default, the sync would be considered finished.
+ w2.immediatelyNotifyBlastSync();
+ mWm.mSyncEngine.onSurfacePlacement();
+ verify(mockCallback, never()).onTransactionReady(anyInt(), any());
+
+ assertEquals(SYNC_STATE_READY, w2.mSyncState);
// Even though one Window finished drawing, both windows should still be using blast sync
assertTrue(w1.useBLASTSync());
assertTrue(w2.useBLASTSync());
- w2.immediatelyNotifyBlastSync();
+ w1.immediatelyNotifyBlastSync();
+ mWm.mSyncEngine.onSurfacePlacement();
verify(mockCallback).onTransactionReady(anyInt(), any());
assertFalse(w1.useBLASTSync());
assertFalse(w2.useBLASTSync());
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 19bed48a4bc5..2691ae84db28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -46,6 +46,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;
import static com.google.common.truth.Truth.assertThat;
@@ -609,7 +610,8 @@ public class WindowStateTests extends WindowTestsBase {
// Check that the window is in resizing if using blast sync.
win.reportResized();
- win.prepareForSync(mock(BLASTSyncEngine.TransactionReadyListener.class), 1);
+ win.prepareSync();
+ assertEquals(SYNC_STATE_WAITING_FOR_DRAW, win.mSyncState);
win.updateResizingWindowIfNeeded();
assertThat(mWm.mResizingWindows).contains(win);
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 924b286a4f8e..6237be0f4b26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1006,10 +1006,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
mDisplayId = displayId;
mService.mTaskOrganizerController.registerTaskOrganizer(this);
WindowContainerToken primary = mService.mTaskOrganizerController.createRootTask(
- displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo().token;
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
WindowContainerToken secondary = mService.mTaskOrganizerController.createRootTask(
- displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo().token;
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask();
}
TestSplitOrganizer(ActivityTaskManagerService service) {
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 228f98e489e4..5eaa6c87a102 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -477,10 +477,6 @@ package android.telephony {
field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
- public class SignalStrength implements android.os.Parcelable {
- ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
- }
-
public final class SmsCbCmasInfo implements android.os.Parcelable {
ctor public SmsCbCmasInfo(int, int, int, int, int, int);
method public int describeContents();
@@ -702,6 +698,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
@@ -1435,6 +1432,10 @@ package android.telephony.ims {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
+ public class ImsManager {
+ method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int);
+ }
+
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -1470,10 +1471,13 @@ package android.telephony.ims {
method public void disableIms(int);
method public void enableIms(int);
method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method public long getImsServiceCapabilities();
method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+ method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
method public void readyForFeatureCreation();
+ field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L
}
public final class ImsSsData implements android.os.Parcelable {
@@ -1719,6 +1723,10 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
+ public class SipDelegateManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+ }
+
}
package android.telephony.ims.feature {
@@ -1968,6 +1976,10 @@ package android.telephony.ims.stub {
method public int updateColr(int);
}
+ public class SipTransportImplBase {
+ ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+ }
+
}
package android.telephony.mbms {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3d33b95d34d2..157609130c1b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3832,11 +3832,26 @@ public class CarrierConfigManager {
public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT =
KEY_PREFIX + "wifi_off_deferring_time_millis_int";
+ /**
+ * A boolean flag specifying whether or not this carrier requires one IMS registration for
+ * all IMS services (MMTEL and RCS).
+ * <p>
+ * If set to {@code true}, the IMS Service must use one IMS registration for all IMS
+ * services. If set to {@code false}, IMS services may use separate IMS registrations for
+ * MMTEL and RCS.
+ * <p>
+ * The default value for this configuration is {@code false}.
+ * @see android.telephony.ims.SipDelegateManager
+ */
+ public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL =
+ KEY_PREFIX + "ims_single_registration_required_bool";
+
private Ims() {}
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
+ defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/CdmaEriInformation.java b/telephony/java/android/telephony/CdmaEriInformation.java
deleted file mode 100644
index fd0b905e9c3e..000000000000
--- a/telephony/java/android/telephony/CdmaEriInformation.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * Copyright (C) 2020 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.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * CDMA ERI (Enhanced Roaming Indicator) information.
- *
- * This contains the following ERI information
- *
- * 1. ERI (Enhanced Roaming Indicator) icon index. The number is assigned by
- * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own
- * ERI icon index.
- * 2. CDMA ERI icon mode. This represents how the icon should be displayed.
- * Its one of the following CDMA ERI icon mode
- * {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_NORMAL}
- * {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_FLASH}
- *
- * @hide
- */
-public final class CdmaEriInformation implements Parcelable {
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"ERI_"}, value = {
- ERI_ON,
- ERI_OFF,
- ERI_FLASH
- })
- public @interface EriIconIndex {}
-
- /**
- * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by
- * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
- */
- public static final int ERI_ON = 0;
-
- /**
- * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by
- * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
- */
- public static final int ERI_OFF = 1;
-
- /**
- * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by
- * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
- */
- public static final int ERI_FLASH = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"ERI_ICON_MODE_"}, value = {
- ERI_ICON_MODE_NORMAL,
- ERI_ICON_MODE_FLASH
- })
- public @interface EriIconMode {}
-
- /**
- * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that
- * the ERI icon should be displayed normally.
- *
- * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
- */
- public static final int ERI_ICON_MODE_NORMAL = 0;
-
- /**
- * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that
- * the ERI icon should be flashing.
- *
- * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
- */
- public static final int ERI_ICON_MODE_FLASH = 1;
-
- private @EriIconIndex int mIconIndex;
- private @EriIconMode int mIconMode;
-
- /**
- * Creates CdmaEriInformation from iconIndex and iconMode
- *
- * @hide
- */
- public CdmaEriInformation(@EriIconIndex int iconIndex, @EriIconMode int iconMode) {
- mIconIndex = iconIndex;
- mIconMode = iconMode;
- }
-
- /** Gets the ERI icon index */
- public @EriIconIndex int getEriIconIndex() {
- return mIconIndex;
- }
-
- /**
- * Sets the ERI icon index
- *
- * @hide
- */
- public void setEriIconIndex(@EriIconIndex int iconIndex) {
- mIconIndex = iconIndex;
- }
-
- /** Gets the ERI icon mode */
- public @EriIconMode int getEriIconMode() {
- return mIconMode;
- }
-
- /**
- * Sets the ERI icon mode
- *
- * @hide
- */
- public void setEriIconMode(@EriIconMode int iconMode) {
- mIconMode = iconMode;
- }
- /** Implement the Parcelable interface */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mIconIndex);
- dest.writeInt(mIconMode);
- }
-
- /** Implement the Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Construct a CdmaEriInformation object from the given parcel
- */
- private CdmaEriInformation(Parcel in) {
- mIconIndex = in.readInt();
- mIconMode = in.readInt();
- }
-
- /** Implement the Parcelable interface */
- public static final @android.annotation.NonNull Parcelable.Creator<CdmaEriInformation> CREATOR =
- new Parcelable.Creator<CdmaEriInformation>() {
- @Override
- public CdmaEriInformation createFromParcel(Parcel in) {
- return new CdmaEriInformation(in);
- }
-
- @Override
- public CdmaEriInformation[] newArray(int size) {
- return new CdmaEriInformation[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 3984bd769edd..28feab27a794 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -19,6 +19,7 @@ package android.telephony.ims;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.telephony.SubscriptionManager;
@@ -125,4 +126,24 @@ public class ImsManager {
return new ImsMmTelManager(subscriptionId);
}
+
+ /**
+ * Create an instance of SipDelegateManager for the subscription id specified.
+ * <p>
+ * Used for RCS single registration cases, where an IMS application needs to forward SIP
+ * traffic through the device's IMS service.
+ * @param subscriptionId The ID of the subscription that this SipDelegateManager will use.
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @return a SipDelegateManager instance for the specified subscription ID.
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public SipDelegateManager getSipDelegateManager(int subscriptionId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+ throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+ }
+
+ return new SipDelegateManager(mContext, subscriptionId);
+ }
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 4a0f0076f257..7bd0bc0b69ce 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -18,7 +18,6 @@ package android.telephony;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
@@ -291,9 +290,7 @@ public class SignalStrength implements Parcelable {
* This constructor is used to create a copy of an existing SignalStrength object.
*
* @param s Source SignalStrength
- * @hide
*/
- @SystemApi
public SignalStrength(@NonNull SignalStrength s) {
copyFrom(s);
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d3fca3ec78df..2b17de64dae9 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1804,6 +1804,7 @@ public final class SmsManager {
*
* {@hide}
*/
+ @UnsupportedAppUsage
@RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
public boolean deleteMessageFromIcc(int messageIndex) {
boolean success = false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9b4d31a51d53..9126387b27c5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2843,7 +2843,7 @@ public class TelephonyManager {
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static @NonNull @NetworkType int[] getAllNetworkTypes() {
- return NETWORK_TYPES;
+ return NETWORK_TYPES.clone();
}
/**
@@ -5644,27 +5644,78 @@ public class TelephonyManager {
}
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ERI_"}, value = {
+ ERI_ON,
+ ERI_OFF,
+ ERI_FLASH
+ })
+ public @interface EriIconIndex {}
+
+ /**
+ * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by
+ * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ */
+ public static final int ERI_ON = 0;
+
/**
- * Get the CDMA ERI (Enhanced Roaming Indicator) information
+ * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by
+ * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ */
+ public static final int ERI_OFF = 1;
+
+ /**
+ * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by
+ * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+ */
+ public static final int ERI_FLASH = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ERI_ICON_MODE_"}, value = {
+ ERI_ICON_MODE_NORMAL,
+ ERI_ICON_MODE_FLASH
+ })
+ public @interface EriIconMode {}
+
+ /**
+ * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that
+ * the ERI icon should be displayed normally.
*
- * Returns {@link android.telephony#CdmaEriInformation}
+ * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
+ * @hide
+ */
+ public static final int ERI_ICON_MODE_NORMAL = 0;
+
+ /**
+ * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that
+ * the ERI icon should be flashing.
*
+ * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
* @hide
*/
+ public static final int ERI_ICON_MODE_FLASH = 1;
+
+ /**
+ * Returns the CDMA ERI icon index to display. The number is assigned by
+ * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI icon index.
+ * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
+ * @hide
+ */
+ @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- @NonNull
- public CdmaEriInformation getCdmaEriInformation() {
- return new CdmaEriInformation(
- getCdmaEriIconIndex(getSubId()), getCdmaEriIconMode(getSubId()));
+ public @EriIconIndex int getCdmaEnhancedRoamingIndicatorIconIndex() {
+ return getCdmaEriIconIndex(getSubId());
}
/**
- * Returns the CDMA ERI icon index to display for a subscription
+ * Returns the CDMA ERI icon index to display for a subscription.
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
- public int getCdmaEriIconIndex(int subId) {
+ public @EriIconIndex int getCdmaEriIconIndex(int subId) {
try {
ITelephony telephony = getITelephony();
if (telephony == null)
@@ -5688,7 +5739,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
- public int getCdmaEriIconMode(int subId) {
+ public @EriIconMode int getCdmaEriIconMode(int subId) {
try {
ITelephony telephony = getITelephony();
if (telephony == null)
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index c806d5c8ed3a..9ab5aeb9c34c 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -18,6 +18,7 @@ package android.telephony.ims;
import android.annotation.LongDef;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -125,7 +126,6 @@ public class ImsService extends Service {
* {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and
* this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and
* {@link ImsFeature#FEATURE_RCS} features.
- * @hide
*/
public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1;
@@ -423,9 +423,12 @@ public class ImsService extends Service {
* <p>
* This should be a static configuration and should not change at runtime.
* @return The optional static capabilities of this ImsService implementation.
- * @hide
*/
+ // ImsService follows a different convention, since it is a stub class. The on* methods are
+ // final and call back into the framework with a state update.
+ @SuppressLint("OnNameExpected")
public @ImsServiceCapability long getImsServiceCapabilities() {
+ // Stub implementation to be implemented by ImsService.
return 0L;
}
@@ -513,9 +516,12 @@ public class ImsService extends Service {
* supported for this ImsService.
* @param slotId The slot that is associated with the SipTransport implementation.
* @return the SipTransport implementation for the specified slot.
- * @hide Keep this hidden until there is something to expose in SipTransport.
*/
+ // ImsService follows a different convention, since it is a stub class. The on* methods are
+ // final and call back into the framework with a state update.
+ @SuppressLint("OnNameExpected")
public @Nullable SipTransportImplBase getSipTransport(int slotId) {
+ // Stub implementation for ImsServices that do not support SipTransport.
return null;
}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
new file mode 100644
index 000000000000..82c8a9cd58f4
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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.ims;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsRcsController;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Manages the creation and destruction of SipDelegates, which allow an IMS application to forward
+ * SIP messages for the purposes of providing a single IMS registration to the carrier's IMS network
+ * from multiple sources.
+ * @hide
+ */
+@SystemApi
+public class SipDelegateManager {
+
+ private final Context mContext;
+ private final int mSubId;
+
+ /**
+ * Only visible for testing. To instantiate an instance of this class, please use
+ * {@link ImsManager#getSipDelegateManager(int)}.
+ * @hide
+ */
+ @VisibleForTesting
+ public SipDelegateManager(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Determines if creating SIP delegates are supported for the subscription specified.
+ * <p>
+ * If SIP delegates are not supported on this device or the carrier associated with this
+ * subscription, creating a SIP delegate will always fail, as this feature is not supported.
+ * @return true if this device supports creating a SIP delegate and the carrier associated with
+ * this subscription supports single registration, false if creating SIP delegates is not
+ * supported.
+ * @throws ImsException If the remote ImsService is not available for any reason or the
+ * subscription associated with this instance is no longer active. See
+ * {@link ImsException#getCode()} for more information.
+ *
+ * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isSupported() throws ImsException {
+ try {
+ IImsRcsController controller = getIImsRcsController();
+ if (controller == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ return controller.isSipDelegateSupported(mSubId);
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(),
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ private IImsRcsController getIImsRcsController() {
+ IBinder binder = TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyImsServiceRegisterer()
+ .get();
+ return IImsRcsController.Stub.asInterface(binder);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index e01ea9179452..6d25a09e079f 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -53,6 +53,9 @@ interface IImsRcsController {
void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
+ // SipDelegateManager
+ boolean isSipDelegateSupported(int subId);
+
// Internal commands that should not be made public
void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 17bd4b14925f..b2b2914b3739 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -17,6 +17,7 @@
package android.telephony.ims.stub;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.telephony.ims.aidl.ISipTransport;
import java.util.concurrent.Executor;
@@ -24,9 +25,9 @@ import java.util.concurrent.Executor;
/**
* Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other
* IMS applications in order to support IMS single registration.
- *
- * @hide Until there is an implementation, keep this hidden
+ * @hide
*/
+@SystemApi
public class SipTransportImplBase {
private final Executor mBinderExecutor;
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 9d35cbc3de7f..0e9f723d6063 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,4 +9,5 @@ android_test {
"android-support-test",
"ub-uiautomator",
],
+ test_suites: ["device-tests"],
}
diff --git a/tests/Input/TEST_MAPPING b/tests/Input/TEST_MAPPING
new file mode 100644
index 000000000000..15b2bfa9fdc1
--- /dev/null
+++ b/tests/Input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "InputTests"
+ }
+ ]
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1aae6cb35239..4081346f62f9 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -147,6 +147,7 @@ import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
import android.net.DataStallReportParcelable;
+import android.net.EthernetManager;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
@@ -358,6 +359,7 @@ public class ConnectivityServiceTest {
@Mock AppOpsManager mAppOpsManager;
@Mock TelephonyManager mTelephonyManager;
@Mock MockableSystemProperties mSystemProperties;
+ @Mock EthernetManager mEthernetManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -445,6 +447,7 @@ public class ConnectivityServiceTest {
if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
+ if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
return super.getSystemService(name);
}
@@ -1274,7 +1277,6 @@ public class ConnectivityServiceTest {
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
- doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE);
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 8ab302a50662..76edd6341b55 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -25,50 +25,95 @@ import java.io.IOException;
* is output to STDOUT in csv format.
*/
public class PowerStatsServiceProtoParser {
- private static void printRailInfo(PowerStatsServiceProto proto) {
+ private static void printEnergyMeterInfo(PowerStatsServiceMeterProto proto) {
String csvHeader = new String();
- for (int i = 0; i < proto.getRailInfoCount(); i++) {
- RailInfoProto railInfo = proto.getRailInfo(i);
- csvHeader += "Index" + ","
- + "Timestamp" + ","
- + railInfo.getRailName() + "/" + railInfo.getSubsysName() + ",";
+ for (int i = 0; i < proto.getChannelInfoCount(); i++) {
+ ChannelInfoProto energyMeterInfo = proto.getChannelInfo(i);
+ csvHeader += "Index,Timestamp," + energyMeterInfo.getChannelId()
+ + "/" + energyMeterInfo.getChannelName() + ",";
}
System.out.println(csvHeader);
}
- private static void printEnergyData(PowerStatsServiceProto proto) {
- int railInfoCount = proto.getRailInfoCount();
+ private static void printEnergyMeasurements(PowerStatsServiceMeterProto proto) {
+ int energyMeterInfoCount = proto.getChannelInfoCount();
- if (railInfoCount > 0) {
- int energyDataCount = proto.getEnergyDataCount();
- int energyDataSetCount = energyDataCount / railInfoCount;
+ if (energyMeterInfoCount > 0) {
+ int energyMeasurementCount = proto.getEnergyMeasurementCount();
+ int energyMeasurementSetCount = energyMeasurementCount / energyMeterInfoCount;
- for (int i = 0; i < energyDataSetCount; i++) {
+ for (int i = 0; i < energyMeasurementSetCount; i++) {
String csvRow = new String();
- for (int j = 0; j < railInfoCount; j++) {
- EnergyDataProto energyData = proto.getEnergyData(i * railInfoCount + j);
- csvRow += energyData.getIndex() + ","
- + energyData.getTimestampMs() + ","
- + energyData.getEnergyUws() + ",";
+ for (int j = 0; j < energyMeterInfoCount; j++) {
+ EnergyMeasurementProto energyMeasurement =
+ proto.getEnergyMeasurement(i * energyMeterInfoCount + j);
+ csvRow += energyMeasurement.getChannelId() + ","
+ + energyMeasurement.getTimestampMs() + ","
+ + energyMeasurement.getEnergyUws() + ",";
}
System.out.println(csvRow);
}
} else {
- System.out.println("Error: railInfoCount is zero");
+ System.out.println("Error: energyMeterInfoCount is zero");
+ }
+ }
+
+ private static void printEnergyConsumerId(PowerStatsServiceModelProto proto) {
+ String csvHeader = new String();
+ for (int i = 0; i < proto.getEnergyConsumerIdCount(); i++) {
+ EnergyConsumerIdProto energyConsumerId = proto.getEnergyConsumerId(i);
+ csvHeader += "Index,Timestamp," + energyConsumerId.getEnergyConsumerId() + ",";
+ }
+ System.out.println(csvHeader);
+ }
+
+ private static void printEnergyConsumerResults(PowerStatsServiceModelProto proto) {
+ int energyConsumerIdCount = proto.getEnergyConsumerIdCount();
+
+ if (energyConsumerIdCount > 0) {
+ int energyConsumerResultCount = proto.getEnergyConsumerResultCount();
+ int energyConsumerResultSetCount = energyConsumerResultCount / energyConsumerIdCount;
+
+ for (int i = 0; i < energyConsumerResultSetCount; i++) {
+ String csvRow = new String();
+ for (int j = 0; j < energyConsumerIdCount; j++) {
+ EnergyConsumerResultProto energyConsumerResult =
+ proto.getEnergyConsumerResult(i * energyConsumerIdCount + j);
+ csvRow += energyConsumerResult.getEnergyConsumerId() + ","
+ + energyConsumerResult.getTimestampMs() + ","
+ + energyConsumerResult.getEnergyUws() + ",";
+ }
+ System.out.println(csvRow);
+ }
+ } else {
+ System.out.println("Error: energyConsumerIdCount is zero");
}
}
private static void generateCsvFile(String pathToIncidentReport) {
try {
- IncidentReportProto irProto =
- IncidentReportProto.parseFrom(new FileInputStream(pathToIncidentReport));
+ // Print power meter data.
+ IncidentReportMeterProto irMeterProto =
+ IncidentReportMeterProto.parseFrom(new FileInputStream(pathToIncidentReport));
+
+ if (irMeterProto.hasIncidentReport()) {
+ PowerStatsServiceMeterProto pssMeterProto = irMeterProto.getIncidentReport();
+ printEnergyMeterInfo(pssMeterProto);
+ printEnergyMeasurements(pssMeterProto);
+ } else {
+ System.out.println("Meter incident report not found. Exiting.");
+ }
+
+ // Print power model data.
+ IncidentReportModelProto irModelProto =
+ IncidentReportModelProto.parseFrom(new FileInputStream(pathToIncidentReport));
- if (irProto.hasIncidentReport()) {
- PowerStatsServiceProto pssProto = irProto.getIncidentReport();
- printRailInfo(pssProto);
- printEnergyData(pssProto);
+ if (irModelProto.hasIncidentReport()) {
+ PowerStatsServiceModelProto pssModelProto = irModelProto.getIncidentReport();
+ printEnergyConsumerId(pssModelProto);
+ printEnergyConsumerResults(pssModelProto);
} else {
- System.out.println("Incident report not found. Exiting.");
+ System.out.println("Model incident report not found. Exiting.");
}
} catch (IOException e) {
System.out.println("Unable to open incident report file: " + pathToIncidentReport);
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index 754a8dc408a4..da5888b6adee 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -263,7 +263,7 @@ package android.net.wifi {
field public static final int BAND_2GHZ = 1; // 0x1
field public static final int BAND_5GHZ = 2; // 0x2
field public static final int BAND_6GHZ = 4; // 0x4
- field public static final int BAND_ANY = 7; // 0x7
+ field @Deprecated public static final int BAND_ANY = 7; // 0x7
field public static final int RANDOMIZATION_NONE = 0; // 0x0
field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 237922a6fe1e..2649b66c703f 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -90,6 +90,9 @@ public final class SoftApConfiguration implements Parcelable {
* Device is allowed to choose the optimal band (2Ghz, 5Ghz, 6Ghz) based on device capability,
* operating country code and current radio conditions.
* @hide
+ *
+ * @deprecated The bands are a bit mask - use any combination of {@code BAND_},
+ * for instance {@code BAND_2GHZ | BAND_5GHZ | BAND_6GHZ}.
*/
@SystemApi
public static final int BAND_ANY = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ;