summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/permission/service/java/com/android/permission/persistence/IoUtils.java2
-rw-r--r--apex/statsd/framework/Android.bp6
-rw-r--r--apex/statsd/framework/java/android/util/StatsLog.java2
-rw-r--r--cmds/statsd/src/atoms.proto94
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java6
-rw-r--r--core/java/android/app/ActivityThread.java73
-rw-r--r--core/java/android/app/ApplicationPackageManager.java7
-rw-r--r--core/java/android/app/ITaskStackListener.aidl10
-rw-r--r--core/java/android/app/LoadedApk.java28
-rw-r--r--core/java/android/app/Notification.java7
-rw-r--r--core/java/android/app/TaskStackListener.java4
-rw-r--r--core/java/android/content/Intent.java3
-rw-r--r--core/java/android/content/pm/PackageInstaller.java12
-rw-r--r--core/java/android/content/pm/PackageItemInfo.java12
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedActivityUtils.java9
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedComponentUtils.java29
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java11
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java93
-rw-r--r--core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl32
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlService.aidl3
-rw-r--r--core/java/android/os/GraphicsEnvironment.java166
-rw-r--r--core/java/android/os/UserManager.java7
-rwxr-xr-xcore/java/android/provider/Settings.java18
-rw-r--r--core/java/android/provider/Telephony.java40
-rw-r--r--core/java/android/service/autofill/InlineSuggestionRoot.java4
-rw-r--r--core/java/android/service/controls/Control.java20
-rw-r--r--core/java/android/telephony/PhoneStateListener.java4
-rw-r--r--core/java/android/view/GestureDetector.java14
-rw-r--r--core/java/android/view/SurfaceView.java6
-rw-r--r--core/java/android/view/autofill/AutofillId.java5
-rw-r--r--core/java/android/webkit/WebChromeClient.java8
-rw-r--r--core/java/android/window/VirtualDisplayTaskEmbedder.java4
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java18
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java77
-rw-r--r--core/java/com/android/internal/app/ChooserGridLayoutManager.java11
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java11
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java2
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java63
-rw-r--r--core/java/com/android/internal/app/ResolverViewPager.java12
-rw-r--r--core/java/com/android/internal/logging/UiEventLogger.java24
-rw-r--r--core/java/com/android/internal/logging/UiEventLoggerImpl.java27
-rw-r--r--core/java/com/android/internal/logging/testing/UiEventLoggerFake.java30
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java49
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java24
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp12
-rw-r--r--core/proto/android/server/connectivity/data_stall_event.proto1
-rw-r--r--core/proto/android/stats/mediametrics/mediametrics.proto5
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/res/values-el/strings.xml2
-rw-r--r--core/res/res/values-en-rXC/strings.xml6
-rw-r--r--core/res/res/values-gl/strings.xml2
-rw-r--r--core/res/res/values-km/strings.xml2
-rw-r--r--core/res/res/values-kn/strings.xml4
-rw-r--r--core/res/res/values-mr/strings.xml2
-rw-r--r--core/res/res/values-nl/strings.xml2
-rw-r--r--core/res/res/values-or/strings.xml4
-rw-r--r--core/res/res/values/attrs_manifest.xml25
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/tests/PackageInstallerSessions/Android.bp42
-rw-r--r--core/tests/PackageInstallerSessions/AndroidManifest.xml29
-rw-r--r--core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt188
-rw-r--r--core/tests/coretests/src/android/view/autofill/AutofillIdTest.java26
-rw-r--r--core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java10
-rw-r--r--core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java6
-rw-r--r--data/etc/privapp-permissions-platform.xml5
-rw-r--r--data/keyboards/Vendor_045e_Product_0b12.kl59
-rw-r--r--media/java/android/media/MediaCas.java23
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java172
-rw-r--r--media/java/android/media/projection/MediaProjectionManager.java11
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrPlayback.java28
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrRecorder.java28
-rw-r--r--packages/CarSystemUI/Android.bp2
-rw-r--r--packages/CarSystemUI/res/drawable/nav_button_background.xml26
-rw-r--r--packages/CarSystemUI/res/layout/car_left_navigation_bar.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_bar.xml10
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_button.xml4
-rw-r--r--packages/CarSystemUI/res/layout/car_right_navigation_bar.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_top_navigation_bar.xml3
-rw-r--r--packages/CarSystemUI/res/layout/headsup_container_bottom.xml9
-rw-r--r--packages/CarSystemUI/res/values/styles.xml2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java13
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java7
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java7
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java56
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java13
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java22
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java75
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java30
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/drawable/dismiss_circle_background.xml4
-rw-r--r--packages/SystemUI/res/drawable/floating_dismiss_gradient.xml24
-rw-r--r--packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml19
-rw-r--r--packages/SystemUI/res/drawable/ic_music_note.xml (renamed from packages/SystemUI/res/drawable/dismiss_target_x.xml)18
-rw-r--r--packages/SystemUI/res/layout/partial_conversation_info.xml33
-rw-r--r--packages/SystemUI/res/layout/priority_onboarding_half_shell.xml176
-rw-r--r--packages/SystemUI/res/layout/quick_qs_status_icons.xml2
-rw-r--r--packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml2
-rw-r--r--packages/SystemUI/res/layout/quick_settings_footer.xml6
-rw-r--r--packages/SystemUI/res/layout/quick_settings_header_info.xml2
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml1
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml2
-rw-r--r--packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml9
-rw-r--r--packages/SystemUI/res/values-night/styles.xml5
-rw-r--r--packages/SystemUI/res/values-sw320dp/dimens.xml36
-rw-r--r--packages/SystemUI/res/values-sw372dp/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml29
-rw-r--r--packages/SystemUI/res/values/strings.xml17
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.aidl19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java50
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java86
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/Prefs.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java256
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt192
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHost.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt252
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java (renamed from packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java)66
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt5
-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.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java13
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java7
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java4
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java12
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java7
-rw-r--r--services/core/java/com/android/server/VibratorService.java60
-rwxr-xr-xservices/core/java/com/android/server/audio/AudioService.java46
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java63
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java83
-rw-r--r--services/core/java/com/android/server/location/UserInfoHelper.java213
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java15
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java33
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java130
-rw-r--r--services/core/java/com/android/server/pm/PersistentPreferredActivity.java19
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java3
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java48
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java20
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java7
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java44
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java6
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java237
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java50
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskChangeNotificationController.java15
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java145
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt85
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt174
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java74
-rw-r--r--telephony/java/android/telephony/SmsCbMessage.java8
-rw-r--r--wifi/Android.bp1
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java9
232 files changed, 4645 insertions, 2047 deletions
diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
index 0ae44603516e..569a78c0ab41 100644
--- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
+++ b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
@@ -20,6 +20,8 @@ import android.annotation.NonNull;
/**
* Utility class for IO.
+ *
+ * @hide
*/
public class IoUtils {
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 1f479963eaa9..15a2f22e0fea 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -20,8 +20,8 @@ genrule {
name: "statslog-statsd-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module statsd" +
- " --javaPackage com.android.internal.util --javaClass StatsdStatsLog",
- out: ["com/android/internal/util/StatsdStatsLog.java"],
+ " --javaPackage com.android.internal.statsd --javaClass StatsdStatsLog",
+ out: ["com/android/internal/statsd/StatsdStatsLog.java"],
}
java_library_static {
@@ -60,7 +60,7 @@ java_sdk_library {
"android.os",
"android.util",
// From :statslog-statsd-java-gen
- "com.android.internal.util",
+ "com.android.internal.statsd",
],
api_packages: [
diff --git a/apex/statsd/framework/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java
index 4eeae57fe195..0a9f4ebabdf0 100644
--- a/apex/statsd/framework/java/android/util/StatsLog.java
+++ b/apex/statsd/framework/java/android/util/StatsLog.java
@@ -28,7 +28,7 @@ import android.os.IStatsd;
import android.os.Process;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.StatsdStatsLog;
+import com.android.internal.statsd.StatsdStatsLog;
/**
* StatsLog provides an API for developers to send events to statsd. The events can be used to
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 36a8b2cfc084..81d059ed84d9 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -444,6 +444,11 @@ message Atom {
TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"];
MediaOutputOpSwitchReported mediaoutput_op_switch_reported =
277 [(module) = "settings"];
+ CellBroadcastMessageFiltered cb_message_filtered =
+ 278 [(module) = "cellbroadcast"];
+ TvTunerDvrStatus tv_tuner_dvr_status = 279 [(module) = "framework"];
+ TvCasSessionOpenStatus tv_cas_session_open_status =
+ 280 [(module) = "framework"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -9134,8 +9139,10 @@ message IntegrityRulesPushed {
/**
* Logs when a cell broadcast message is received on the device.
*
- * Logged from CellBroadcastService module:
+ * Logged from Cell Broadcast module and platform:
* packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ * packages/apps/CellBroadcastReceiver/
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
*/
message CellBroadcastMessageReported {
// The type of Cell Broadcast message
@@ -9146,8 +9153,40 @@ message CellBroadcastMessageReported {
CDMA_SPC = 3;
}
+ // The parts of the cell broadcast message pipeline
+ enum ReportSource {
+ UNKNOWN_SOURCE = 0;
+ FRAMEWORK = 1;
+ CB_SERVICE = 2;
+ CB_RECEIVER_APP = 3;
+ }
+
// GSM, CDMA, CDMA-SCP
optional CbType type = 1;
+
+ // The source of the report
+ optional ReportSource source = 2;
+}
+
+/**
+ * Logs when a cell broadcast message is filtered out, or otherwise intentionally not sent to CBR.
+ *
+ * Logged from CellBroadcastService module:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageFiltered {
+ enum FilterReason {
+ NOT_FILTERED = 0;
+ DUPLICATE_MESSAGE = 1;
+ GEOFENCED_MESSAGE = 2;
+ AREA_INFO_MESSAGE = 3;
+ }
+
+ // GSM, CDMA, CDMA-SCP
+ optional CellBroadcastMessageReported.CbType type = 1;
+
+ // The source of the report
+ optional FilterReason filter = 2;
}
/**
@@ -9174,6 +9213,7 @@ message CellBroadcastMessageError {
UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12;
UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13;
UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14;
+ NO_CONNECTION_TO_CB_SERVICE = 15;
}
// What kind of error occurred
@@ -9205,6 +9245,58 @@ message TvTunerStateChanged {
// new state
optional State state = 2;
}
+
+/**
+ * Logs the status of a dvr playback or record.
+ * This is atom ID 279.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/tv/tuner/dvr
+ */
+message TvTunerDvrStatus {
+ enum Type {
+ UNKNOWN_TYPE = 0;
+ PLAYBACK = 1; // is a playback
+ RECORD = 2; // is a record
+ }
+ enum State {
+ UNKNOWN_STATE = 0;
+ STARTED = 1; // DVR is started
+ STOPPED = 2; // DVR is stopped
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // DVR type
+ optional Type type = 2;
+ // DVR state
+ optional State state = 3;
+ // Identify the segment of a record or playback
+ optional int32 segment_id = 4;
+ // indicate how many overflow or underflow happened between started to stopped
+ optional int32 overflow_underflow_count = 5;
+}
+
+/**
+ * Logs when a cas session opened through MediaCas.
+ * This is atom ID 280.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/MediaCas.java
+ */
+message TvCasSessionOpenStatus {
+ enum State {
+ UNKNOWN = 0;
+ SUCCEEDED = 1; // indicate that the session is opened successfully.
+ FAILED = 2; // indicate that the session isn’t opened successfully.
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Cas system Id
+ optional int32 cas_system_id = 2;
+ // State of the session
+ optional State state = 3;
+}
+
/**
* Logs when an app is frozen or unfrozen.
*
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ed0ea556dc9d..ac00a042b79e 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -520,6 +520,12 @@ public abstract class AccessibilityService extends Service {
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13;
+ /**
+ * Action to show Launcher's all apps.
+ * @hide
+ */
+ public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cffa59c06a53..108b9eec34fb 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3252,18 +3252,56 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public void handleFixedRotationAdjustments(@NonNull IBinder token,
@Nullable FixedRotationAdjustments fixedRotationAdjustments) {
- final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null
- ? displayAdjustments -> displayAdjustments.setFixedRotationAdjustments(
- fixedRotationAdjustments)
- : null;
+ handleFixedRotationAdjustments(token, fixedRotationAdjustments, null /* overrideConfig */);
+ }
+
+ /**
+ * Applies the rotation adjustments to override display information in resources belong to the
+ * provided token. If the token is activity token, the adjustments also apply to application
+ * because the appearance of activity is usually more sensitive to the application resources.
+ *
+ * @param token The token to apply the adjustments.
+ * @param fixedRotationAdjustments The information to override the display adjustments of
+ * corresponding resources. If it is null, the exiting override
+ * will be cleared.
+ * @param overrideConfig The override configuration of activity. It is used to override
+ * application configuration. If it is non-null, it means the token is
+ * confirmed as activity token. Especially when launching new activity,
+ * {@link #mActivities} hasn't put the new token.
+ */
+ private void handleFixedRotationAdjustments(@NonNull IBinder token,
+ @Nullable FixedRotationAdjustments fixedRotationAdjustments,
+ @Nullable Configuration overrideConfig) {
+ // The element of application configuration override is set only if the application
+ // adjustments are needed, because activity already has its own override configuration.
+ final Configuration[] appConfigOverride;
+ final Consumer<DisplayAdjustments> override;
+ if (fixedRotationAdjustments != null) {
+ appConfigOverride = new Configuration[1];
+ override = displayAdjustments -> {
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ if (appConfigOverride[0] != null) {
+ displayAdjustments.getConfiguration().updateFrom(appConfigOverride[0]);
+ }
+ };
+ } else {
+ appConfigOverride = null;
+ override = null;
+ }
if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
// No resources are associated with the token.
return;
}
- if (mActivities.get(token) == null) {
- // Only apply the override to application for activity token because the appearance of
- // activity is usually more sensitive to the application resources.
- return;
+ if (overrideConfig == null) {
+ final ActivityClientRecord r = mActivities.get(token);
+ if (r == null) {
+ // It is not an activity token. Nothing to do for application.
+ return;
+ }
+ overrideConfig = r.overrideConfig;
+ }
+ if (appConfigOverride != null) {
+ appConfigOverride[0] = overrideConfig;
}
// Apply the last override to application resources for compatibility. Because the Resources
@@ -3503,7 +3541,8 @@ public final class ActivityThread extends ClientTransactionHandler {
// The rotation adjustments must be applied before creating the activity, so the activity
// can get the adjusted display info during creation.
if (r.mPendingFixedRotationAdjustments != null) {
- handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments);
+ handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments,
+ r.overrideConfig);
r.mPendingFixedRotationAdjustments = null;
}
@@ -7388,6 +7427,10 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ public Bundle getCoreSettings() {
+ return mCoreSettings;
+ }
+
public int getIntCoreSetting(String key, int defaultValue) {
synchronized (mResourcesManager) {
if (mCoreSettings != null) {
@@ -7397,6 +7440,18 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ /**
+ * Get the string value of the given key from core settings.
+ */
+ public String getStringCoreSetting(String key, String defaultValue) {
+ synchronized (mResourcesManager) {
+ if (mCoreSettings != null) {
+ return mCoreSettings.getString(key, defaultValue);
+ }
+ return defaultValue;
+ }
+ }
+
float getFloatCoreSetting(String key, float defaultValue) {
synchronized (mResourcesManager) {
if (mCoreSettings != null) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6bd8fd7c6ecf..6f8233d5de9b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -139,6 +139,10 @@ public class ApplicationPackageManager extends PackageManager {
public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
"app_permission_button_allow_always";
+ // Name of the package which the permission controller's resources are in.
+ public static final String PERMISSION_CONTROLLER_RESOURCE_PACKAGE =
+ "com.android.permissioncontroller";
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -893,8 +897,7 @@ public class ApplicationPackageManager extends PackageManager {
mContext.createPackageContext(permissionController, 0);
int textId = context.getResources().getIdentifier(APP_PERMISSION_BUTTON_ALLOW_ALWAYS,
- "string", "com.android.permissioncontroller");
-// permissionController); STOPSHIP b/147434671
+ "string", PERMISSION_CONTROLLER_RESOURCE_PACKAGE);
if (textId != 0) {
return context.getText(textId);
}
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 2d06ee8d06bc..b68639eb9ac6 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -216,4 +216,14 @@ oneway interface ITaskStackListener {
* in {@link android.content.pm.ActivityInfo}.
*/
void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation);
+
+ /**
+ * Called when a rotation is about to start on the foreground activity.
+ * This applies for:
+ * * free sensor rotation
+ * * forced rotation
+ * * rotation settings set through adb command line
+ * * rotation that occurs when rotation tile is toggled in quick settings
+ */
+ void onActivityRotation();
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 10f7835b3d69..f9b48e710148 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -38,6 +38,7 @@ import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
+import android.os.GraphicsEnvironment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -46,6 +47,7 @@ import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.security.net.config.NetworkSecurityConfigProvider;
import android.sysprop.VndkProperties;
import android.text.TextUtils;
@@ -824,6 +826,32 @@ public final class LoadedApk {
final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
+ if (mActivityThread != null) {
+ final String gpuDebugApp = mActivityThread.getStringCoreSetting(
+ Settings.Global.GPU_DEBUG_APP, "");
+ if (!gpuDebugApp.isEmpty() && mPackageName.equals(gpuDebugApp)) {
+
+ // The current application is used to debug, attempt to get the debug layers.
+ try {
+ // Get the ApplicationInfo from PackageManager so that metadata fields present.
+ final ApplicationInfo ai = ActivityThread.getPackageManager()
+ .getApplicationInfo(mPackageName, PackageManager.GET_META_DATA,
+ UserHandle.myUserId());
+ final String debugLayerPath = GraphicsEnvironment.getInstance()
+ .getDebugLayerPathsFromSettings(mActivityThread.getCoreSettings(),
+ ActivityThread.getPackageManager(), mPackageName, ai);
+ if (debugLayerPath != null) {
+ libraryPermittedPath += File.pathSeparator + debugLayerPath;
+ }
+ } catch (RemoteException e) {
+ // Unlikely to fail for applications, but in case of failure, something is wrong
+ // inside the system server, hence just skip.
+ Slog.e(ActivityThread.TAG,
+ "RemoteException when fetching debug layer paths for: " + mPackageName);
+ }
+ }
+ }
+
// If we're not asked to include code, we construct a classloader that has
// no code path included. We still need to set up the library search paths
// and permitted path because NativeActivity relies on it (it attempts to
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e8ce92db62ad..980fdb87f3fb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7625,9 +7625,8 @@ public class Notification implements Parcelable
}
boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT;
- Icon largeIcon = isConversationLayout && mShortcutIcon != null
- ? mShortcutIcon
- : mBuilder.mN.mLargeIcon;
+ Icon conversationIcon = mShortcutIcon;
+ Icon largeIcon = mBuilder.mN.mLargeIcon;
TemplateBindResult bindResult = new TemplateBindResult();
StandardTemplateParams p = mBuilder.mParams.reset()
.hasProgress(false)
@@ -7671,6 +7670,8 @@ public class Notification implements Parcelable
contentView.setCharSequence(R.id.status_bar_latest_event_content,
"setConversationTitle", conversationTitle);
if (isConversationLayout) {
+ contentView.setIcon(R.id.status_bar_latest_event_content,
+ "setConversationIcon", conversationIcon);
contentView.setBoolean(R.id.status_bar_latest_event_content,
"setIsImportantConversation", isImportantConversation);
}
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 5d8daf88a8de..843d1c7414f8 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -199,4 +199,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
@Override
public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) {
}
+
+ @Override
+ public void onActivityRotation() {
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e331471a33db..be3cfeff729e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1884,6 +1884,9 @@ public class Intent implements Parcelable, Cloneable {
/**
* Activity action: Launch UI to manage auto-revoke state.
+ *
+ * This is equivalent to Intent#ACTION_APPLICATION_DETAILS_SETTINGS
+ *
* <p>
* Input: {@link Intent#setData data} should be a {@code package}-scheme {@link Uri} with
* a package name, whose auto-revoke state will be reviewed (mandatory).
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index ed75504529b9..fc4ccd072e75 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1449,6 +1449,13 @@ public class PackageInstaller {
/** {@hide} */
public static final int UID_UNKNOWN = -1;
+ /**
+ * This value is derived from the maximum file name length. No package above this limit
+ * can ever be successfully installed on the device.
+ * @hide
+ */
+ public static final int MAX_PACKAGE_NAME_LENGTH = 255;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int mode = MODE_INVALID;
@@ -1642,6 +1649,8 @@ public class PackageInstaller {
/**
* Optionally set a label representing the app being installed.
+ *
+ * This value will be trimmed to the first 1000 characters.
*/
public void setAppLabel(@Nullable CharSequence appLabel) {
this.appLabel = (appLabel != null) ? appLabel.toString() : null;
@@ -1711,7 +1720,8 @@ public class PackageInstaller {
*
* <p>Initially, all restricted permissions are whitelisted but you can change
* which ones are whitelisted by calling this method or the corresponding ones
- * on the {@link PackageManager}.
+ * on the {@link PackageManager}. Only soft or hard restricted permissions on the current
+ * Android version are supported and any invalid entries will be removed.
*
* @see PackageManager#addWhitelistedRestrictedPermission(String, String, int)
* @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int)
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index f354bdb5a08b..65ce1e7ef079 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -49,8 +49,16 @@ import java.util.Objects;
* in the implementation of Parcelable in subclasses.
*/
public class PackageItemInfo {
- /** The maximum length of a safe label, in characters */
- private static final int MAX_SAFE_LABEL_LENGTH = 50000;
+
+ /**
+ * The maximum length of a safe label, in characters
+ *
+ * TODO(b/157997155): It may make sense to expose this publicly so that apps can check for the
+ * value and truncate the strings/use a different label, without having to hardcode and make
+ * assumptions about the value.
+ * @hide
+ */
+ public static final int MAX_SAFE_LABEL_LENGTH = 1000;
/** @hide */
public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index f64560a14832..fb8fd74545c7 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -302,7 +302,14 @@ public class ParsedActivityUtils {
}
String permission = array.getNonConfigurationString(permissionAttr, 0);
- activity.setPermission(permission != null ? permission : pkg.getPermission());
+ if (isAlias) {
+ // An alias will override permissions to allow referencing an Activity through its alias
+ // without needing the original permission. If an alias needs the same permission,
+ // it must be re-declared.
+ activity.setPermission(permission);
+ } else {
+ activity.setPermission(permission != null ? permission : pkg.getPermission());
+ }
final boolean setExported = array.hasValue(exportedAttr);
if (setExported) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index b37b61757053..6811e06fbe7e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -20,7 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -29,9 +32,6 @@ import android.text.TextUtils;
import android.util.TypedValue;
import com.android.internal.annotations.VisibleForTesting;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
/** @hide */
class ParsedComponentUtils {
@@ -60,16 +60,27 @@ class ParsedComponentUtils {
component.setName(className);
component.setPackageName(packageName);
- if (useRoundIcon) {
- component.icon = array.getResourceId(roundIconAttr, 0);
+ int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
+ if (roundIconVal != 0) {
+ component.icon = roundIconVal;
+ component.nonLocalizedLabel = null;
+ } else {
+ int iconVal = array.getResourceId(iconAttr, 0);
+ if (iconVal != 0) {
+ component.icon = iconVal;
+ component.nonLocalizedLabel = null;
+ }
}
- if (component.icon == 0) {
- component.icon = array.getResourceId(iconAttr, 0);
+ int logoVal = array.getResourceId(logoAttr, 0);
+ if (logoVal != 0) {
+ component.logo = logoVal;
}
- component.logo = array.getResourceId(logoAttr, 0);
- component.banner = array.getResourceId(bannerAttr, 0);
+ int bannerVal = array.getResourceId(bannerAttr, 0);
+ if (bannerVal != 0) {
+ component.banner = bannerVal;
+ }
if (descriptionAttr != null) {
component.descriptionRes = array.getResourceId(descriptionAttr, 0);
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 8d472da1fb7c..570cc2c11738 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -116,22 +116,25 @@ public class BiometricManager {
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for <strong>Strong</strong>, as defined by the Android CDD.
+ * requirements for <strong>Tier 3</strong> (formerly <strong>Strong</strong>), as defined
+ * by the Android CDD.
*/
int BIOMETRIC_STRONG = 0x000F;
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for <strong>Weak</strong>, as defined by the Android CDD.
+ * requirements for <strong>Tier 2</strong> (formerly <strong>Weak</strong>), as defined by
+ * the Android CDD.
*
* <p>Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that
- * <code>BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK</code>.
+ * {@code BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK}.
*/
int BIOMETRIC_WEAK = 0x00FF;
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for <strong>Convenience</strong>, as defined by the Android CDD.
+ * requirements for <strong>Tier 1</strong> (formerly <strong>Convenience</strong>), as
+ * defined by the Android CDD.
*
* <p>This constant is intended for use by {@link android.provider.DeviceConfig} to adjust
* the reported strength of a biometric sensor. It is not a valid parameter for any of the
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 6bc962b67576..208406566e52 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -18,6 +18,7 @@ package android.hardware.hdmi;
import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +31,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
@@ -40,6 +42,7 @@ import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* The {@link HdmiControlManager} class is used to send HDMI control messages
@@ -818,6 +821,24 @@ public final class HdmiControlManager {
mHdmiControlStatusChangeListeners = new ArrayMap<>();
/**
+ * Listener used to get the status of the HDMI CEC volume control feature (enabled/disabled).
+ * @hide
+ */
+ public interface HdmiCecVolumeControlFeatureListener {
+ /**
+ * Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
+ *
+ * @param enabled status of HDMI CEC volume control feature
+ * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
+ **/
+ void onHdmiCecVolumeControlFeature(boolean enabled);
+ }
+
+ private final ArrayMap<HdmiCecVolumeControlFeatureListener,
+ IHdmiCecVolumeControlFeatureListener>
+ mHdmiCecVolumeControlFeatureListeners = new ArrayMap<>();
+
+ /**
* Listener used to get vendor-specific commands.
*/
public interface VendorCommandListener {
@@ -979,4 +1000,76 @@ public final class HdmiControlManager {
};
}
+ /**
+ * Adds a listener to get informed of changes to the state of the HDMI CEC volume control
+ * feature.
+ *
+ * Upon adding a listener, the current state of the HDMI CEC volume control feature will be
+ * sent immediately.
+ *
+ * <p>To stop getting the notification,
+ * use {@link #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener)}.
+ *
+ * @param listener {@link HdmiCecVolumeControlFeatureListener} instance
+ * @hide
+ * @see #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener)
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void addHdmiCecVolumeControlFeatureListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull HdmiCecVolumeControlFeatureListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ if (mHdmiCecVolumeControlFeatureListeners.containsKey(listener)) {
+ Log.e(TAG, "listener is already registered");
+ return;
+ }
+ IHdmiCecVolumeControlFeatureListener wrappedListener =
+ createHdmiCecVolumeControlFeatureListenerWrapper(executor, listener);
+ mHdmiCecVolumeControlFeatureListeners.put(listener, wrappedListener);
+ try {
+ mService.addHdmiCecVolumeControlFeatureListener(wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a listener to stop getting informed of changes to the state of the HDMI CEC volume
+ * control feature.
+ *
+ * @param listener {@link HdmiCecVolumeControlFeatureListener} instance to be removed
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void removeHdmiCecVolumeControlFeatureListener(
+ HdmiCecVolumeControlFeatureListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ IHdmiCecVolumeControlFeatureListener wrappedListener =
+ mHdmiCecVolumeControlFeatureListeners.remove(listener);
+ if (wrappedListener == null) {
+ Log.e(TAG, "tried to remove not-registered listener");
+ return;
+ }
+ try {
+ mService.removeHdmiCecVolumeControlFeatureListener(wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IHdmiCecVolumeControlFeatureListener createHdmiCecVolumeControlFeatureListenerWrapper(
+ Executor executor, final HdmiCecVolumeControlFeatureListener listener) {
+ return new android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener.Stub() {
+ @Override
+ public void onHdmiCecVolumeControlFeature(boolean enabled) {
+ Binder.clearCallingIdentity();
+ executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled));
+ }
+ };
+ }
}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl
new file mode 100644
index 000000000000..873438bb1d20
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.hardware.hdmi;
+
+/**
+ * Listener used to get the status of the HDMI CEC volume control feature (enabled/disabled).
+ * @hide
+ */
+oneway interface IHdmiCecVolumeControlFeatureListener {
+
+ /**
+ * Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
+ *
+ * @param enabled status of HDMI CEC volume control feature
+ * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
+ **/
+ void onHdmiCecVolumeControlFeature(boolean enabled);
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 3582a927ff46..4c724ef62ea9 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -18,6 +18,7 @@ package android.hardware.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiDeviceEventListener;
@@ -44,6 +45,8 @@ interface IHdmiControlService {
void queryDisplayStatus(IHdmiControlCallback callback);
void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
+ void addHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
+ void removeHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
void addHotplugEventListener(IHdmiHotplugEventListener listener);
void removeHotplugEventListener(IHdmiHotplugEventListener listener);
void addDeviceEventListener(IHdmiDeviceEventListener listener);
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 034e6a7a06c4..df58a6c636f5 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,6 +22,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -93,8 +94,8 @@ public class GraphicsEnvironment {
private static final int GAME_DRIVER_GLOBAL_OPT_IN_OFF = 3;
private ClassLoader mClassLoader;
- private String mLayerPath;
- private String mDebugLayerPath;
+ private String mLibrarySearchPaths;
+ private String mLibraryPermittedPaths;
/**
* Set up GraphicsEnvironment
@@ -185,118 +186,131 @@ public class GraphicsEnvironment {
}
/**
- * Store the layer paths available to the loader.
+ * Store the class loader for namespace lookup later.
*/
public void setLayerPaths(ClassLoader classLoader,
- String layerPath,
- String debugLayerPath) {
+ String searchPaths,
+ String permittedPaths) {
// We have to store these in the class because they are set up before we
// have access to the Context to properly set up GraphicsEnvironment
mClassLoader = classLoader;
- mLayerPath = layerPath;
- mDebugLayerPath = debugLayerPath;
+ mLibrarySearchPaths = searchPaths;
+ mLibraryPermittedPaths = permittedPaths;
+ }
+
+ /**
+ * Returns the debug layer paths from settings.
+ * Returns null if:
+ * 1) The application process is not debuggable or layer injection metadata flag is not
+ * true; Or
+ * 2) ENABLE_GPU_DEBUG_LAYERS is not true; Or
+ * 3) Package name is not equal to GPU_DEBUG_APP.
+ */
+ public String getDebugLayerPathsFromSettings(
+ Bundle coreSettings, IPackageManager pm, String packageName,
+ ApplicationInfo ai) {
+ if (!debugLayerEnabled(coreSettings, packageName, ai)) {
+ return null;
+ }
+ Log.i(TAG, "GPU debug layers enabled for " + packageName);
+ String debugLayerPaths = "";
+
+ // Grab all debug layer apps and add to paths.
+ final String gpuDebugLayerApps =
+ coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP, "");
+ if (!gpuDebugLayerApps.isEmpty()) {
+ Log.i(TAG, "GPU debug layer apps: " + gpuDebugLayerApps);
+ // If a colon is present, treat this as multiple apps, so Vulkan and GLES
+ // layer apps can be provided at the same time.
+ final String[] layerApps = gpuDebugLayerApps.split(":");
+ for (int i = 0; i < layerApps.length; i++) {
+ String paths = getDebugLayerAppPaths(pm, layerApps[i]);
+ if (!paths.isEmpty()) {
+ // Append the path so files placed in the app's base directory will
+ // override the external path
+ debugLayerPaths += paths + File.pathSeparator;
+ }
+ }
+ }
+ return debugLayerPaths;
}
/**
* Return the debug layer app's on-disk and in-APK lib directories
*/
- private static String getDebugLayerAppPaths(PackageManager pm, String app) {
+ private static String getDebugLayerAppPaths(IPackageManager pm, String packageName) {
final ApplicationInfo appInfo;
try {
- appInfo = pm.getApplicationInfo(app, PackageManager.MATCH_ALL);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Debug layer app '" + app + "' not installed");
-
- return null;
+ appInfo = pm.getApplicationInfo(packageName, PackageManager.MATCH_ALL,
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ return "";
+ }
+ if (appInfo == null) {
+ Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
}
final String abi = chooseAbi(appInfo);
-
final StringBuilder sb = new StringBuilder();
sb.append(appInfo.nativeLibraryDir)
- .append(File.pathSeparator);
- sb.append(appInfo.sourceDir)
+ .append(File.pathSeparator)
+ .append(appInfo.sourceDir)
.append("!/lib/")
.append(abi);
final String paths = sb.toString();
-
if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths);
return paths;
}
+ private boolean debugLayerEnabled(Bundle coreSettings, String packageName, ApplicationInfo ai) {
+ // Only enable additional debug functionality if the following conditions are met:
+ // 1. App is debuggable or device is rooted or layer injection metadata flag is true
+ // 2. ENABLE_GPU_DEBUG_LAYERS is true
+ // 3. Package name is equal to GPU_DEBUG_APP
+ if (!isDebuggable() && !canInjectLayers(ai)) {
+ return false;
+ }
+ final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+ if (enable == 0) {
+ return false;
+ }
+ final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP, "");
+ if (packageName == null
+ || (gpuDebugApp.isEmpty() || packageName.isEmpty())
+ || !gpuDebugApp.equals(packageName)) {
+ return false;
+ }
+ return true;
+ }
+
/**
* Set up layer search paths for all apps
- * If debuggable, check for additional debug settings
*/
private void setupGpuLayers(
Context context, Bundle coreSettings, PackageManager pm, String packageName,
ApplicationInfo ai) {
+ final boolean enabled = debugLayerEnabled(coreSettings, packageName, ai);
String layerPaths = "";
+ if (enabled) {
+ layerPaths = mLibraryPermittedPaths;
- // Only enable additional debug functionality if the following conditions are met:
- // 1. App is debuggable or device is rooted or layer injection metadata flag is true
- // 2. ENABLE_GPU_DEBUG_LAYERS is true
- // 3. Package name is equal to GPU_DEBUG_APP
+ final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
+ Log.i(TAG, "Vulkan debug layer list: " + layers);
+ if (layers != null && !layers.isEmpty()) {
+ setDebugLayers(layers);
+ }
- if (isDebuggable() || canInjectLayers(ai)) {
-
- final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
-
- if (enable != 0) {
-
- final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP);
-
- if ((gpuDebugApp != null && packageName != null)
- && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
- && gpuDebugApp.equals(packageName)) {
- Log.i(TAG, "GPU debug layers enabled for " + packageName);
-
- // Prepend the debug layer path as a searchable path.
- // This will ensure debug layers added will take precedence over
- // the layers specified by the app.
- layerPaths = mDebugLayerPath + ":";
-
- // If there is a debug layer app specified, add its path.
- final String gpuDebugLayerApp =
- coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP);
-
- if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) {
- Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp);
- // If a colon is present, treat this as multiple apps, so Vulkan and GLES
- // layer apps can be provided at the same time.
- String[] layerApps = gpuDebugLayerApp.split(":");
- for (int i = 0; i < layerApps.length; i++) {
- String paths = getDebugLayerAppPaths(pm, layerApps[i]);
- if (paths != null) {
- // Append the path so files placed in the app's base directory will
- // override the external path
- layerPaths += paths + ":";
- }
- }
- }
-
- final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
-
- Log.i(TAG, "Vulkan debug layer list: " + layers);
- if (layers != null && !layers.isEmpty()) {
- setDebugLayers(layers);
- }
-
- final String layersGLES =
- coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES);
-
- Log.i(TAG, "GLES debug layer list: " + layersGLES);
- if (layersGLES != null && !layersGLES.isEmpty()) {
- setDebugLayersGLES(layersGLES);
- }
- }
+ final String layersGLES =
+ coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES);
+ Log.i(TAG, "GLES debug layer list: " + layersGLES);
+ if (layersGLES != null && !layersGLES.isEmpty()) {
+ setDebugLayersGLES(layersGLES);
}
}
// Include the app's lib directory in all cases
- layerPaths += mLayerPath;
-
+ layerPaths += mLibrarySearchPaths;
setLayerPaths(mClassLoader, layerPaths);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7845200f4bf7..a8391c2b5461 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4090,13 +4090,6 @@ public class UserManager {
public static int getMaxSupportedUsers() {
// Don't allow multiple users on certain builds
if (android.os.Build.ID.startsWith("JVP")) return 1;
- if (ActivityManager.isLowRamDeviceStatic()) {
- // Low-ram devices are Svelte. Most of the time they don't get multi-user.
- if ((Resources.getSystem().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
- != Configuration.UI_MODE_TYPE_TELEVISION) {
- return 1;
- }
- }
return SystemProperties.getInt("fw.max_users",
Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1b19e1290121..e10fceaa5bc7 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1778,6 +1778,15 @@ public final class Settings {
= "android.settings.NOTIFICATION_SETTINGS";
/**
+ * Activity Action: Show conversation settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CONVERSATION_SETTINGS
+ = "android.settings.CONVERSATION_SETTINGS";
+
+ /**
* Activity Action: Show notification history screen.
*
* @hide
@@ -14245,15 +14254,6 @@ public final class Settings {
public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader";
/**
- * Persistent user id that is last logged in to.
- *
- * They map to user ids, for example, 10, 11, 12.
- *
- * @hide
- */
- public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
-
- /**
* Whether we've enabled native flags health check on this device. Takes effect on
* reboot. The value "1" enables native flags health check; otherwise it's disabled.
* @hide
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index b34268d04238..a2489b9b68d9 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4325,6 +4325,15 @@ public final class Telephony {
public static final String ETWS_WARNING_TYPE = "etws_warning_type";
/**
+ * ETWS (Earthquake and Tsunami Warning System) primary message or not (ETWS alerts only).
+ * <p>See {@link android.telephony.SmsCbEtwsInfo}</p>
+ * <P>Type: BOOLEAN</P>
+ *
+ * @hide // TODO: Unhide this for S.
+ */
+ public static final String ETWS_IS_PRIMARY = "etws_is_primary";
+
+ /**
* CMAS (Commercial Mobile Alert System) message class (CMAS alerts only).
* <p>See {@link android.telephony.SmsCbCmasInfo}</p>
* <P>Type: INTEGER</P>
@@ -4464,37 +4473,6 @@ public final class Telephony {
CMAS_URGENCY,
CMAS_CERTAINTY
};
-
- /**
- * Query columns for instantiating {@link android.telephony.SmsCbMessage} objects.
- * @hide
- */
- public static final String[] QUERY_COLUMNS_FWK = {
- _ID,
- SLOT_INDEX,
- SUBSCRIPTION_ID,
- GEOGRAPHICAL_SCOPE,
- PLMN,
- LAC,
- CID,
- SERIAL_NUMBER,
- SERVICE_CATEGORY,
- LANGUAGE_CODE,
- MESSAGE_BODY,
- MESSAGE_FORMAT,
- MESSAGE_PRIORITY,
- ETWS_WARNING_TYPE,
- CMAS_MESSAGE_CLASS,
- CMAS_CATEGORY,
- CMAS_RESPONSE_TYPE,
- CMAS_SEVERITY,
- CMAS_URGENCY,
- CMAS_CERTAINTY,
- RECEIVED_TIME,
- MESSAGE_BROADCASTED,
- GEOMETRIES,
- MAXIMUM_WAIT_TIME
- };
}
/**
diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
index c879653859d8..16c3f1d4e476 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -58,7 +58,9 @@ public class InlineSuggestionRoot extends FrameLayout {
case MotionEvent.ACTION_DOWN: {
mDownX = event.getX();
mDownY = event.getY();
- } break;
+ }
+ // Intentionally fall through to the next case so that when the window is obscured
+ // we transfer the touch to the remote IME window and don't handle it locally.
case MotionEvent.ACTION_MOVE: {
final float distance = MathUtils.dist(mDownX, mDownY,
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index d01bc2524332..8383072a48e3 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -73,25 +73,37 @@ public final class Control implements Parcelable {
})
public @interface Status {};
+ /**
+ * Reserved for use with the {@link StatelessBuilder}, and while loading. When state is
+ * requested via {@link ControlsProviderService#createPublisherFor}, use other status codes
+ * to indicate the proper device state.
+ */
public static final int STATUS_UNKNOWN = 0;
/**
- * The device corresponding to the {@link Control} is responding correctly.
+ * Used to indicate that the state of the device was successfully retrieved. This includes
+ * all scenarios where the device may have a warning for the user, such as "Lock jammed",
+ * or "Vacuum stuck". Any information for the user should be set through
+ * {@link StatefulBuilder#setStatusText}.
*/
public static final int STATUS_OK = 1;
/**
- * The device corresponding to the {@link Control} cannot be found or was removed.
+ * The device corresponding to the {@link Control} cannot be found or was removed. The user
+ * will be alerted and directed to the application to resolve.
*/
public static final int STATUS_NOT_FOUND = 2;
/**
- * The device corresponding to the {@link Control} is in an error state.
+ * Used to indicate that there was a temporary error while loading the device state. A default
+ * error message will be displayed in place of any custom text that was set through
+ * {@link StatefulBuilder#setStatusText}.
*/
public static final int STATUS_ERROR = 3;
/**
- * The {@link Control} is currently disabled.
+ * The {@link Control} is currently disabled. A default error message will be displayed in
+ * place of any custom text that was set through {@link StatefulBuilder#setStatusText}.
*/
public static final int STATUS_DISABLED = 4;
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e4fbf9f0e187..9b293eb463e5 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -340,6 +340,10 @@ public class PhoneStateListener {
/**
* Listen for display info changed event.
*
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+ * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ *
* @see #onDisplayInfoChanged
*/
public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index a9af59543c78..f6c72c4eefbc 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -289,11 +289,6 @@ public class GestureDetector {
private VelocityTracker mVelocityTracker;
/**
- * True if the detector can throw exception when touch steam is unexpected .
- */
- private boolean mExceptionForTouchStream;
-
- /**
* Consistency verifier for debugging purposes.
*/
private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
@@ -472,8 +467,6 @@ public class GestureDetector {
mTouchSlopSquare = touchSlop * touchSlop;
mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
- mExceptionForTouchStream = context != null
- && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R;
}
/**
@@ -646,13 +639,6 @@ public class GestureDetector {
break;
case MotionEvent.ACTION_MOVE:
- if (mExceptionForTouchStream && !mStillDown) {
- throw new IllegalStateException("Incomplete event stream received: "
- + "Received ACTION_MOVE before ACTION_DOWN. ACTION_DOWN must precede "
- + "ACTION_MOVE following ACTION_UP or ACTION_CANCEL, or when this "
- + "GestureDetector has not yet received any events.");
- }
-
if (mInLongPress || mInContextClick) {
break;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index bd811fc1f052..a954f3631a01 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1200,8 +1200,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
if (mDeferredDestroySurfaceControl != null) {
- mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
- mDeferredDestroySurfaceControl = null;
+ synchronized (mSurfaceControlLock) {
+ mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
+ mDeferredDestroySurfaceControl = null;
+ }
}
runOnUiThread(this::performDrawFinished);
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index b387a68dd8a3..68943bf2a83a 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -75,7 +75,10 @@ public final class AutofillId implements Parcelable {
/** @hide */
public static AutofillId withoutSession(@NonNull AutofillId id) {
final int flags = id.mFlags & ~FLAG_HAS_SESSION;
- return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION);
+ final long virtualChildId =
+ ((id.mFlags & FLAG_IS_VIRTUAL_LONG) != 0) ? id.mVirtualLongId
+ : id.mVirtualIntId;
+ return new AutofillId(flags, id.mViewId, virtualChildId, NO_SESSION);
}
/** @hide */
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 7042f29fc4e4..4a6551176198 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -205,6 +205,8 @@ public class WebChromeClient {
* <p>Note that if the {@link WebChromeClient} is set to be {@code null},
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and Javascript execution will continue immediately.
+ * <p>Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
@@ -240,6 +242,8 @@ public class WebChromeClient {
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and the default value of {@code false} will be returned to
* the JavaScript code immediately.
+ * <p>Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
@@ -274,6 +278,8 @@ public class WebChromeClient {
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and {@code null} will be returned to the JavaScript code
* immediately.
+ * <p>Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
@@ -308,6 +314,8 @@ public class WebChromeClient {
* <p>Note that if the {@link WebChromeClient} is set to be {@code null},
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and the navigation will be resumed immediately.
+ * <p>Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index d2614da31ff9..9ccb4c172158 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -365,8 +365,8 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
// Found the topmost stack on target display. Now check if the topmost task's
// description changed.
if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
- mHost.onTaskBackgroundColorChanged(VirtualDisplayTaskEmbedder.this,
- taskInfo.taskDescription.getBackgroundColor());
+ mHost.post(()-> mHost.onTaskBackgroundColorChanged(VirtualDisplayTaskEmbedder.this,
+ taskInfo.taskDescription.getBackgroundColor()));
}
}
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 493865ac563f..b723db287823 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -151,6 +151,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
mOnProfileSelectedListener.onProfileSelected(position);
}
}
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (mOnProfileSelectedListener != null) {
+ mOnProfileSelectedListener.onProfilePageStateChanged(state);
+ }
+ }
});
viewPager.setAdapter(this);
viewPager.setCurrentItem(mCurrentPage);
@@ -606,6 +613,17 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
* {@link #PROFILE_WORK} if the work profile was selected.
*/
void onProfileSelected(int profileIndex);
+
+
+ /**
+ * Callback for when the scroll state changes. Useful for discovering when the user begins
+ * dragging, when the pager is automatically settling to the current page, or when it is
+ * fully stopped/idle.
+ * @param state {@link ViewPager#SCROLL_STATE_IDLE}, {@link ViewPager#SCROLL_STATE_DRAGGING}
+ * or {@link ViewPager#SCROLL_STATE_SETTLING}
+ * @see ViewPager.OnPageChangeListener#onPageScrollStateChanged
+ */
+ void onProfilePageStateChanged(int state);
}
/**
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2a43287a3ae3..8f3edb8f1787 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -102,6 +102,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.Button;
@@ -129,6 +130,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.GridLayoutManager;
import com.android.internal.widget.RecyclerView;
import com.android.internal.widget.ResolverDrawerLayout;
+import com.android.internal.widget.ViewPager;
import com.google.android.collect.Lists;
@@ -204,6 +206,10 @@ public class ChooserActivity extends ResolverActivity implements
public static final int SELECTION_TYPE_STANDARD = 3;
public static final int SELECTION_TYPE_COPY = 4;
+ private static final int SCROLL_STATUS_IDLE = 0;
+ private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1;
+ private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2;
+
// statsd logger wrapper
protected ChooserActivityLogger mChooserActivityLogger;
@@ -293,6 +299,7 @@ public class ChooserActivity extends ResolverActivity implements
protected MetricsLogger mMetricsLogger;
private ContentPreviewCoordinator mPreviewCoord;
+ private int mScrollStatus = SCROLL_STATUS_IDLE;
@VisibleForTesting
protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter;
@@ -2647,6 +2654,7 @@ public class ChooserActivity extends ResolverActivity implements
if (recyclerView.getVisibility() == View.VISIBLE) {
int directShareHeight = 0;
rowsToShow = Math.min(4, rowsToShow);
+ boolean shouldShowExtraRow = shouldShowExtraRow(rowsToShow);
mLastNumberOfChildren = recyclerView.getChildCount();
for (int i = 0, childCount = recyclerView.getChildCount();
i < childCount && rowsToShow > 0; i++) {
@@ -2657,6 +2665,9 @@ public class ChooserActivity extends ResolverActivity implements
}
int height = child.getHeight();
offset += height;
+ if (shouldShowExtraRow) {
+ offset += height;
+ }
if (gridAdapter.getTargetType(
recyclerView.getChildAdapterPosition(child))
@@ -2680,7 +2691,7 @@ public class ChooserActivity extends ResolverActivity implements
offset = Math.min(offset, minHeight);
}
} else {
- ViewGroup currentEmptyStateView = getCurrentEmptyStateView();
+ ViewGroup currentEmptyStateView = getActiveEmptyStateView();
if (currentEmptyStateView.getVisibility() == View.VISIBLE) {
offset += currentEmptyStateView.getHeight();
}
@@ -2692,6 +2703,18 @@ public class ChooserActivity extends ResolverActivity implements
}
/**
+ * If we have a tabbed view and are showing 1 row in the current profile and an empty
+ * state screen in the other profile, to prevent cropping of the empty state screen we show
+ * a second row in the current profile.
+ */
+ private boolean shouldShowExtraRow(int rowsToShow) {
+ return shouldShowTabs()
+ && rowsToShow == 1
+ && mChooserMultiProfilePagerAdapter.shouldShowEmptyStateScreen(
+ mChooserMultiProfilePagerAdapter.getInactiveListAdapter());
+ }
+
+ /**
* Returns {@link #PROFILE_PERSONAL}, {@link #PROFILE_WORK}, or -1 if the given user handle
* does not match either the personal or work user handle.
**/
@@ -2705,7 +2728,7 @@ public class ChooserActivity extends ResolverActivity implements
return -1;
}
- private ViewGroup getCurrentEmptyStateView() {
+ private ViewGroup getActiveEmptyStateView() {
int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage();
return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView();
}
@@ -2822,10 +2845,20 @@ public class ChooserActivity extends ResolverActivity implements
final float defaultElevation = elevatedView.getElevation();
final float chooserHeaderScrollElevation =
getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
-
mChooserMultiProfilePagerAdapter.getActiveAdapterView().addOnScrollListener(
new RecyclerView.OnScrollListener() {
public void onScrollStateChanged(RecyclerView view, int scrollState) {
+ if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
+ if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) {
+ mScrollStatus = SCROLL_STATUS_IDLE;
+ setHorizontalScrollingEnabled(true);
+ }
+ } else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
+ if (mScrollStatus == SCROLL_STATUS_IDLE) {
+ mScrollStatus = SCROLL_STATUS_SCROLLING_VERTICAL;
+ setHorizontalScrollingEnabled(false);
+ }
+ }
}
public void onScrolled(RecyclerView view, int dx, int dy) {
@@ -3026,8 +3059,42 @@ public class ChooserActivity extends ResolverActivity implements
currentRootAdapter.updateDirectShareExpansion();
}
- void prepareIntentForCrossProfileLaunch(Intent intent) {
- intent.fixUris(UserHandle.myUserId());
+ @Override
+ protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ if (shouldShowTabs()) {
+ mChooserMultiProfilePagerAdapter
+ .setEmptyStateBottomOffset(insets.getSystemWindowInsetBottom());
+ mChooserMultiProfilePagerAdapter.setupContainerPadding(
+ getActiveEmptyStateView().findViewById(R.id.resolver_empty_state_container));
+ }
+ return super.onApplyWindowInsets(v, insets);
+ }
+
+ private void setHorizontalScrollingEnabled(boolean enabled) {
+ ResolverViewPager viewPager = findViewById(R.id.profile_pager);
+ viewPager.setSwipingEnabled(enabled);
+ }
+
+ private void setVerticalScrollEnabled(boolean enabled) {
+ ChooserGridLayoutManager layoutManager =
+ (ChooserGridLayoutManager) mChooserMultiProfilePagerAdapter.getActiveAdapterView()
+ .getLayoutManager();
+ layoutManager.setVerticalScrollEnabled(enabled);
+ }
+
+ @Override
+ void onHorizontalSwipeStateChanged(int state) {
+ if (state == ViewPager.SCROLL_STATE_DRAGGING) {
+ if (mScrollStatus == SCROLL_STATUS_IDLE) {
+ mScrollStatus = SCROLL_STATUS_SCROLLING_HORIZONTAL;
+ setVerticalScrollEnabled(false);
+ }
+ } else if (state == ViewPager.SCROLL_STATE_IDLE) {
+ if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) {
+ mScrollStatus = SCROLL_STATUS_IDLE;
+ setVerticalScrollEnabled(true);
+ }
+ }
}
/**
diff --git a/core/java/com/android/internal/app/ChooserGridLayoutManager.java b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
index 317a987cf359..c50ebd9562c9 100644
--- a/core/java/com/android/internal/app/ChooserGridLayoutManager.java
+++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
@@ -28,6 +28,8 @@ import com.android.internal.widget.RecyclerView;
*/
public class ChooserGridLayoutManager extends GridLayoutManager {
+ private boolean mVerticalScrollEnabled = true;
+
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". If spanCount is not specified in the XML, it defaults to a
@@ -67,4 +69,13 @@ public class ChooserGridLayoutManager extends GridLayoutManager {
// Do not count the footer view in the official count
return super.getRowCountForAccessibility(recycler, state) - 1;
}
+
+ void setVerticalScrollEnabled(boolean verticalScrollEnabled) {
+ mVerticalScrollEnabled = verticalScrollEnabled;
+ }
+
+ @Override
+ public boolean canScrollVertically() {
+ return mVerticalScrollEnabled && super.canScrollVertically();
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 774be3c9c4b8..ffa6041721c6 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -38,6 +38,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
private final ChooserProfileDescriptor[] mItems;
private final boolean mIsSendAction;
+ private int mBottomOffset;
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter adapter,
@@ -245,6 +246,16 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
}
}
+ void setEmptyStateBottomOffset(int bottomOffset) {
+ mBottomOffset = bottomOffset;
+ }
+
+ @Override
+ protected void setupContainerPadding(View container) {
+ container.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
+ container.getPaddingRight(), container.getPaddingBottom() + mBottomOffset);
+ }
+
class ChooserProfileDescriptor extends ProfileDescriptor {
private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
private RecyclerView recyclerView;
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index e65d1fe9ce53..61a52bcc03f9 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -18,6 +18,7 @@ package com.android.internal.app;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER;
import static com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE;
import android.annotation.Nullable;
@@ -246,6 +247,7 @@ public class IntentForwarderActivity extends Activity {
int selectedProfile = findSelectedProfile(className);
sanitizeIntent(intentReceived);
intentReceived.putExtra(EXTRA_SELECTED_PROFILE, selectedProfile);
+ intentReceived.putExtra(EXTRA_CALLING_USER, UserHandle.of(callingUserId));
startActivityAsCaller(intentReceived, null, null, false, userId);
finish();
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f96f560fc60f..f8eec57bbd6b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -184,6 +184,18 @@ public class ResolverActivity extends Activity implements
static final String EXTRA_SELECTED_PROFILE =
"com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE";
+ /**
+ * {@link UserHandle} extra to indicate the user of the user that the starting intent
+ * originated from.
+ * <p>This is not necessarily the same as {@link #getUserId()} or {@link UserHandle#myUserId()},
+ * as there are edge cases when the intent resolver is launched in the other profile.
+ * For example, when we have 0 resolved apps in current profile and multiple resolved
+ * apps in the other profile, opening a link from the current profile launches the intent
+ * resolver in the other one. b/148536209 for more info.
+ */
+ static final String EXTRA_CALLING_USER =
+ "com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER";
+
static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK;
@@ -470,17 +482,20 @@ public class ResolverActivity extends Activity implements
// the intent resolver is started in the other profile. Since this is the only case when
// this happens, we check for it here and set the current profile's tab.
int selectedProfile = getCurrentProfile();
- UserHandle intentUser = UserHandle.of(getLaunchingUserId());
+ UserHandle intentUser = getIntent().hasExtra(EXTRA_CALLING_USER)
+ ? getIntent().getParcelableExtra(EXTRA_CALLING_USER)
+ : getUser();
if (!getUser().equals(intentUser)) {
if (getPersonalProfileUserHandle().equals(intentUser)) {
selectedProfile = PROFILE_PERSONAL;
} else if (getWorkProfileUserHandle().equals(intentUser)) {
selectedProfile = PROFILE_WORK;
}
- }
- int selectedProfileExtra = getSelectedProfileExtra();
- if (selectedProfileExtra != -1) {
- selectedProfile = selectedProfileExtra;
+ } else {
+ int selectedProfileExtra = getSelectedProfileExtra();
+ if (selectedProfileExtra != -1) {
+ selectedProfile = selectedProfileExtra;
+ }
}
// We only show the default app for the profile of the current user. The filterLastUsed
// flag determines whether to show a default app and that app is not shown in the
@@ -535,22 +550,6 @@ public class ResolverActivity extends Activity implements
return selectedProfile;
}
- /**
- * Returns the user id of the user that the starting intent originated from.
- * <p>This is not necessarily equal to {@link #getUserId()} or {@link UserHandle#myUserId()},
- * as there are edge cases when the intent resolver is launched in the other profile.
- * For example, when we have 0 resolved apps in current profile and multiple resolved apps
- * in the other profile, opening a link from the current profile launches the intent resolver
- * in the other one. b/148536209 for more info.
- */
- private int getLaunchingUserId() {
- int contentUserHint = getIntent().getContentUserHint();
- if (contentUserHint == UserHandle.USER_CURRENT) {
- return UserHandle.myUserId();
- }
- return contentUserHint;
- }
-
protected @Profile int getCurrentProfile() {
return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
}
@@ -1250,7 +1249,9 @@ public class ResolverActivity extends Activity implements
return true;
}
- void prepareIntentForCrossProfileLaunch(Intent intent) {}
+ private void prepareIntentForCrossProfileLaunch(Intent intent) {
+ intent.fixUris(UserHandle.myUserId());
+ }
private boolean isLaunchingTargetInOtherProfile() {
return mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
@@ -1653,10 +1654,18 @@ public class ResolverActivity extends Activity implements
viewPager.setVisibility(View.VISIBLE);
tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
mMultiProfilePagerAdapter.setOnProfileSelectedListener(
- index -> {
- tabHost.setCurrentTab(index);
- resetButtonBar();
- resetCheckedItem();
+ new AbstractMultiProfilePagerAdapter.OnProfileSelectedListener() {
+ @Override
+ public void onProfileSelected(int index) {
+ tabHost.setCurrentTab(index);
+ resetButtonBar();
+ resetCheckedItem();
+ }
+
+ @Override
+ public void onProfilePageStateChanged(int state) {
+ onHorizontalSwipeStateChanged(state);
+ }
});
mMultiProfilePagerAdapter.setOnSwitchOnWorkSelectedListener(
() -> {
@@ -1668,6 +1677,8 @@ public class ResolverActivity extends Activity implements
findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
}
+ void onHorizontalSwipeStateChanged(int state) {}
+
private void maybeHideDivider() {
if (!isIntentPicker()) {
return;
diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java
index 4eb6e3bd2071..9cdfc2f5c763 100644
--- a/core/java/com/android/internal/app/ResolverViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -18,6 +18,7 @@ package com.android.internal.app;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.ViewPager;
@@ -30,6 +31,8 @@ import com.android.internal.widget.ViewPager;
*/
public class ResolverViewPager extends ViewPager {
+ private boolean mSwipingEnabled = true;
+
public ResolverViewPager(Context context) {
super(context);
}
@@ -70,4 +73,13 @@ public class ResolverViewPager extends ViewPager {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+
+ void setSwipingEnabled(boolean swipingEnabled) {
+ mSwipingEnabled = swipingEnabled;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return mSwipingEnabled && super.onInterceptTouchEvent(ev);
+ }
}
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index 67ffd4d93404..5212265f6c8a 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -60,4 +60,28 @@ public interface UiEventLogger {
*/
void logWithInstanceId(@NonNull UiEventEnum event, int uid, @Nullable String packageName,
@Nullable InstanceId instance);
+
+ /**
+ * Log an event with ranked-choice information along with package.
+ * Does nothing if event.getId() <= 0.
+ * @param event an enum implementing UiEventEnum interface.
+ * @param uid the uid of the relevant app, if known (0 otherwise).
+ * @param packageName the package name of the relevant app, if known (null otherwise).
+ * @param position the position picked.
+ */
+ void logWithPosition(@NonNull UiEventEnum event, int uid, @Nullable String packageName,
+ int position);
+
+ /**
+ * Log an event with ranked-choice information along with package and instance ID.
+ * Does nothing if event.getId() <= 0.
+ * @param event an enum implementing UiEventEnum interface.
+ * @param uid the uid of the relevant app, if known (0 otherwise).
+ * @param packageName the package name of the relevant app, if known (null otherwise).
+ * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to
+ * logWithPosition().
+ * @param position the position picked.
+ */
+ void logWithInstanceIdAndPosition(@NonNull UiEventEnum event, int uid,
+ @Nullable String packageName, @Nullable InstanceId instance, int position);
}
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index 4d171ec8a3a8..c9156c13aae3 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -48,4 +48,31 @@ public class UiEventLoggerImpl implements UiEventLogger {
log(event, uid, packageName);
}
}
+
+ @Override
+ public void logWithPosition(UiEventEnum event, int uid, String packageName, int position) {
+ final int eventID = event.getId();
+ if (eventID > 0) {
+ FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
+ /* event_id = 1 */ eventID,
+ /* package_name = 2 */ packageName,
+ /* instance_id = 3 */ 0,
+ /* position_picked = 4 */ position);
+ }
+ }
+
+ @Override
+ public void logWithInstanceIdAndPosition(UiEventEnum event, int uid, String packageName,
+ InstanceId instance, int position) {
+ final int eventID = event.getId();
+ if ((eventID > 0) && (instance != null)) {
+ FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
+ /* event_id = 1 */ eventID,
+ /* package_name = 2 */ packageName,
+ /* instance_id = 3 */ instance.getId(),
+ /* position_picked = 4 */ position);
+ } else {
+ logWithPosition(event, uid, packageName, position);
+ }
+ }
}
diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
index 180ab0810f5b..2d09434807a6 100644
--- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
+++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
@@ -35,13 +35,15 @@ public class UiEventLoggerFake implements UiEventLogger {
public final int eventId;
public final int uid;
public final String packageName;
- public final InstanceId instanceId; // Used only for WithInstanceId variant
+ public final InstanceId instanceId; // Used only for WithInstanceId variants
+ public final int position; // Used only for Position variants
FakeUiEvent(int eventId, int uid, String packageName) {
this.eventId = eventId;
this.uid = uid;
this.packageName = packageName;
this.instanceId = null;
+ this.position = 0;
}
FakeUiEvent(int eventId, int uid, String packageName, InstanceId instanceId) {
@@ -49,6 +51,15 @@ public class UiEventLoggerFake implements UiEventLogger {
this.uid = uid;
this.packageName = packageName;
this.instanceId = instanceId;
+ this.position = 0;
+ }
+
+ FakeUiEvent(int eventId, int uid, String packageName, InstanceId instanceId, int position) {
+ this.eventId = eventId;
+ this.uid = uid;
+ this.packageName = packageName;
+ this.instanceId = instanceId;
+ this.position = position;
}
}
@@ -92,4 +103,21 @@ public class UiEventLoggerFake implements UiEventLogger {
mLogs.add(new FakeUiEvent(eventId, uid, packageName, instance));
}
}
+
+ @Override
+ public void logWithPosition(UiEventEnum event, int uid, String packageName, int position) {
+ final int eventId = event.getId();
+ if (eventId > 0) {
+ mLogs.add(new FakeUiEvent(eventId, uid, packageName, null, position));
+ }
+ }
+
+ @Override
+ public void logWithInstanceIdAndPosition(UiEventEnum event, int uid, String packageName,
+ InstanceId instance, int position) {
+ final int eventId = event.getId();
+ if (eventId > 0) {
+ mLogs.add(new FakeUiEvent(eventId, uid, packageName, instance, position));
+ }
+ }
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index ad6c7e8f7f60..adc7ba30c157 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -8,10 +8,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -37,10 +37,12 @@ public class ScreenshotHelper {
private int mSource;
private boolean mHasStatusBar;
private boolean mHasNavBar;
- private Bitmap mBitmap;
+ private Bundle mBitmapBundle;
private Rect mBoundsInScreen;
private Insets mInsets;
private int mTaskId;
+ private int mUserId;
+ private ComponentName mTopComponent;
ScreenshotRequest(int source, boolean hasStatus, boolean hasNav) {
mSource = source;
@@ -48,24 +50,29 @@ public class ScreenshotHelper {
mHasNavBar = hasNav;
}
- ScreenshotRequest(
- int source, Bitmap bitmap, Rect boundsInScreen, Insets insets, int taskId) {
+ ScreenshotRequest(int source, Bundle bitmapBundle, Rect boundsInScreen, Insets insets,
+ int taskId, int userId, ComponentName topComponent) {
mSource = source;
- mBitmap = bitmap;
+ mBitmapBundle = bitmapBundle;
mBoundsInScreen = boundsInScreen;
mInsets = insets;
mTaskId = taskId;
+ mUserId = userId;
+ mTopComponent = topComponent;
}
ScreenshotRequest(Parcel in) {
mSource = in.readInt();
mHasStatusBar = in.readBoolean();
mHasNavBar = in.readBoolean();
+
if (in.readInt() == 1) {
- mBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ mBitmapBundle = in.readBundle(getClass().getClassLoader());
mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader());
mInsets = in.readParcelable(Insets.class.getClassLoader());
mTaskId = in.readInt();
+ mUserId = in.readInt();
+ mTopComponent = in.readParcelable(ComponentName.class.getClassLoader());
}
}
@@ -81,8 +88,8 @@ public class ScreenshotHelper {
return mHasNavBar;
}
- public Bitmap getBitmap() {
- return mBitmap;
+ public Bundle getBitmapBundle() {
+ return mBitmapBundle;
}
public Rect getBoundsInScreen() {
@@ -97,6 +104,15 @@ public class ScreenshotHelper {
return mTaskId;
}
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public ComponentName getTopComponent() {
+ return mTopComponent;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -107,14 +123,16 @@ public class ScreenshotHelper {
dest.writeInt(mSource);
dest.writeBoolean(mHasStatusBar);
dest.writeBoolean(mHasNavBar);
- if (mBitmap == null) {
+ if (mBitmapBundle == null) {
dest.writeInt(0);
} else {
dest.writeInt(1);
- dest.writeParcelable(mBitmap, 0);
+ dest.writeBundle(mBitmapBundle);
dest.writeParcelable(mBoundsInScreen, 0);
dest.writeParcelable(mInsets, 0);
dest.writeInt(mTaskId);
+ dest.writeInt(mUserId);
+ dest.writeParcelable(mTopComponent, 0);
}
}
@@ -234,19 +252,22 @@ public class ScreenshotHelper {
/**
* Request that provided image be handled as if it was a screenshot.
*
- * @param screenshot The bitmap to treat as the screen shot.
+ * @param screenshotBundle Bundle containing the buffer and color space of the screenshot.
* @param boundsInScreen The bounds in screen coordinates that the bitmap orginated from.
* @param insets The insets that the image was shown with, inside the screenbounds.
* @param taskId The taskId of the task that the screen shot was taken of.
+ * @param userId The userId of user running the task provided in taskId.
+ * @param topComponent The component name of the top component running in the task.
* @param handler A handler used in case the screenshot times out
* @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the
* screenshot was taken.
*/
- public void provideScreenshot(@NonNull Bitmap screenshot, @NonNull Rect boundsInScreen,
- @NonNull Insets insets, int taskId, int source,
+ public void provideScreenshot(@NonNull Bundle screenshotBundle, @NonNull Rect boundsInScreen,
+ @NonNull Insets insets, int taskId, int userId, ComponentName topComponent, int source,
@NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) {
ScreenshotRequest screenshotRequest =
- new ScreenshotRequest(source, screenshot, boundsInScreen, insets, taskId);
+ new ScreenshotRequest(source, screenshotBundle, boundsInScreen, insets, taskId,
+ userId, topComponent);
takeScreenshot(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_TIMEOUT_MS,
handler, screenshotRequest, completionConsumer);
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 688e00bc5a29..0d2dbefb9cd7 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -480,23 +480,25 @@ public class ConversationLayout extends FrameLayout
// (This usually happens for most 1:1 conversations)
conversationText = messagingGroup.getSenderName();
}
- Icon avatarIcon = messagingGroup.getAvatarIcon();
- if (avatarIcon == null) {
- avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+ if (mConversationIcon == null) {
+ Icon avatarIcon = messagingGroup.getAvatarIcon();
+ if (avatarIcon == null) {
+ avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+ }
+ mConversationIcon = avatarIcon;
}
- mConversationIcon = avatarIcon;
- mConversationIconView.setImageIcon(mConversationIcon);
break;
}
}
} else {
- if (mLargeIcon != null) {
+ if (mConversationIcon == null && mLargeIcon != null) {
mConversationIcon = mLargeIcon;
+ }
+ if (mConversationIcon != null) {
mConversationIconView.setVisibility(VISIBLE);
mConversationFacePile.setVisibility(GONE);
- mConversationIconView.setImageIcon(mLargeIcon);
+ mConversationIconView.setImageIcon(mConversationIcon);
} else {
- mConversationIcon = null;
mConversationIconView.setVisibility(GONE);
// This will also inflate it!
mConversationFacePile.setVisibility(VISIBLE);
@@ -709,6 +711,11 @@ public class ConversationLayout extends FrameLayout
mLargeIcon = largeIcon;
}
+ @RemotableViewMethod
+ public void setConversationIcon(Icon conversationIcon) {
+ mConversationIcon = conversationIcon;
+ }
+
/**
* Sets the conversation title of this conversation.
*
@@ -1216,7 +1223,6 @@ public class ConversationLayout extends FrameLayout
mExpandButtonContainer.setVisibility(VISIBLE);
mExpandButtonInnerContainer.setOnClickListener(onClickListener);
} else {
- // TODO: handle content paddings to end of layout
mExpandButtonContainer.setVisibility(GONE);
}
updateContentEndPaddings();
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index fc2005a31696..3d8cae8e74d0 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -526,8 +526,16 @@ static void UnsetChldSignalHandler() {
// Calls POSIX setgroups() using the int[] object as an argument.
// A nullptr argument is tolerated.
-static void SetGids(JNIEnv* env, jintArray managed_gids, fail_fn_t fail_fn) {
+static void SetGids(JNIEnv* env, jintArray managed_gids, jboolean is_child_zygote,
+ fail_fn_t fail_fn) {
if (managed_gids == nullptr) {
+ if (is_child_zygote) {
+ // For child zygotes like webview and app zygote, we want to clear out
+ // any supplemental groups the parent zygote had.
+ if (setgroups(0, NULL) == -1) {
+ fail_fn(CREATE_ERROR("Failed to remove supplementary groups for child zygote"));
+ }
+ }
return;
}
@@ -1665,7 +1673,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- SetGids(env, gids, fail_fn);
+ SetGids(env, gids, is_child_zygote, fail_fn);
SetRLimits(env, rlimits, fail_fn);
if (need_pre_initialize_native_bridge) {
diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto
index 23fcf6ebc2cc..787074ba494e 100644
--- a/core/proto/android/server/connectivity/data_stall_event.proto
+++ b/core/proto/android/server/connectivity/data_stall_event.proto
@@ -32,6 +32,7 @@ enum ApBand {
AP_BAND_UNKNOWN = 0;
AP_BAND_2GHZ = 1;
AP_BAND_5GHZ = 2;
+ AP_BAND_6GHZ = 3;
}
// Refer to definition in TelephonyManager.java.
diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
index e1af9622adb3..9f0ff591a506 100644
--- a/core/proto/android/stats/mediametrics/mediametrics.proto
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -131,7 +131,7 @@ message AudioTrackData {
* Logged from:
* frameworks/av/media/libstagefright/MediaCodec.cpp
* frameworks/av/services/mediaanalytics/statsd_codec.cpp
- * Next Tag: 21
+ * Next Tag: 26
*/
message CodecData {
optional string codec = 1;
@@ -156,6 +156,9 @@ message CodecData {
optional int64 latency_unknown = 20;
optional int32 queue_input_buffer_error = 21;
optional int32 queue_secure_input_buffer_error = 22;
+ optional string bitrate_mode = 23;
+ optional int32 bitrate = 24;
+ optional int64 lifetime_millis = 25;
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fd8460f9c478..464a47002b17 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3656,7 +3656,8 @@
<p>The package installer v2 APIs are still a work in progress and we're
currently validating they work in all scenarios.
<p>Not for use by third-party applications.
- TODO(b/152310230): remove this permission once the APIs are confirmed to be sufficient.
+ TODO(b/152310230): use this permission to protect only Incremental installations
+ once the APIs are confirmed to be sufficient.
@hide
-->
<permission android:name="com.android.permission.USE_INSTALLER_V2"
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 65872f43f46e..afeceb775524 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1309,7 +1309,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Εντοπίστηκε αναλογικό αξεσουάρ ήχου"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Η συνδεδεμένη συσκευή δεν είναι συμβατή με αυτό το τηλέφωνο. Πατήστε για να μάθετε περισσότερα."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Συνδέθηκε ο εντοπισμός σφαλμάτων USB"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Απενεργοποιήστε τον εντοπισμό/διόρθ. σφαλμάτων USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Πατήστε για απενεργοποίηση εντοπισμού/διόρθ. σφαλμάτων USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Συνδέθηκε ο ασύρματος εντοπισμός σφαλμάτων"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Πατήστε, για να απενεργοποιήσετε τον ασύρματο εντοπισμό σφαλμάτων"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 36380b822864..31abd8235bf4 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -202,10 +202,8 @@
<string name="printing_disabled_by" msgid="3517499806528864633">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎Printing disabled by ‎‏‎‎‏‏‎<xliff:g id="OWNER_APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
<string name="personal_apps_suspension_title" msgid="7561416677884286600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎Turn on your work profile‎‏‎‎‏‎"</string>
<string name="personal_apps_suspension_text" msgid="6115455688932935597">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎Your personal apps are blocked until you turn on your work profile‎‏‎‎‏‎"</string>
- <!-- no translation found for personal_apps_suspension_soon_text (8123898693479590) -->
- <skip />
- <!-- no translation found for personal_apps_suspended_turn_profile_on (2758012869627513689) -->
- <skip />
+ <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎‎Personal apps will be blocked on ‎‏‎‎‏‏‎<xliff:g id="DATE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ at ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎. Your IT admin doesn’t allow your work profile to stay off for more than ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%3$d</xliff:g>‎‏‎‎‏‏‏‎ days.‎‏‎‎‏‎"</string>
+ <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎Turn on‎‏‎‎‏‎"</string>
<string name="me" msgid="6207584824693813140">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‎Me‎‏‎‎‏‎"</string>
<string name="power_dialog" product="tablet" msgid="8333207765671417261">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎Tablet options‎‏‎‎‏‎"</string>
<string name="power_dialog" product="tv" msgid="7792839006640933763">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎Android TV options‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 91a8d0585709..bb06173918d7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -2032,7 +2032,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ficheiros</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + <xliff:g id="COUNT_1">%d</xliff:g> ficheiro</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Non hai persoas recomendadas coas que compartir contido"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Non hai recomendacións de persoas coas que compartir contido"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de aplicacións"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta aplicación non está autorizada a realizar gravacións, pero pode capturar audio a través deste dispositivo USB."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Inicio"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 3fe076695642..5161fdc65446 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1230,7 +1230,7 @@
<string name="volume_unknown" msgid="4041914008166576293">"កម្រិត​សំឡេង"</string>
<string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"កម្រិត​សំឡេង​ប៊្លូធូស"</string>
<string name="volume_icon_description_ringer" msgid="2187800636867423459">"កម្រិត​សំឡេង​រោទ៍"</string>
- <string name="volume_icon_description_incall" msgid="4491255105381227919">"កម្រិត​សំឡេង​ហៅ"</string>
+ <string name="volume_icon_description_incall" msgid="4491255105381227919">"កម្រិត​សំឡេង​ហៅទូរសព្ទ"</string>
<string name="volume_icon_description_media" msgid="4997633254078171233">"កម្រិត​សំឡេង​មេឌៀ"</string>
<string name="volume_icon_description_notification" msgid="579091344110747279">"កម្រិត​សំឡេង​ការ​ជូន​ដំណឹង"</string>
<string name="ringtone_default" msgid="9118299121288174597">"សំឡេង​រោទ៍​លំនាំដើម"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index bc896d61a43b..25aa3b5eaf94 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1308,7 +1308,7 @@
<string name="usb_power_notification_message" msgid="7284765627437897702">"ಸಂಪರ್ಕಗೊಂಡಿರುವ ಸಾಧನವನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ. ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ಅನ್‌ಲಾಗ್ ಆಡಿಯೋ ಪರಿಕರ ಪತ್ತೆಯಾಗಿದೆ"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ಲಗತ್ತಿಸಲಾದ ಸಾಧನವು ಈ ಫೋನಿನೊಂದಿಗೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="adb_active_notification_title" msgid="408390247354560331">"USB ಡೀಬಗಿಂಗ್‌‌ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <string name="adb_active_notification_title" msgid="408390247354560331">"USB ಡೀಬಗ್‌ ಮಾಡುವಿಕೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="adb_active_notification_message" msgid="5617264033476778211">"USB ಡೀಬಗ್‌ ಮಾಡುವಿಕೆಯನ್ನು ಆಫ್‌ ಮಾಡಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB ಡೀಬಗ್‌ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಆಯ್ಕೆ ಮಾಡಿ."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ವೈರ್‌ಲೆಸ್ ಡೀಬಗ್‌ ಮಾಡುವಿಕೆಯನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
@@ -2032,7 +2032,7 @@
<item quantity="one"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ಫೈಲ್‌ಗಳು</item>
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ಫೈಲ್‌ಗಳು</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ಹಂಚಿಕೊಳ್ಳಲು, ಯಾವುದೇ ಶಿಫಾರಸು ಮಾಡಲಾದ ಜನರಿಲ್ಲ"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ಹಂಚಿಕೊಳ್ಳಲು ಶಿಫಾರಸು ಮಾಡಲಾದವರು ಯಾರೂ ಇಲ್ಲ"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"ಆ್ಯಪ್‌ಗಳ ಪಟ್ಟಿ"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"ಈ ಆ್ಯಪ್‌ಗೆ ರೆಕಾರ್ಡ್ ಅನುಮತಿಯನ್ನು ನೀಡಲಾಗಿಲ್ಲ, ಆದರೆ ಈ USB ಸಾಧನದ ಮೂಲಕ ಆಡಿಯೊವನ್ನು ಸೆರೆಹಿಡಿಯಬಲ್ಲದು."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"ಹೋಮ್"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index a7e6f99ba8fd..a5a0f9e587ec 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -2032,7 +2032,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> फाइल</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + <xliff:g id="COUNT_1">%d</xliff:g> फाइल</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"शेअर करण्यासाठी कोणतीही शिफारस केलेले लोक नाहीत"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"शेअर करण्यासाठी शिफारस केलेल्या कोणत्याही व्यक्ती नाहीत"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"अ‍ॅप्स सूची"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"या अ‍ॅपला रेकॉर्ड करण्याची परवानगी दिली गेली नाही पण हे USB डिव्हाइस वापरून ऑडिओ कॅप्चर केला जाऊ शकतो."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"होम"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index cc8137cc244c..64302dab7ce9 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1912,7 +1912,7 @@
<string name="demo_starting_message" msgid="6577581216125805905">"Demo starten…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Apparaat resetten…"</string>
<string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> uitgeschakeld"</string>
- <string name="conference_call" msgid="5731633152336490471">"Telefonische vergadering"</string>
+ <string name="conference_call" msgid="5731633152336490471">"Conferencecall"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"Knopinfo"</string>
<string name="app_category_game" msgid="4534216074910244790">"Games"</string>
<string name="app_category_audio" msgid="8296029904794676222">"Muziek en audio"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f803f02e92bc..4becdaa16ef3 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -60,7 +60,7 @@
<string name="ClirMmi" msgid="4702929460236547156">"ଆଉଟଗୋଇଙ୍ଗ୍ କଲର୍ ଆଇଡି"</string>
<string name="ColpMmi" msgid="4736462893284419302">"ସଂଯୁକ୍ତ ଲାଇନ୍ ID"</string>
<string name="ColrMmi" msgid="5889782479745764278">"ସଂଯୁକ୍ତ ଲାଇନ୍ ID କଟକଣା"</string>
- <string name="CfMmi" msgid="8390012691099787178">"କଲ୍‌ ଫରୱାର୍ଡିଙ୍ଗ"</string>
+ <string name="CfMmi" msgid="8390012691099787178">"କଲ୍‌ ଫରୱାର୍ଡିଂ"</string>
<string name="CwMmi" msgid="3164609577675404761">"କଲ୍‌ ଅପେକ୍ଷାରତ"</string>
<string name="BaMmi" msgid="7205614070543372167">"କଲ୍‌ ବ୍ୟାରିଙ୍ଗ୍"</string>
<string name="PwdMmi" msgid="3360991257288638281">"ପାସ୍‌ୱର୍ଡ ପରିବର୍ତ୍ତନ"</string>
@@ -88,7 +88,7 @@
<string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"ଜରୁରୀକାଳୀନ କଲ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"ୱାଇ-ଫାଇ ସାହାଯ୍ୟରେ ଜରୁରୀକାଳୀନ କଲ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="notification_channel_network_alert" msgid="4788053066033851841">"ଆଲର୍ଟ"</string>
- <string name="notification_channel_call_forward" msgid="8230490317314272406">"କଲ୍‌ ଫରୱାର୍ଡିଙ୍ଗ"</string>
+ <string name="notification_channel_call_forward" msgid="8230490317314272406">"କଲ୍‌ ଫରୱାର୍ଡିଂ"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"ଜରୁରୀକାଳୀନ କଲବ୍ୟାକ୍‍ ମୋଡ୍‍"</string>
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"ମୋବାଇଲ୍‍ ଡାଟା ଷ୍ଟାଟସ୍‌"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS ମେସେଜ୍‌"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c962256e477c..f42b248f4670 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1830,30 +1830,13 @@
<!-- @hide no longer used, kept to preserve padding -->
<attr name="allowAutoRevokePermissionsExemption" format="boolean" />
- <!-- Declare the app's tolerance to having its permissions automatically revoked when unused for an extended
- period of time -->
+ <!-- No longer used. Declaring this does nothing -->
<attr name="autoRevokePermissions">
- <!-- App supports re-requesting its permissions if revoked.
- Revoking app's permissions doesn't cause user experience issues, aside from a repeated permission request.
-
- Permissions may be automatically revoked from an app if unused. The app must check and possibly request the
- necessary permission on each permission-gated call-->
+ <!-- No longer used -->
<enum name="allowed" value="0" />
- <!-- App may experience degraded functionality when its previously-granted permissions are revoked.
- Revoking app's permissions may cause user experience issues, that are not critical to the user.
-
- Apps with this declaration can choose to request an exemption from auto revoke from user by starting
- an activity with {@code Intent.ACTION_AUTO_REVOKE_PERMISSIONS}. -->
+ <!-- No longer used -->
<enum name="discouraged" value="1" />
- <!-- User may experience severe consequences if this app's permissions are revoked unexpectedly.
-
- E.g. app may fail to do a user-critical background job that may likely impact user's
- safety/security/device accessibility.
-
- This declaration may cause an additional review when publishing your app.
-
- Apps with this declaration are exempt from auto revoke by default, though the user has the final say
- in both revoking the permissions as well as the app's auto revoke exemption status. -->
+ <!-- No longer used -->
<enum name="disallowed" value="2" />
</attr>
</declare-styleable>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dc21e878d132..a1c2450be153 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1613,15 +1613,15 @@
<string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string>
<!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
<string name="face_error_canceled">Face operation canceled.</string>
- <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=54] -->
+ <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=68] -->
<string name="face_error_user_canceled">Face unlock canceled by user.</string>
<!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
<string name="face_error_lockout">Too many attempts. Try again later.</string>
- <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=71] -->
+ <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=77] -->
<string name="face_error_lockout_permanent">Too many attempts. Face unlock disabled.</string>
<!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
<string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string>
- <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=52] -->
+ <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=59] -->
<string name="face_error_not_enrolled">You haven\u2019t set up face unlock.</string>
<!-- Generic error message shown when the app requests face unlock on a device without a sensor. [CHAR LIMIT=61] -->
<string name="face_error_hw_not_present">Face unlock is not supported on this device.</string>
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
new file mode 100644
index 000000000000..e74f30ee10a4
--- /dev/null
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -0,0 +1,42 @@
+//
+// 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.
+//
+
+android_test {
+ name: "FrameworksCorePackageInstallerSessionsTests",
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "frameworks-base-testutils",
+ "platform-test-annotations",
+ "testng",
+ "truth-prebuilt",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "framework",
+ "framework-res",
+ ],
+
+ platform_apis: true,
+ sdk_version: "core_platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/PackageInstallerSessions/AndroidManifest.xml b/core/tests/PackageInstallerSessions/AndroidManifest.xml
new file mode 100644
index 000000000000..5b22d2b4f3e3
--- /dev/null
+++ b/core/tests/PackageInstallerSessions/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.package_installer_sessions"
+ >
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.coretests.package_installer_sessions"/>
+</manifest>
diff --git a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
new file mode 100644
index 000000000000..494c92a8aa3f
--- /dev/null
+++ b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
@@ -0,0 +1,188 @@
+/*
+ * 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.content.pm.PackageInstaller.SessionParams
+import android.platform.test.annotations.Presubmit
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.LargeTest
+import com.android.compatibility.common.util.ShellIdentityUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.testng.Assert.assertThrows
+import kotlin.random.Random
+
+/**
+ * For verifying public [PackageInstaller] session APIs. This differs from
+ * [com.android.server.pm.PackageInstallerSessionTest] in services because that mocks the session,
+ * whereas this test uses the installer on device.
+ */
+@Presubmit
+class PackageSessionTests {
+
+ companion object {
+ /**
+ * Permissions marked "hardRestricted" or "softRestricted" in core/res/AndroidManifest.xml.
+ */
+ private val RESTRICTED_PERMISSIONS = listOf(
+ "android.permission.SEND_SMS",
+ "android.permission.RECEIVE_SMS",
+ "android.permission.READ_SMS",
+ "android.permission.RECEIVE_WAP_PUSH",
+ "android.permission.RECEIVE_MMS",
+ "android.permission.READ_CELL_BROADCASTS",
+ "android.permission.ACCESS_BACKGROUND_LOCATION",
+ "android.permission.READ_CALL_LOG",
+ "android.permission.WRITE_CALL_LOG",
+ "android.permission.PROCESS_OUTGOING_CALLS"
+ )
+ }
+
+ private val context: Context = InstrumentationRegistry.getContext()
+
+ private val installer = context.packageManager.packageInstaller
+
+ @Before
+ @After
+ fun abandonAllSessions() {
+ installer.mySessions.asSequence()
+ .map { it.sessionId }
+ .forEach {
+ try {
+ installer.abandonSession(it)
+ } catch (ignored: Exception) {
+ // Querying for sessions checks by calling package name, but abandoning
+ // checks by UID, which won't match if this test failed to clean up
+ // on a previous install + run + uninstall, so ignore these failures.
+ }
+ }
+ }
+
+ @Test
+ fun truncateAppLabel() {
+ val longLabel = invalidAppLabel()
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setAppLabel(longLabel)
+ }
+
+ createSession(params) {
+ assertThat(installer.getSessionInfo(it)?.appLabel)
+ .isEqualTo(longLabel.take(PackageItemInfo.MAX_SAFE_LABEL_LENGTH))
+ }
+ }
+
+ @Test
+ fun removeInvalidAppPackageName() {
+ val longName = invalidPackageName()
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setAppPackageName(longName)
+ }
+
+ createSession(params) {
+ assertThat(installer.getSessionInfo(it)?.appPackageName)
+ .isEqualTo(null)
+ }
+ }
+
+ @Test
+ fun removeInvalidInstallerPackageName() {
+ val longName = invalidPackageName()
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setInstallerPackageName(longName)
+ }
+
+ createSession(params) {
+ // If a custom installer name is dropped, it defaults to the caller
+ assertThat(installer.getSessionInfo(it)?.installerPackageName)
+ .isEqualTo(context.packageName)
+ }
+ }
+
+ @Test
+ fun truncateWhitelistPermissions() {
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setWhitelistedRestrictedPermissions(invalidPermissions())
+ }
+
+ createSession(params) {
+ assertThat(installer.getSessionInfo(it)?.whitelistedRestrictedPermissions!!)
+ .containsExactlyElementsIn(RESTRICTED_PERMISSIONS)
+ }
+ }
+
+ @LargeTest
+ @Test
+ fun allocateMaxSessionsWithPermission() {
+ ShellIdentityUtils.invokeWithShellPermissions {
+ repeat(1024) { createDummySession() }
+ assertThrows(IllegalStateException::class.java) { createDummySession() }
+ }
+ }
+
+ @LargeTest
+ @Test
+ fun allocateMaxSessionsNoPermission() {
+ repeat(50) { createDummySession() }
+ assertThrows(IllegalStateException::class.java) { createDummySession() }
+ }
+
+ private fun createDummySession() {
+ installer.createSession(SessionParams(SessionParams.MODE_FULL_INSTALL)
+ .apply {
+ setAppPackageName(invalidPackageName())
+ setAppLabel(invalidAppLabel())
+ setWhitelistedRestrictedPermissions(invalidPermissions())
+ })
+ }
+
+ private fun invalidPackageName(maxLength: Int = SessionParams.MAX_PACKAGE_NAME_LENGTH): String {
+ return (0 until (maxLength + 10))
+ .asSequence()
+ .mapIndexed { index, _ ->
+ // A package name needs at least one separator
+ if (index == 2) {
+ '.'
+ } else {
+ Random.nextInt('z' - 'a').toChar() + 'a'.toInt()
+ }
+ }
+ .joinToString(separator = "")
+ }
+
+ private fun invalidAppLabel() = (0 until PackageItemInfo.MAX_SAFE_LABEL_LENGTH + 10)
+ .asSequence()
+ .map { Random.nextInt(Char.MAX_VALUE.toInt()).toChar() }
+ .joinToString(separator = "")
+
+ private fun invalidPermissions() = RESTRICTED_PERMISSIONS.toMutableSet()
+ .apply {
+ // Add some invalid permission names
+ repeat(10) { add(invalidPackageName(300)) }
+ }
+
+ private fun createSession(params: SessionParams, block: (Int) -> Unit = {}) {
+ val sessionId = installer.createSession(params)
+ try {
+ block(sessionId)
+ } finally {
+ installer.abandonSession(sessionId)
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
index a8ca6f048a11..b329e55b569f 100644
--- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -126,6 +126,32 @@ public class AutofillIdTest {
}
@Test
+ public void testVirtual_Long_withoutSession() {
+ final AutofillId id = new AutofillId(new AutofillId(42), 108L, 666);
+ final AutofillId idWithoutSession = AutofillId.withoutSession(id);
+ assertThat(idWithoutSession.getViewId()).isEqualTo(42);
+ assertThat(idWithoutSession.isVirtualLong()).isTrue();
+ assertThat(idWithoutSession.isVirtualInt()).isFalse();
+ assertThat(idWithoutSession.isNonVirtual()).isFalse();
+ assertThat(idWithoutSession.getVirtualChildLongId()).isEqualTo(108L);
+ assertThat(idWithoutSession.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+ assertThat(idWithoutSession.getSessionId()).isEqualTo(NO_SESSION);
+ }
+
+ @Test
+ public void testVirtual_Int_withoutSession() {
+ final AutofillId id = new AutofillId(42, 108);
+ final AutofillId idWithoutSession = AutofillId.withoutSession(id);
+ assertThat(idWithoutSession.getViewId()).isEqualTo(42);
+ assertThat(idWithoutSession.isVirtualLong()).isFalse();
+ assertThat(idWithoutSession.isVirtualInt()).isTrue();
+ assertThat(idWithoutSession.isNonVirtual()).isFalse();
+ assertThat(idWithoutSession.getVirtualChildIntId()).isEqualTo(108);
+ assertThat(idWithoutSession.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+ assertThat(idWithoutSession.getSessionId()).isEqualTo(NO_SESSION);
+ }
+
+ @Test
public void testSetResetSession() {
final AutofillId id = new AutofillId(42);
assertNonVirtual(id, 42, NO_SESSION);
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 7cd2f3b4c2ab..a4f206586625 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -363,6 +363,16 @@ public class HdmiAudioSystemClientTest {
public boolean isHdmiCecVolumeControlEnabled() {
return true;
}
+
+ @Override
+ public void addHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ }
+
+ @Override
+ public void removeHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ }
}
}
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index fe33cd80f735..4b8173732b4d 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -29,11 +29,12 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.WindowManager;
@@ -91,8 +92,7 @@ public final class ScreenshotHelperTest {
@Test
public void testProvidedImageScreenshot() {
mScreenshotHelper.provideScreenshot(
- Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), new Rect(),
- Insets.of(0, 0, 0, 0), 1,
+ new Bundle(), new Rect(), Insets.of(0, 0, 0, 0), 1, 1, new ComponentName("", ""),
WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null);
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e00813ce2aaf..9b503eba5c68 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -378,6 +378,9 @@ applications that come with the platform
<permission name="android.permission.SET_WALLPAPER" />
<permission name="android.permission.SET_WALLPAPER_COMPONENT" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+ <!-- Permissions required for Incremental CTS tests -->
+ <permission name="com.android.permission.USE_INSTALLER_V2"/>
+ <permission name="android.permission.LOADER_USAGE_STATS"/>
<!-- Permission required to test system only camera devices. -->
<permission name="android.permission.SYSTEM_CAMERA" />
<!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
@@ -418,6 +421,8 @@ applications that come with the platform
<permission name="android.permission.TV_INPUT_HARDWARE" />
<!-- Permission required for CTS test - PrivilegedLocationPermissionTest -->
<permission name="android.permission.LOCATION_HARDWARE" />
+ <!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
+ <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/keyboards/Vendor_045e_Product_0b12.kl b/data/keyboards/Vendor_045e_Product_0b12.kl
new file mode 100644
index 000000000000..0b44c7434af2
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_0b12.kl
@@ -0,0 +1,59 @@
+# 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.
+
+#
+# XBox USB Controller
+#
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+
+# The branded "X" button in the center of the controller
+key 316 BUTTON_MODE
+
+# Three parallel horizontal lines (hamburger menu)
+key 315 BUTTON_START
+
+#Button below the "X" button
+key 167 MEDIA_RECORD
+
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index c652628eb425..590def4d4ced 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityManager;
import android.content.Context;
import android.hardware.cas.V1_0.HidlCasPluginDescriptor;
import android.hardware.cas.V1_0.ICas;
@@ -43,6 +44,8 @@ import android.os.RemoteException;
import android.util.Log;
import android.util.Singleton;
+import com.android.internal.util.FrameworkStatsLog;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -122,6 +125,7 @@ public final class MediaCas implements AutoCloseable {
private String mTvInputServiceSessionId;
private int mClientId;
private int mCasSystemId;
+ private int mUserId;
private TunerResourceManager mTunerResourceManager = null;
private final Map<Session, Integer> mSessionMap = new HashMap<>();
@@ -673,6 +677,8 @@ public final class MediaCas implements AutoCloseable {
*/
public MediaCas(int CA_system_id) throws UnsupportedCasException {
try {
+ mCasSystemId = CA_system_id;
+ mUserId = ActivityManager.getCurrentUser();
IMediaCasService service = getService();
android.hardware.cas.V1_2.IMediaCasService serviceV12 =
android.hardware.cas.V1_2.IMediaCasService.castFrom(service);
@@ -721,7 +727,6 @@ public final class MediaCas implements AutoCloseable {
this(casSystemId);
Objects.requireNonNull(context, "context must not be null");
- mCasSystemId = casSystemId;
mTunerResourceManager = (TunerResourceManager)
context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
if (mTunerResourceManager != null) {
@@ -925,10 +930,18 @@ public final class MediaCas implements AutoCloseable {
mICas.openSession(cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
addSessionToResourceMap(cb.mSession, sessionResourceHandle);
+ Log.d(TAG, "Write Stats Log for succeed to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
+ Log.d(TAG, "Write Stats Log for fail to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__FAILED);
return null;
}
@@ -964,10 +977,18 @@ public final class MediaCas implements AutoCloseable {
mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
addSessionToResourceMap(cb.mSession, sessionResourceHandle);
+ Log.d(TAG, "Write Stats Log for succeed to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
+ Log.d(TAG, "Write Stats Log for fail to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__FAILED);
return null;
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 981bf7af9f25..05c6e3ad9392 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -137,7 +137,7 @@ public abstract class MediaRoute2ProviderService extends Service {
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
private MediaRoute2ProviderServiceStub mStub;
private IMediaRoute2ProviderServiceCallback mRemoteCallback;
- private MediaRoute2ProviderInfo mProviderInfo;
+ private volatile MediaRoute2ProviderInfo mProviderInfo;
@GuardedBy("mSessionLock")
private ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
@@ -167,8 +167,8 @@ public abstract class MediaRoute2ProviderService extends Service {
/**
* Called when a volume setting is requested on a route of the provider
*
- * @param requestId the id of this request
- * @param routeId the id of the route
+ * @param requestId the ID of this request
+ * @param routeId the ID of the route
* @param volume the target volume
* @see MediaRoute2Info.Builder#setVolume(int)
*/
@@ -178,8 +178,8 @@ public abstract class MediaRoute2ProviderService extends Service {
* Called when {@link MediaRouter2.RoutingController#setVolume(int)} is called on
* a routing session of the provider
*
- * @param requestId the id of this request
- * @param sessionId the id of the routing session
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the routing session
* @param volume the target volume
* @see RoutingSessionInfo.Builder#setVolume(int)
*/
@@ -188,7 +188,7 @@ public abstract class MediaRoute2ProviderService extends Service {
/**
* Gets information of the session with the given id.
*
- * @param sessionId id of the session
+ * @param sessionId the ID of the session
* @return information of the session with the given id.
* null if the session is released or ID is not valid.
*/
@@ -218,7 +218,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* If this session is created without any creation request, use {@link #REQUEST_ID_NONE}
* as the request ID.
*
- * @param requestId id of the previous request to create this session provided in
+ * @param requestId the ID of the previous request to create this session provided in
* {@link #onCreateSession(long, String, String, Bundle)}. Can be
* {@link #REQUEST_ID_NONE} if this session is created without any request.
* @param sessionInfo information of the new session.
@@ -237,18 +237,15 @@ public abstract class MediaRoute2ProviderService extends Service {
return;
}
mSessionInfo.put(sessionInfo.getId(), sessionInfo);
- }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- // TODO(b/157873487): Calling binder calls in multiple thread may cause timing issue.
- // Consider to change implementations to avoid the problems.
- // For example, post binder calls, always send all sessions at once, etc.
- mRemoteCallback.notifySessionCreated(requestId, sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session created.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionCreated(requestId, sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session created.");
+ }
}
}
@@ -267,22 +264,22 @@ public abstract class MediaRoute2ProviderService extends Service {
Log.w(TAG, "Ignoring unknown session info.");
return;
}
- }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- mRemoteCallback.notifySessionUpdated(sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionUpdated(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session info changed.");
+ }
}
}
/**
* Notifies that the session is released.
*
- * @param sessionId id of the released session.
+ * @param sessionId the ID of the released session.
* @see #onReleaseSession(long, String)
*/
public final void notifySessionReleased(@NonNull String sessionId) {
@@ -292,20 +289,20 @@ public abstract class MediaRoute2ProviderService extends Service {
RoutingSessionInfo sessionInfo;
synchronized (mSessionLock) {
sessionInfo = mSessionInfo.remove(sessionId);
- }
- if (sessionInfo == null) {
- Log.w(TAG, "Ignoring unknown session info.");
- return;
- }
+ if (sessionInfo == null) {
+ Log.w(TAG, "Ignoring unknown session info.");
+ return;
+ }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- mRemoteCallback.notifySessionReleased(sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionReleased(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session info changed.");
+ }
}
}
@@ -348,9 +345,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* If you can't create the session or want to reject the request, call
* {@link #notifyRequestFailed(long, int)} with the given {@code requestId}.
*
- * @param requestId the id of this request
+ * @param requestId the ID of this request
* @param packageName the package name of the application that selected the route
- * @param routeId the id of the route initially being connected
+ * @param routeId the ID of the route initially being connected
* @param sessionHints an optional bundle of app-specific arguments sent by
* {@link MediaRouter2}, or null if none. The contents of this bundle
* may affect the result of session creation.
@@ -372,8 +369,8 @@ public abstract class MediaRoute2ProviderService extends Service {
* Note: Calling {@link #notifySessionReleased(String)} will <em>NOT</em> trigger
* this method to be called.
*
- * @param requestId the id of this request
- * @param sessionId id of the session being released.
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session being released.
* @see #notifySessionReleased(String)
* @see #getSessionInfo(String)
*/
@@ -384,9 +381,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onSelectRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -396,9 +393,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onDeselectRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -408,9 +405,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onTransferToRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -475,13 +472,39 @@ public abstract class MediaRoute2ProviderService extends Service {
final class MediaRoute2ProviderServiceStub extends IMediaRoute2ProviderService.Stub {
MediaRoute2ProviderServiceStub() { }
- boolean checkCallerisSystem() {
+ private boolean checkCallerIsSystem() {
return Binder.getCallingUid() == Process.SYSTEM_UID;
}
+ private boolean checkSessionIdIsValid(String sessionId, String description) {
+ if (TextUtils.isEmpty(sessionId)) {
+ Log.w(TAG, description + ": Ignoring empty sessionId from system service.");
+ return false;
+ }
+ if (getSessionInfo(sessionId) == null) {
+ Log.w(TAG, description + ": Ignoring unknown session from system service. "
+ + "sessionId=" + sessionId);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkRouteIdIsValid(String routeId, String description) {
+ if (TextUtils.isEmpty(routeId)) {
+ Log.w(TAG, description + ": Ignoring empty routeId from system service.");
+ return false;
+ }
+ if (mProviderInfo == null || mProviderInfo.getRoute(routeId) == null) {
+ Log.w(TAG, description + ": Ignoring unknown route from system service. "
+ + "routeId=" + routeId);
+ return false;
+ }
+ return true;
+ }
+
@Override
public void setCallback(IMediaRoute2ProviderServiceCallback callback) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::setCallback,
@@ -490,7 +513,7 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(
@@ -500,7 +523,10 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void setRouteVolume(long requestId, String routeId, int volume) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkRouteIdIsValid(routeId, "setRouteVolume")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
@@ -510,7 +536,10 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void requestCreateSession(long requestId, String packageName, String routeId,
@Nullable Bundle requestCreateSession) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkRouteIdIsValid(routeId, "requestCreateSession")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
@@ -518,14 +547,13 @@ public abstract class MediaRoute2ProviderService extends Service {
requestCreateSession));
}
- //TODO(b/157873546): Ignore requests with unknown session ID. -> For all similar commands.
@Override
public void selectRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "selectRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "selectRoute")
+ || !checkRouteIdIsValid(routeId, "selectRoute")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
@@ -534,11 +562,11 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void deselectRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "deselectRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "deselectRoute")
+ || !checkRouteIdIsValid(routeId, "deselectRoute")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
@@ -547,11 +575,11 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void transferToRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "transferToRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "transferToRoute")
+ || !checkRouteIdIsValid(routeId, "transferToRoute")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
@@ -560,7 +588,10 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void setSessionVolume(long requestId, String sessionId, int volume) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkSessionIdIsValid(sessionId, "setSessionVolume")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
@@ -569,11 +600,10 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void releaseSession(long requestId, String sessionId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index c4d27eca02f8..e719b2a04720 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -90,6 +90,17 @@ public final class MediaProjectionManager {
* projection is stopped. This allows for user controls to be displayed on top of the screen
* being captured.
*
+ * <p>
+ * Apps targeting SDK version {@link android.os.Build.VERSION_CODES#Q} or later should specify
+ * the foreground service type using the attribute {@link android.R.attr#foregroundServiceType}
+ * in the service element of the app's manifest file.
+ * The {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION} attribute
+ * should be specified.
+ * </p>
+ *
+ * @see <a href="https://developer.android.com/preview/privacy/foreground-service-types">
+ * Foregroud Service Types</a>
+ *
* @param resultCode The result code from {@link android.app.Activity#onActivityResult(int,
* int, android.content.Intent)}
* @param resultData The resulting data from {@link android.app.Activity#onActivityResult(int,
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 68071b0b0fe3..bb00bb3b8d56 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -20,12 +20,16 @@ import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -72,9 +76,15 @@ public class DvrPlayback implements AutoCloseable {
*/
public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+ private static final String TAG = "TvTunerPlayback";
+
private long mNativeContext;
private OnPlaybackStatusChangedListener mListener;
private Executor mExecutor;
+ private int mUserId;
+ private static int sInstantId = 0;
+ private int mSegmentId = 0;
+ private int mUnderflow;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
@@ -88,6 +98,9 @@ public class DvrPlayback implements AutoCloseable {
private native long nativeRead(byte[] bytes, long offset, long size);
private DvrPlayback() {
+ mUserId = ActivityManager.getCurrentUser();
+ mSegmentId = (sInstantId & 0x0000ffff) << 16;
+ sInstantId++;
}
/** @hide */
@@ -98,6 +111,9 @@ public class DvrPlayback implements AutoCloseable {
}
private void onPlaybackStatusChanged(int status) {
+ if (status == PLAYBACK_STATUS_EMPTY) {
+ mUnderflow++;
+ }
if (mExecutor != null && mListener != null) {
mExecutor.execute(() -> mListener.onPlaybackStatusChanged(status));
}
@@ -154,6 +170,13 @@ public class DvrPlayback implements AutoCloseable {
*/
@Result
public int start() {
+ mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
+ mUnderflow = 0;
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
return nativeStartDvr();
}
@@ -167,6 +190,11 @@ public class DvrPlayback implements AutoCloseable {
*/
@Result
public int stop() {
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mUnderflow);
return nativeStopDvr();
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 198bd0f4e78e..887116725961 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,14 +19,19 @@ package android.media.tv.tuner.dvr;
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.util.concurrent.Executor;
+
/**
* Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer.
*
@@ -34,9 +39,14 @@ import java.util.concurrent.Executor;
*/
@SystemApi
public class DvrRecorder implements AutoCloseable {
+ private static final String TAG = "TvTunerRecord";
private long mNativeContext;
private OnRecordStatusChangedListener mListener;
private Executor mExecutor;
+ private int mUserId;
+ private static int sInstantId = 0;
+ private int mSegmentId = 0;
+ private int mOverflow;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
@@ -50,6 +60,9 @@ public class DvrRecorder implements AutoCloseable {
private native long nativeWrite(byte[] bytes, long offset, long size);
private DvrRecorder() {
+ mUserId = ActivityManager.getCurrentUser();
+ mSegmentId = (sInstantId & 0x0000ffff) << 16;
+ sInstantId++;
}
/** @hide */
@@ -60,6 +73,9 @@ public class DvrRecorder implements AutoCloseable {
}
private void onRecordStatusChanged(int status) {
+ if (status == Filter.STATUS_OVERFLOW) {
+ mOverflow++;
+ }
if (mExecutor != null && mListener != null) {
mExecutor.execute(() -> mListener.onRecordStatusChanged(status));
}
@@ -112,6 +128,13 @@ public class DvrRecorder implements AutoCloseable {
*/
@Result
public int start() {
+ mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
+ mOverflow = 0;
+ Log.d(TAG, "Write Stats Log for Record.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
return nativeStartDvr();
}
@@ -124,6 +147,11 @@ public class DvrRecorder implements AutoCloseable {
*/
@Result
public int stop() {
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow);
return nativeStopDvr();
}
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 2a8a39a1fe1a..32b33a758535 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -32,6 +32,7 @@ android_library {
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",
+ "car-ui-lib",
"android.car.userlib",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
@@ -95,6 +96,7 @@ android_library {
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
+ "car-ui-lib",
"SystemUI-tags",
"SystemUI-proto",
"metrics-helper-lib",
diff --git a/packages/CarSystemUI/res/drawable/nav_button_background.xml b/packages/CarSystemUI/res/drawable/nav_button_background.xml
deleted file mode 100644
index 376347cdf4a9..000000000000
--- a/packages/CarSystemUI/res/drawable/nav_button_background.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/nav_bar_ripple_background_color">
- <item android:id="@android:id/mask">
- <shape android:shape="rectangle">
- <solid android:color="?android:colorAccent"/>
- <corners android:radius="6dp"/>
- </shape>
- </item>
-</ripple>
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index a8c70989253e..94816f81a4c5 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -79,7 +79,7 @@
android:gravity="bottom"
android:orientation="vertical">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 2a715d0c3494..93174983b116 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -29,9 +29,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:paddingStart="20dp"
+ android:gravity="center"
+ android:layoutDirection="ltr"
android:paddingEnd="20dp"
- android:gravity="center">
+ android:paddingStart="20dp">
<com.android.systemui.car.navigationbar.CarNavigationButton
android:id="@+id/home"
@@ -135,9 +136,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:paddingStart="@dimen/car_keyline_1"
- android:paddingEnd="@dimen/car_keyline_1"
android:gravity="center"
+ android:layoutDirection="ltr"
+ android:paddingEnd="@dimen/car_keyline_1"
+ android:paddingStart="@dimen/car_keyline_1"
android:visibility="gone"
/>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index ca4e76ee104b..a8f115742023 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -27,7 +27,7 @@
android:animateLayoutChanges="true"
android:orientation="vertical">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/car_nav_button_icon_image"
android:layout_height="@dimen/car_navigation_button_icon_height"
android:layout_width="match_parent"
@@ -40,7 +40,7 @@
android:clickable="false"
/>
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/car_nav_button_more_icon"
android:layout_height="wrap_content"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index fd75570e759c..dc9583382921 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -82,7 +82,7 @@
android:gravity="bottom"
android:orientation="vertical">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 60e0d7e430df..cdc29eec21cd 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -27,7 +27,8 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1">
+ android:layout_weight="1"
+ android:layoutDirection="ltr">
<FrameLayout
android:id="@+id/left_hvac_container"
diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
index caf1677234d0..1782d2536035 100644
--- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -29,6 +29,15 @@
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
+ <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the
+ focus here when the user navigates to another window. This is also used to prevent
+ wrap-around which is why it must be first or last in Tab order. -->
+ <com.android.car.ui.FocusParkingView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintTop_toTopOf="parent"/>
+
<View
android:id="@+id/scrim"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 371bebdebc86..7fc69e6d5d8f 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -44,6 +44,6 @@
<style name="NavigationBarButton">
<item name="android:layout_height">96dp</item>
<item name="android:layout_width">96dp</item>
- <item name="android:background">@drawable/nav_button_background</item>
+ <item name="android:background">@*android:drawable/item_background_material</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index ab61b443df97..2dad5f872e73 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -219,6 +219,14 @@ public class CarKeyguardViewController extends OverlayViewController implements
}
@Override
+ public void setOccluded(boolean occluded, boolean animate) {
+ getOverlayViewGlobalStateController().setOccluded(occluded);
+ if (!occluded) {
+ reset(/* hideBouncerWhenShowing= */ false);
+ }
+ }
+
+ @Override
public void onCancelClicked() {
if (mBouncer == null) return;
@@ -315,11 +323,6 @@ public class CarKeyguardViewController extends OverlayViewController implements
}
@Override
- public void setOccluded(boolean occluded, boolean animate) {
- // no-op
- }
-
- @Override
public boolean shouldDisableWindowAnimationsForUnlock() {
return false;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 20fc1bcd6013..0ced4021ce38 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -74,8 +74,10 @@ public class CarNavigationBarView extends LinearLayout {
mDarkIconManager.setShouldLog(true);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
}
- // needs to be clickable so that it will receive ACTION_MOVE events
+ // Needs to be clickable so that it will receive ACTION_MOVE events.
setClickable(true);
+ // Needs to not be focusable so rotary won't highlight the entire nav bar.
+ setFocusable(false);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
index 5e113d6366a1..e7e33a5439f9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
@@ -32,8 +32,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
import java.net.URISyntaxException;
@@ -53,8 +53,8 @@ public class CarNavigationButton extends LinearLayout {
private static final String EXTRA_BUTTON_PACKAGES = "packages";
private Context mContext;
- private AlphaOptimizedImageButton mIcon;
- private AlphaOptimizedImageButton mMoreIcon;
+ private AlphaOptimizedImageView mIcon;
+ private AlphaOptimizedImageView mMoreIcon;
private ImageView mUnseenIcon;
private String mIntent;
private String mLongIntent;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index 3b7b48a77186..d60bc418ece2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -24,6 +24,7 @@ import android.view.ViewGroup;
import androidx.annotation.LayoutRes;
+import com.android.car.ui.FocusParkingView;
import com.android.systemui.R;
import javax.inject.Inject;
@@ -146,6 +147,12 @@ public class NavigationBarViewFactory {
CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
/* root= */ null);
+
+ // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
+ // the user navigates to another window. This is also used to prevent wrap-around which is
+ // why it must be first or last in Tab order.
+ view.addView(new FocusParkingView(mContext));
+
mCachedViewMap.put(type, view);
return mCachedViewMap.get(type);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
index aeb1d39599db..d4f720715a69 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
@@ -24,7 +24,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.FrameLayout;
import com.android.car.notification.R;
import com.android.car.notification.headsup.CarHeadsUpNotificationContainer;
@@ -44,7 +43,7 @@ public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotifica
private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
private final ViewGroup mWindow;
- private final FrameLayout mHeadsUpContentFrame;
+ private final ViewGroup mHeadsUpContentFrame;
@Inject
CarHeadsUpNotificationSystemContainer(Context context,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 30e26578bd73..3969f92c690a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -138,4 +138,11 @@ public class OverlayViewController {
protected boolean shouldShowNavigationBar() {
return false;
}
+
+ /**
+ * Returns {@code true} if this view should be hidden during the occluded state.
+ */
+ protected boolean shouldShowWhenOccluded() {
+ return false;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 70260b0d4cef..8e9410964313 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -24,7 +24,9 @@ import androidx.annotation.VisibleForTesting;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -47,11 +49,16 @@ public class OverlayViewGlobalStateController {
private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
private final CarNavigationBarController mCarNavigationBarController;
+
+ private boolean mIsOccluded;
+
@VisibleForTesting
Map<OverlayViewController, Integer> mZOrderMap;
@VisibleForTesting
SortedMap<Integer, OverlayViewController> mZOrderVisibleSortedMap;
@VisibleForTesting
+ Set<OverlayViewController> mViewsHiddenForOcclusion;
+ @VisibleForTesting
OverlayViewController mHighestZOrder;
@Inject
@@ -63,6 +70,7 @@ public class OverlayViewGlobalStateController {
mCarNavigationBarController = carNavigationBarController;
mZOrderMap = new HashMap<>();
mZOrderVisibleSortedMap = new TreeMap<>();
+ mViewsHiddenForOcclusion = new HashSet<>();
}
/**
@@ -91,6 +99,10 @@ public class OverlayViewGlobalStateController {
*/
public void showView(OverlayViewController viewController, @Nullable Runnable show) {
debugLog();
+ if (mIsOccluded && !viewController.shouldShowWhenOccluded()) {
+ mViewsHiddenForOcclusion.add(viewController);
+ return;
+ }
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(true);
}
@@ -147,6 +159,10 @@ public class OverlayViewGlobalStateController {
*/
public void hideView(OverlayViewController viewController, @Nullable Runnable hide) {
debugLog();
+ if (mIsOccluded && mViewsHiddenForOcclusion.contains(viewController)) {
+ mViewsHiddenForOcclusion.remove(viewController);
+ return;
+ }
if (!viewController.isInflated()) {
Log.d(TAG, "Content cannot be hidden since it isn't inflated: "
+ viewController.getClass().getName());
@@ -240,6 +256,43 @@ public class OverlayViewGlobalStateController {
return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN();
}
+ /**
+ * Set the OverlayViewWindow to be in occluded or unoccluded state. When OverlayViewWindow is
+ * occluded, all views mounted to it that are not configured to be shown during occlusion will
+ * be hidden.
+ */
+ public void setOccluded(boolean occluded) {
+ if (occluded) {
+ // Hide views before setting mIsOccluded to true so the regular hideView logic is used,
+ // not the one used during occlusion.
+ hideViewsForOcclusion();
+ mIsOccluded = true;
+ } else {
+ mIsOccluded = false;
+ // show views after setting mIsOccluded to false so the regular showView logic is used,
+ // not the one used during occlusion.
+ showViewsHiddenForOcclusion();
+ }
+ }
+
+ private void hideViewsForOcclusion() {
+ HashSet<OverlayViewController> viewsCurrentlyShowing = new HashSet<>(
+ mZOrderVisibleSortedMap.values());
+ viewsCurrentlyShowing.forEach(overlayController -> {
+ if (!overlayController.shouldShowWhenOccluded()) {
+ hideView(overlayController, overlayController::hideInternal);
+ mViewsHiddenForOcclusion.add(overlayController);
+ }
+ });
+ }
+
+ private void showViewsHiddenForOcclusion() {
+ mViewsHiddenForOcclusion.forEach(overlayViewController -> {
+ showView(overlayViewController, overlayViewController::showInternal);
+ });
+ mViewsHiddenForOcclusion.clear();
+ }
+
private void debugLog() {
if (!DEBUG) {
return;
@@ -250,5 +303,8 @@ public class OverlayViewGlobalStateController {
Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap);
Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size());
Log.d(TAG, "mZOrderMap: " + mZOrderMap);
+ Log.d(TAG, "mIsOccluded: " + mIsOccluded);
+ Log.d(TAG, "mViewsHiddenForOcclusion: " + mViewsHiddenForOcclusion);
+ Log.d(TAG, "mViewsHiddenForOcclusion.size(): " + mViewsHiddenForOcclusion.size());
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index 38836d85e8d4..189e240169c3 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -169,6 +170,18 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
}
@Test
+ public void setOccludedFalse_currentlyOccluded_bouncerReset() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+ mCarKeyguardViewController.setOccluded(/* occluded= */ true, /* animate= */ false);
+ reset(mBouncer);
+
+ mCarKeyguardViewController.setOccluded(/* occluded= */ false, /* animate= */ false);
+
+ verify(mBouncer).show(/* resetSecuritySelection= */ true);
+ }
+
+ @Test
public void onCancelClicked_callsCancelClickedListener() {
when(mBouncer.isSecure()).thenReturn(true);
mCarKeyguardViewController.show(/* options= */ null);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
index 54282d39998b..bcaa5e9a03ee 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
@@ -36,8 +36,8 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -74,7 +74,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
@Test
public void onCreate_iconIsVisible() {
- AlphaOptimizedImageButton icon = mDefaultButton.findViewById(
+ AlphaOptimizedImageView icon = mDefaultButton.findViewById(
R.id.car_nav_button_icon_image);
assertThat(icon.getDrawable()).isNotNull();
@@ -83,12 +83,12 @@ public class CarNavigationButtonTest extends SysuiTestCase {
@Test
public void onSelected_selectedIconDefined_togglesIcon() {
mDefaultButton.setSelected(true);
- Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
mDefaultButton.setSelected(false);
- Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isNotEqualTo(unselectedIconDrawable);
@@ -100,12 +100,12 @@ public class CarNavigationButtonTest extends SysuiTestCase {
R.id.selected_icon_undefined);
selectedIconUndefinedButton.setSelected(true);
- Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
selectedIconUndefinedButton.setSelected(false);
- Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isEqualTo(unselectedIconDrawable);
@@ -150,7 +150,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
@Test
public void onSelected_doesNotShowMoreWhenSelected_doesNotShowMoreIcon() {
mDefaultButton.setSelected(true);
- AlphaOptimizedImageButton moreIcon = mDefaultButton.findViewById(
+ AlphaOptimizedImageView moreIcon = mDefaultButton.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
@@ -161,7 +161,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
R.id.not_highlightable_more_button);
showMoreWhenSelected.setSelected(true);
- AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.VISIBLE);
@@ -173,7 +173,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
R.id.highlightable_no_more_button);
showMoreWhenSelected.setSelected(true);
showMoreWhenSelected.setSelected(false);
- AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
@@ -187,7 +187,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
roleBasedButton.setSelected(false);
roleBasedButton.setAppIcon(appIcon);
- Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+ Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(currentDrawable).isEqualTo(appIcon);
@@ -212,7 +212,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
roleBasedButton.setSelected(true);
roleBasedButton.setAppIcon(appIcon);
- Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+ Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(currentDrawable).isEqualTo(appIcon);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 9e6e616e3ccf..cba42e5a9be4 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -491,6 +491,81 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
}
@Test
+ public void setOccludedTrue_viewToHideWhenOccludedVisible_viewHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isFalse();
+ }
+
+ @Test
+ public void setOccludedTrue_viewToNotHideWhenOccludedVisible_viewShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isTrue();
+ }
+
+ @Test
+ public void hideViewAndThenSetOccludedTrue_viewHiddenForOcclusion_viewHiddenAfterOcclusion() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+ mOverlayViewGlobalStateController.setOccluded(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, /* runnable= */ null);
+ mOverlayViewGlobalStateController.setOccluded(false);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isFalse();
+ }
+
+ @Test
+ public void setOccludedTrueAndThenShowView_viewToNotHideForOcclusion_viewShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isTrue();
+ }
+
+ @Test
+ public void setOccludedTrueAndThenShowView_viewToHideForOcclusion_viewHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isFalse();
+ }
+
+ @Test
+ public void setOccludedFalse_viewShownAfterSetOccludedTrue_viewToHideForOcclusion_viewShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+ mOverlayViewGlobalStateController.setOccluded(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.setOccluded(false);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isTrue();
+ }
+
+ @Test
public void inflateView_notInflated_inflates() {
when(mOverlayViewController2.isInflated()).thenReturn(false);
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index d039c9f646df..1b5062efa23e 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -140,8 +140,8 @@
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Адкрытая сетка"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Бяспечная сетка"</string>
<string name="process_kernel_label" msgid="950292573930336765">"АС Android"</string>
- <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Выдаленыя прыкладанні"</string>
- <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Выдаленыя прыкладанні і карыстальнiкi"</string>
+ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Выдаленыя праграмы"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Выдаленыя праграмы і карыстальнiкi"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Абнаўленні сістэмы"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"USB-мадэм"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Партатыўны хот-спот"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 65f456e20e4e..d003ef0b9c71 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -449,7 +449,7 @@
<string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string>
- <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiiresti laadimine"</string>
+ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Aeglaselt laadimine"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string>
<string name="battery_info_status_not_charging" msgid="8330015078868707899">"Vooluvõrgus, praegu ei saa laadida"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index a6202956efa5..38eeda245616 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -19,10 +19,13 @@ package com.android.settingslib.applications;
import android.app.Application;
import android.content.ComponentName;
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.ResolveInfo;
import android.hardware.usb.IUsbManager;
+import android.net.Uri;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -44,6 +47,15 @@ public class AppUtils {
*/
private static InstantAppDataProvider sInstantAppDataProvider = null;
+ private static final Intent sBrowserIntent;
+
+ static {
+ sBrowserIntent = new Intent()
+ .setAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse("http:"));
+ }
+
public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry,
IUsbManager usbManager, PackageManager pm, Context context) {
String packageName = appEntry.info.packageName;
@@ -153,4 +165,22 @@ public class AppUtils {
return com.android.settingslib.utils.applications.AppUtils.getAppContentDescription(context,
packageName, userId);
}
+
+ /**
+ * Returns a boolean indicating whether a given package is a browser app.
+ *
+ * An app is a "browser" if it has an activity resolution that wound up
+ * marked with the 'handleAllWebDataURI' flag.
+ */
+ public static boolean isBrowserApp(Context context, String packageName, int userId) {
+ sBrowserIntent.setPackage(packageName);
+ final List<ResolveInfo> list = context.getPackageManager().queryIntentActivitiesAsUser(
+ sBrowserIntent, PackageManager.MATCH_ALL, userId);
+ for (ResolveInfo info : list) {
+ if (info.activityInfo != null && info.handleAllWebDataURI) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index fa87b62bd73a..eb7ad72eb1ba 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -316,7 +316,6 @@ public class SettingsBackupTest {
Settings.Global.KERNEL_CPU_THREAD_READER,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
Settings.Global.LANG_ID_UPDATE_METADATA_URL,
- Settings.Global.LAST_ACTIVE_USER_ID,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ae8e8e82241d..44349c5f5ab4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -306,6 +306,9 @@
<!-- Permission needed for CTS test - PrivilegedLocationPermissionTest -->
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
+ <!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
+ <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/drawable/dismiss_circle_background.xml b/packages/SystemUI/res/drawable/dismiss_circle_background.xml
index e311c520d3d6..7809c8398c2d 100644
--- a/packages/SystemUI/res/drawable/dismiss_circle_background.xml
+++ b/packages/SystemUI/res/drawable/dismiss_circle_background.xml
@@ -21,8 +21,8 @@
<stroke
android:width="1dp"
- android:color="#66FFFFFF" />
+ android:color="#AAFFFFFF" />
- <solid android:color="#B3000000" />
+ <solid android:color="#77000000" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml b/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml
new file mode 100644
index 000000000000..8f7fb1011cf4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="270"
+ android:startColor="#00000000"
+ android:endColor="#77000000"
+ android:type="linear" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml b/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml
new file mode 100644
index 000000000000..6a0695e817c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/transparent" />
+ <item android:drawable="@drawable/floating_dismiss_gradient" />
+</transition> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/dismiss_target_x.xml b/packages/SystemUI/res/drawable/ic_music_note.xml
index 3672efffe139..30959a870a02 100644
--- a/packages/SystemUI/res/drawable/dismiss_target_x.xml
+++ b/packages/SystemUI/res/drawable/ic_music_note.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 The Android Open Source Project
~
@@ -14,15 +13,12 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<!-- 'X' icon. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="#FFFFFFFF"
- android:strokeColor="#FF000000"/>
-</vector> \ No newline at end of file
+ android:fillColor="#FF000000"
+ android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index b34822291815..803b0c61e6da 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -52,39 +52,20 @@
android:gravity="center_vertical"
android:layout_alignEnd="@id/conversation_icon"
android:layout_toEndOf="@id/conversation_icon">
- <LinearLayout
+ <TextView
+ android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="start"
- android:orientation="horizontal">
- <TextView
- android:id="@+id/name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.NotificationImportanceChannel"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- style="@style/TextAppearance.NotificationImportanceHeader"
- android:layout_marginStart="2dp"
- android:layout_marginEnd="2dp"
- android:text="@*android:string/notification_header_divider_symbol" />
- <TextView
- android:id="@+id/parent_channel_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.NotificationImportanceChannel"/>
-
- </LinearLayout>
+ android:ellipsize="end"
+ android:textDirection="locale"
+ style="@style/TextAppearance.NotificationImportanceChannel"/>
<TextView
- android:id="@+id/pkg_name"
+ android:id="@+id/parent_channel_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/TextAppearance.NotificationImportanceChannelGroup"
android:ellipsize="end"
android:textDirection="locale"
- android:maxLines="1"/>
+ style="@style/TextAppearance.NotificationImportanceChannel"/>
<TextView
android:id="@+id/group_name"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
index c27b3a9b3bf4..bf2eac3c8ff3 100644
--- a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
+++ b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
@@ -38,157 +38,67 @@
android:background="@drawable/rounded_bg_full"
>
- <!-- We have a known number of rows that can be shown; just design them all here -->
- <LinearLayout
- android:id="@+id/show_at_top_tip"
+ <ImageView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/notification_guts_conversation_icon_size"
+ android:layout_height="@dimen/notification_guts_conversation_icon_size"
+ android:layout_gravity="center_horizontal" />
+
+ <TextView
+ android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:orientation="horizontal"
- >
- <ImageView
- android:id="@+id/bell_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="center_vertical"
- android:src="@drawable/ic_notifications_alert"
- android:tint="?android:attr/colorControlNormal" />
-
- <TextView
- android:id="@+id/show_at_top_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:gravity="center_vertical|start"
- android:textSize="15sp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/priority_onboarding_show_at_top_text"
- style="@style/TextAppearance.NotificationInfo"
- />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/show_avatar_tip"
+ android:gravity="center_horizontal"
+ android:layout_marginTop="16dp"
+ android:text="@string/priority_onboarding_title"
+ style="@style/TextAppearance.NotificationImportanceChannel"
+ />
+
+ <View
+ android:id="@+id/divider"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:orientation="horizontal"
- >
- <ImageView
- android:id="@+id/avatar_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="center_vertical"
- android:src="@drawable/ic_person"
- android:tint="?android:attr/colorControlNormal" />
-
- <TextView
- android:id="@+id/avatar_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:gravity="center_vertical|start"
- android:textSize="15sp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/priority_onboarding_show_avatar_text"
- style="@style/TextAppearance.NotificationInfo"
- />
+ android:layout_height="0.5dp"
+ android:layout_marginTop="20dp"
+ android:layout_marginBottom="20dp"
+ android:background="@color/material_grey_300" />
- </LinearLayout>
-
- <!-- These rows show optionally -->
-
- <LinearLayout
- android:id="@+id/floating_bubble_tip"
+ <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:orientation="horizontal"
- >
-
- <ImageView
- android:id="@+id/bubble_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="center_vertical"
- android:src="@drawable/ic_create_bubble"
- android:tint="?android:attr/colorControlNormal" />
+ android:gravity="start"
+ android:text="@string/priority_onboarding_behavior"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"
+ />
- <TextView
- android:id="@+id/bubble_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:gravity="center_vertical|start"
- android:textSize="15sp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/priority_onboarding_appear_as_bubble_text"
- style="@style/TextAppearance.NotificationInfo"
- />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/ignore_dnd_tip"
+ <TextView
+ android:id="@+id/behaviors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:orientation="horizontal"
- >
-
- <ImageView
- android:id="@+id/dnd_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="center_vertical"
- android:src="@drawable/moon"
- android:tint="?android:attr/colorControlNormal" />
-
- <TextView
- android:id="@+id/dnd_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:gravity="center_vertical|start"
- android:textSize="15sp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/priority_onboarding_ignores_dnd_text"
- style="@style/TextAppearance.NotificationInfo"
- />
-
- </LinearLayout>
+ android:gravity="start"
+ android:layout_marginTop="8dp"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"
+ />
<!-- Bottom button container -->
<RelativeLayout
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ android:layout_marginTop="32dp"
android:orientation="horizontal"
>
<TextView
+ android:id="@+id/settings_button"
+ android:text="@string/priority_onboarding_settings_button_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="125dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
android:id="@+id/done_button"
android:text="@string/priority_onboarding_done_button_title"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 5b7e7e7d59a3..44f52efd175e 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -21,8 +21,6 @@
android:layout_height="wrap_content"
android:paddingTop="@dimen/qs_header_top_padding"
android:paddingBottom="@dimen/qs_header_bottom_padding"
- android:paddingStart="@dimen/status_bar_padding_start"
- android:paddingEnd="@dimen/status_bar_padding_end"
android:layout_below="@id/quick_status_bar_system_icons"
android:clipChildren="false"
android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 28672241b1b2..12127f529054 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -18,8 +18,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
style="@style/BrightnessDialogContainer">
<com.android.systemui.settings.ToggleSliderView
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 846c5386dd06..15f398aa52e6 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -23,7 +23,7 @@
android:paddingStart="@dimen/qs_footer_padding_start"
android:paddingEnd="@dimen/qs_footer_padding_end"
android:gravity="center_vertical"
- android:background="?android:attr/colorPrimary" >
+ android:background="@android:color/transparent">
<TextView
android:id="@+id/footer_text"
@@ -32,7 +32,7 @@
android:gravity="start"
android:layout_weight="1"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
- android:textColor="?android:attr/textColorSecondary"/>
+ style="@style/qs_security_footer"/>
<ImageView
android:id="@+id/footer_icon"
@@ -40,6 +40,6 @@
android:layout_height="@dimen/qs_footer_icon_size"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline"
- android:tint="?android:attr/textColorSecondary"/>
+ style="@style/qs_security_footer"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 683e86728e91..e6ef9b4b902c 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -19,8 +19,6 @@
android:layout_width="match_parent"
android:layout_height="@dimen/qs_header_tooltip_height"
android:layout_below="@id/quick_status_bar_system_icons"
- android:paddingStart="@dimen/status_bar_padding_start"
- android:paddingEnd="@dimen/status_bar_padding_end"
android:visibility="invisible"
android:theme="@style/QSHeaderTheme">
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 9a7c344baf20..abeb33111c40 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -33,6 +33,7 @@
android:paddingStart="0dp"
android:elevation="4dp" >
+ <!-- The clock -->
<include layout="@layout/quick_status_bar_header_system_icons" />
<!-- Status icons within the panel itself (and not in the top-most status bar) -->
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index b27d096c12b5..be86e5f5abc5 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -25,8 +25,6 @@
android:gravity="center"
android:orientation="horizontal"
android:clickable="true"
- android:paddingStart="@dimen/status_bar_padding_start"
- android:paddingEnd="@dimen/status_bar_padding_end"
android:paddingTop="@dimen/status_bar_padding_top" >
<com.android.systemui.statusbar.policy.Clock
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index af6f9bb827f6..0c4d5a26f95b 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -16,21 +16,20 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="250dp"
android:layout_height="48dp"
android:orientation="vertical"
- android:padding="10dp"
- android:layout_weight="1">
+ android:padding="13dp">
<TextView
android:id="@+id/screen_recording_dialog_source_text"
- android:layout_width="match_parent"
+ android:layout_width="250dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"/>
<TextView
android:id="@+id/screen_recording_dialog_source_description"
- android:layout_width="wrap_content"
+ android:layout_width="250dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 4fdeb6fa4a92..50261e1b2139 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -29,4 +29,9 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="qs_security_footer" parent="@style/qs_theme">
+ <item name="android:textColor">#B3FFFFFF</item> <!-- 70% white -->
+ <item name="android:tint">#FFFFFFFF</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml
deleted file mode 100644
index c110113e91f4..000000000000
--- a/packages/SystemUI/res/values-sw320dp/dimens.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<resources>
- <!-- Global actions grid -->
- <dimen name="global_actions_grid_vertical_padding">3dp</dimen>
- <dimen name="global_actions_grid_horizontal_padding">3dp</dimen>
-
- <dimen name="global_actions_grid_item_side_margin">5dp</dimen>
- <dimen name="global_actions_grid_item_vertical_margin">4dp</dimen>
- <dimen name="global_actions_grid_item_width">64dp</dimen>
- <dimen name="global_actions_grid_item_height">64dp</dimen>
-
- <dimen name="global_actions_grid_item_icon_width">20dp</dimen>
- <dimen name="global_actions_grid_item_icon_height">20dp</dimen>
- <dimen name="global_actions_grid_item_icon_top_margin">12dp</dimen>
- <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen>
- <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
-
- <!-- Home Controls -->
- <dimen name="global_actions_side_margin">10dp</dimen>
-</resources>
-
diff --git a/packages/SystemUI/res/values-sw372dp/dimens.xml b/packages/SystemUI/res/values-sw372dp/dimens.xml
index e64662ec7bea..d3fff0c059f9 100644
--- a/packages/SystemUI/res/values-sw372dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw372dp/dimens.xml
@@ -17,5 +17,4 @@
-->
<resources>
<dimen name="nav_content_padding">8dp</dimen>
- <dimen name="qs_header_tile_margin_horizontal">13dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a7d176443c55..73d8e9a0d8a7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -224,6 +224,7 @@
<dimen name="notification_guts_conversation_icon_size">56dp</dimen>
<dimen name="notification_guts_conversation_action_height">56dp</dimen>
<dimen name="notification_guts_conversation_action_text_padding_start">32dp</dimen>
+ <dimen name="conversation_onboarding_bullet_gap_width">6dp</dimen>
<!-- The height of the header in inline settings -->
<dimen name="notification_guts_header_height">24dp</dimen>
@@ -482,7 +483,8 @@
<dimen name="pull_span_min">25dp</dimen>
<dimen name="qs_tile_height">106dp</dimen>
- <dimen name="qs_tile_layout_margin_side">6dp</dimen>
+ <!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
+ <dimen name="qs_tile_layout_margin_side">18dp</dimen>
<dimen name="qs_tile_margin_horizontal">18dp</dimen>
<dimen name="qs_tile_margin_horizontal_two_line">2dp</dimen>
<dimen name="qs_tile_margin_vertical">24dp</dimen>
@@ -498,7 +500,6 @@
<dimen name="qs_quick_tile_size">48dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
- <dimen name="qs_header_tile_margin_horizontal">4dp</dimen>
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
@@ -973,7 +974,9 @@
<dimen name="recents_quick_scrub_onboarding_margin_start">8dp</dimen>
<!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
- <dimen name="floating_dismiss_gradient_height">176dp</dimen>
+ <dimen name="floating_dismiss_gradient_height">250dp</dimen>
+
+ <dimen name="floating_dismiss_bottom_margin">50dp</dimen>
<!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
<dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
@@ -1031,8 +1034,23 @@
<dimen name="global_actions_grid_container_shadow_offset">20dp</dimen>
<dimen name="global_actions_grid_container_negative_shadow_offset">-20dp</dimen>
+ <!-- Global actions grid -->
+ <dimen name="global_actions_grid_vertical_padding">3dp</dimen>
+ <dimen name="global_actions_grid_horizontal_padding">3dp</dimen>
+
+ <dimen name="global_actions_grid_item_side_margin">5dp</dimen>
+ <dimen name="global_actions_grid_item_vertical_margin">4dp</dimen>
+ <dimen name="global_actions_grid_item_width">64dp</dimen>
+ <dimen name="global_actions_grid_item_height">64dp</dimen>
+
+ <dimen name="global_actions_grid_item_icon_width">20dp</dimen>
+ <dimen name="global_actions_grid_item_icon_height">20dp</dimen>
+ <dimen name="global_actions_grid_item_icon_top_margin">12dp</dimen>
+ <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen>
+ <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
+
<!-- Margins at the left and right of the power menu and home controls widgets. -->
- <dimen name="global_actions_side_margin">16dp</dimen>
+ <dimen name="global_actions_side_margin">10dp</dimen>
<!-- Amount to shift the layout when exiting/entering for controls activities -->
<dimen name="global_actions_controls_y_translation">20dp</dimen>
@@ -1057,9 +1075,8 @@
<dimen name="edge_margin">8dp</dimen>
<!-- The absolute side margins of quick settings -->
- <dimen name="quick_settings_side_margins">16dp</dimen>
<dimen name="quick_settings_expanded_bottom_margin">16dp</dimen>
- <dimen name="quick_settings_media_extra_bottom_margin">4dp</dimen>
+ <dimen name="quick_settings_media_extra_bottom_margin">6dp</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 97a0d032c4a7..0314fc89d33a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2667,6 +2667,11 @@
<string name="inattentive_sleep_warning_title">Standby</string>
<!-- Priority conversation onboarding screen -->
+ <!-- title of priority onboarding [CHAR LIMIT=75] -->
+ <string name="priority_onboarding_title">Conversation set to priority</string>
+ <!-- Text explaining that the following actions are the behaviors of priority conversations.
+ E.g. priority conversations will show at the top of the conversation section [CHAR LIMIT=75] -->
+ <string name="priority_onboarding_behavior">Priority conversations will:</string>
<!-- Text explaining that priority conversations show at the top of the conversation section [CHAR LIMIT=75] -->
<string name="priority_onboarding_show_at_top_text">Show at top of conversation section</string>
<!-- Text explaining that priority conversations show an avatar on the lock screen [CHAR LIMIT=75] -->
@@ -2677,6 +2682,8 @@
<string name="priority_onboarding_ignores_dnd_text">Interrupt Do Not Disturb</string>
<!-- Title for the affirmative button [CHAR LIMIT=50] -->
<string name="priority_onboarding_done_button_title">Got it</string>
+ <!-- Title for the settings button button [CHAR LIMIT=50] -->
+ <string name="priority_onboarding_settings_button_title">Settings</string>
<!-- Window Magnification strings -->
<!-- Title for Magnification Overlay Window [CHAR LIMIT=NONE] -->
@@ -2774,6 +2781,8 @@
<!-- Close the controls associated with a specific media session [CHAR_LIMIT=NONE] -->
<string name="controls_media_close_session">Close this media session</string>
+ <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_resume">Resume</string>
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
<string name="controls_error_timeout">Inactive, check app</string>
@@ -2781,7 +2790,13 @@
a retry will be attempted [CHAR LIMIT=30] -->
<string name="controls_error_retryable">Error, retrying\u2026</string>
<!-- Error message indicating that the control is no longer available in the application [CHAR LIMIT=30] -->
- <string name="controls_error_removed">Device removed</string>
+ <string name="controls_error_removed">Not found</string>
+ <!-- Title for dialog indicating that the control is no longer available in the application [CHAR LIMIT=30] -->
+ <string name="controls_error_removed_title">Control is unavailable</string>
+ <!-- Message body for dialog indicating that the control is no longer available in the application [CHAR LIMIT=NONE] -->
+ <string name="controls_error_removed_message">Couldn\u2019t access <xliff:g id="device" example="Backdoor lock">%1$s</xliff:g>. Check the <xliff:g id="application" example="Google Home">%2$s</xliff:g> app to make sure the control is still available and that the app settings haven\u2019t changed.</string>
+ <!-- Text for button to open the corresponding application [CHAR_LIMIT=20] -->
+ <string name="controls_open_app">Open app</string>
<!-- Error message indicating that an unspecified error occurred while getting the status [CHAR LIMIT=30] -->
<string name="controls_error_generic">Can\u2019t load status</string>
<!-- Error message indicating that a control action failed [CHAR_LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ed36bdbe1e7e..39f78bf46028 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -387,6 +387,11 @@
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
</style>
+ <style name="qs_security_footer" parent="@style/qs_theme">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:tint">?android:attr/textColorSecondary</item>
+ </style>
+
<style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:colorAccent">@color/remote_input_accent</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 35ad422c56b8..655008b7745a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -23,10 +23,11 @@ import android.os.Bundle;
import android.view.MotionEvent;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.model.Task;
/**
* Temporary callbacks into SystemUI.
- * Next id = 26
+ * Next id = 27
*/
interface ISystemUiProxy {
@@ -122,6 +123,9 @@ interface ISystemUiProxy {
/**
* Handle the provided image as if it was a screenshot.
+ *
+ * Deprecated, use handleImageBundleAsScreenshot with image bundle and UserTask
+ * @deprecated
*/
void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
in Insets visibleInsets, int taskId) = 21;
@@ -146,4 +150,10 @@ interface ISystemUiProxy {
* @param rotation indicates which Surface.Rotation the gesture was started in
*/
void onQuickSwitchToNewTask(int rotation) = 25;
+
+ /**
+ * Handle the provided image as if it was a screenshot.
+ */
+ void handleImageBundleAsScreenshot(in Bundle screenImageBundle, in Rect locationInScreen,
+ in Insets visibleInsets, in Task.TaskKey task) = 26;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.aidl
new file mode 100644
index 000000000000..e7cad2acc645
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.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 com.android.systemui.shared.recents.model;
+
+parcelable Task.TaskKey; \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index dcb134ec933e..186379af4b1d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -26,6 +26,8 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.view.ViewDebug;
import com.android.systemui.shared.recents.utilities.Utilities;
@@ -52,8 +54,10 @@ public class Task {
void onTaskWindowingModeChanged();
}
- /* The Task Key represents the unique primary key for the task */
- public static class TaskKey {
+ /**
+ * The Task Key represents the unique primary key for the task
+ */
+ public static class TaskKey implements Parcelable {
@ViewDebug.ExportedProperty(category="recents")
public final int id;
@ViewDebug.ExportedProperty(category="recents")
@@ -157,6 +161,48 @@ public class Task {
private void updateHashCode() {
mHashCode = Objects.hash(id, windowingMode, userId);
}
+
+ public static final Parcelable.Creator<TaskKey> CREATOR =
+ new Parcelable.Creator<TaskKey>() {
+ @Override
+ public TaskKey createFromParcel(Parcel source) {
+ return TaskKey.readFromParcel(source);
+ }
+
+ @Override
+ public TaskKey[] newArray(int size) {
+ return new TaskKey[size];
+ }
+ };
+
+ @Override
+ public final void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(id);
+ parcel.writeInt(windowingMode);
+ parcel.writeTypedObject(baseIntent, flags);
+ parcel.writeInt(userId);
+ parcel.writeLong(lastActiveTime);
+ parcel.writeInt(displayId);
+ parcel.writeTypedObject(sourceComponent, flags);
+ }
+
+ private static TaskKey readFromParcel(Parcel parcel) {
+ int id = parcel.readInt();
+ int windowingMode = parcel.readInt();
+ Intent baseIntent = parcel.readTypedObject(Intent.CREATOR);
+ int userId = parcel.readInt();
+ long lastActiveTime = parcel.readLong();
+ int displayId = parcel.readInt();
+ ComponentName sourceComponent = parcel.readTypedObject(ComponentName.CREATOR);
+
+ return new TaskKey(id, windowingMode, baseIntent, sourceComponent, userId,
+ lastActiveTime, displayId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
}
@ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java
new file mode 100644
index 000000000000..b79fcbd43972
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java
@@ -0,0 +1,86 @@
+/*
+ * 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.shared.recents.utilities;
+
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.ParcelableColorSpace;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+
+import java.util.Objects;
+
+/**
+ * Utils for working with Bitmaps.
+ */
+public final class BitmapUtil {
+ private static final String KEY_BUFFER = "bitmap_util_buffer";
+ private static final String KEY_COLOR_SPACE = "bitmap_util_color_space";
+
+ private BitmapUtil(){ }
+
+ /**
+ * Creates a Bundle that represents the given Bitmap.
+ * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid
+ * copies when passing across processes, only pass to processes you trust.
+ *
+ * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the
+ * returned Bundle should be treated as a standalone object.
+ *
+ * @param bitmap to convert to bundle
+ * @return a Bundle representing the bitmap, should only be parsed by
+ * {@link #bundleToHardwareBitmap(Bundle)}
+ */
+ public static Bundle hardwareBitmapToBundle(Bitmap bitmap) {
+ if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
+ throw new IllegalArgumentException(
+ "Passed bitmap must have hardware config, found: " + bitmap.getConfig());
+ }
+
+ // Bitmap assumes SRGB for null color space
+ ParcelableColorSpace colorSpace =
+ bitmap.getColorSpace() == null
+ ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
+ : new ParcelableColorSpace(bitmap.getColorSpace());
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());
+ bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);
+
+ return bundle;
+ }
+
+ /**
+ * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .}
+ *
+ * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing this
+ * Bitmap on to any other source.
+ *
+ * @param bundle containing the bitmap
+ * @return a hardware Bitmap
+ */
+ public static Bitmap bundleToHardwareBitmap(Bundle bundle) {
+ if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) {
+ throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
+ }
+
+ HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER);
+ ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE);
+
+ return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer), colorSpace);
+ }
+}
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 dd5cc7c9bbd4..796aaeefb62f 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
@@ -114,4 +114,7 @@ public abstract class TaskStackChangeListener {
/** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */
public void onRecentTaskListFrozenChanged(boolean frozen) { }
+
+ /** @see ITaskStackListener#onActivityRotation()*/
+ public void onActivityRotation() { }
}
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 a76a901c5c81..13f7993f57d4 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
@@ -237,6 +237,11 @@ public class TaskStackChangeListeners extends TaskStackListener {
mHandler.obtainMessage(H.ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
}
+ @Override
+ public void onActivityRotation() {
+ mHandler.obtainMessage(H.ON_ACTIVITY_ROTATION).sendToTarget();
+ }
+
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -260,6 +265,7 @@ public class TaskStackChangeListeners extends TaskStackListener {
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;
public H(Looper looper) {
@@ -427,6 +433,12 @@ public class TaskStackChangeListeners extends TaskStackListener {
}
break;
}
+ case ON_ACTIVITY_ROTATION: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityRotation();
+ }
+ break;
+ }
}
}
if (msg.obj instanceof SomeArgs) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index dd613263e5c2..73783ae7ece2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.shared.system;
+import android.graphics.HardwareRenderer;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
@@ -50,7 +51,13 @@ public class ViewRootImplCompat {
public void registerRtFrameCallback(LongConsumer callback) {
if (mViewRoot != null) {
- mViewRoot.registerRtFrameCallback(callback::accept);
+ mViewRoot.registerRtFrameCallback(
+ new HardwareRenderer.FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long l) {
+ callback.accept(l);
+ }
+ });
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
new file mode 100644
index 000000000000..8cd68ef8acbc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -0,0 +1,26 @@
+package com.android.keyguard
+
+import android.annotation.CurrentTimeMillisLong
+
+/**
+ * Data class for tracking information associated with [KeyguardUpdateMonitor.shouldListenForFace]
+ * method calls.
+ */
+data class KeyguardFaceListenModel(
+ @CurrentTimeMillisLong val timeMillis: Long,
+ val userId: Int,
+ val isListeningForFace: Boolean,
+ val isBouncer: Boolean,
+ val isAuthInterruptActive: Boolean,
+ val isKeyguardAwake: Boolean,
+ val isListeningForFaceAssistant: Boolean,
+ val isSwitchingUser: Boolean,
+ val isFaceDisabled: Boolean,
+ val isBecauseCannotSkipBouncer: Boolean,
+ val isKeyguardGoingAway: Boolean,
+ val isFaceSettingEnabledForUser: Boolean,
+ val isLockIconPressed: Boolean,
+ val isScanningAllowedByStrongAuth: Boolean,
+ val isPrimaryUser: Boolean,
+ val isSecureCameraLaunched: Boolean
+)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0db713e9c276..ee31706c0b94 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -58,6 +58,7 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
@@ -108,9 +109,13 @@ import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
@@ -131,7 +136,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final String TAG = "KeyguardUpdateMonitor";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
- private static final boolean DEBUG_FACE = true;
+ private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_SPEW = false;
private static final int LOW_BATTERY_THRESHOLD = 20;
@@ -362,6 +367,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@VisibleForTesting
SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
+ // Keep track of recent calls to shouldListenForFace() for debugging.
+ private static final int FACE_LISTEN_CALLS_QUEUE_SIZE = 20;
+ private ArrayDeque<KeyguardFaceListenModel> mFaceListenModels;
+
private static int sCurrentUser;
private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
@@ -1945,25 +1954,48 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& strongAuthAllowsScanning && mIsPrimaryUser
&& !mSecureCameraLaunched;
+ // Aggregate relevant fields for debug logging.
+ if (DEBUG_FACE || DEBUG_SPEW) {
+ final KeyguardFaceListenModel model = new KeyguardFaceListenModel(
+ System.currentTimeMillis(),
+ user,
+ shouldListen,
+ mBouncer,
+ mAuthInterruptActive,
+ awakeKeyguard,
+ shouldListenForFaceAssistant(),
+ mSwitchingUser,
+ isFaceDisabled(user),
+ becauseCannotSkipBouncer,
+ mKeyguardGoingAway,
+ mFaceSettingEnabledForUser.get(user),
+ mLockIconPressed,
+ strongAuthAllowsScanning,
+ mIsPrimaryUser,
+ mSecureCameraLaunched);
+ maybeLogFaceListenerModelData(model);
+ }
+
+ return shouldListen;
+ }
+
+ private void maybeLogFaceListenerModelData(KeyguardFaceListenModel model) {
// Too chatty, but very useful when debugging issues.
if (DEBUG_SPEW) {
- Log.v(TAG, "shouldListenForFace(" + user + ")=" + shouldListen + "... "
- + ", mBouncer: " + mBouncer
- + ", mAuthInterruptActive: " + mAuthInterruptActive
- + ", awakeKeyguard: " + awakeKeyguard
- + ", shouldListenForFaceAssistant: " + shouldListenForFaceAssistant()
- + ", mSwitchingUser: " + mSwitchingUser
- + ", isFaceDisabled(" + user + "): " + isFaceDisabled(user)
- + ", becauseCannotSkipBouncer: " + becauseCannotSkipBouncer
- + ", mKeyguardGoingAway: " + mKeyguardGoingAway
- + ", mFaceSettingEnabledForUser(" + user + "): "
- + mFaceSettingEnabledForUser.get(user)
- + ", mLockIconPressed: " + mLockIconPressed
- + ", strongAuthAllowsScanning: " + strongAuthAllowsScanning
- + ", isPrimaryUser: " + mIsPrimaryUser
- + ", mSecureCameraLaunched: " + mSecureCameraLaunched);
+ Log.v(TAG, model.toString());
+ }
+
+ // Add model data to the historical buffer.
+ if (DEBUG_FACE && mFaceRunningState != BIOMETRIC_STATE_RUNNING
+ && model.isListeningForFace()) {
+ if (mFaceListenModels == null) {
+ mFaceListenModels = new ArrayDeque<>(FACE_LISTEN_CALLS_QUEUE_SIZE);
+ }
+ if (mFaceListenModels.size() >= FACE_LISTEN_CALLS_QUEUE_SIZE) {
+ mFaceListenModels.remove();
+ }
+ mFaceListenModels.add(model);
}
- return shouldListen;
}
/**
@@ -2919,5 +2951,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" enabledByUser=" + mFaceSettingEnabledForUser.get(userId));
pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched);
}
+ if (mFaceListenModels != null && !mFaceListenModels.isEmpty()) {
+ final SimpleDateFormat dateFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+ pw.println(" Face listen results (last " + FACE_LISTEN_CALLS_QUEUE_SIZE + " calls):");
+ for (final KeyguardFaceListenModel model : mFaceListenModels) {
+ final String time = dateFormat.format(new Date(model.getTimeMillis()));
+ pw.println(" " + time + " " + model.toString());
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 87990cd3bffa..ccb506de6d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -124,7 +124,7 @@ public final class Prefs {
String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
/** Tracks whether the user has seen the onboarding screen for priority conversations */
- String HAS_SEEN_PRIORITY_ONBOARDING = "HasSeenPriorityOnboarding";
+ String HAS_SEEN_PRIORITY_ONBOARDING = "HaveShownPriorityOnboarding";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 0690907e8433..8a2c101f7057 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -798,6 +798,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleIconFactory = new BubbleIconFactory(mContext);
mStackView.onDisplaySizeChanged();
}
+
+ mStackView.onLayoutDirectionChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index fee7847ee220..790d6a2070a2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -316,6 +316,10 @@ public class BubbleExpandedView extends LinearLayout {
return false;
});
+
+ // BubbleStackView is forced LTR, but we want to respect the locale for expanded view layout
+ // so the Manage button appears on the right.
+ setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
}
private String getBubbleKey() {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index e12b325f5d05..8c76cda3290f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -186,6 +186,9 @@ public class BubbleFlyoutView extends FrameLayout {
}
});
+ // Use locale direction so the text is aligned correctly.
+ setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
+
mBgPaint.setColor(mFloatingBackgroundColor);
mLeftTriangleShape =
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 8fd20517782f..3e694b9f5d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -285,7 +285,10 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
}
});
- ShortcutInfo info = b.getEntry().getRanking().getShortcutInfo();
+ // If the bubble was persisted, the entry is null but it should have shortcut info
+ ShortcutInfo info = b.getEntry() == null
+ ? b.getShortcutInfo()
+ : b.getEntry().getRanking().getShortcutInfo();
if (info == null) {
Log.d(TAG, "ShortcutInfo required to bubble but none found for " + b);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index fb081e2bf904..8a80c4d75e84 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -47,6 +47,7 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
@@ -133,6 +134,9 @@ public class BubbleStackView extends FrameLayout
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
+ /** Duration of the dismiss scrim fading in/out. */
+ private static final int DISMISS_TRANSITION_DURATION_MS = 200;
+
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
@@ -723,6 +727,12 @@ public class BubbleStackView extends FrameLayout
setUpUserEducation();
+ // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
+ // is centered. It greatly simplifies translation positioning/animations. Views that will
+ // actually lay out differently in RTL, such as the flyout and expanded view, will set their
+ // layout direction to LOCALE.
+ setLayoutDirection(LAYOUT_DIRECTION_LTR);
+
mBubbleContainer = new PhysicsAnimationLayout(context);
mBubbleContainer.setActiveController(mStackAnimationController);
mBubbleContainer.setElevation(elevation);
@@ -746,7 +756,7 @@ public class BubbleStackView extends FrameLayout
final View targetView = new DismissCircleView(context);
final FrameLayout.LayoutParams newParams =
new FrameLayout.LayoutParams(targetSize, targetSize);
- newParams.gravity = Gravity.CENTER;
+ newParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
targetView.setLayoutParams(newParams);
mDismissTargetAnimator = PhysicsAnimator.getInstance(targetView);
@@ -755,9 +765,16 @@ public class BubbleStackView extends FrameLayout
MATCH_PARENT,
getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
Gravity.BOTTOM));
+
+ final int bottomMargin =
+ getResources().getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin);
+ mDismissTargetContainer.setPadding(0, 0, 0, bottomMargin);
+ mDismissTargetContainer.setClipToPadding(false);
mDismissTargetContainer.setClipChildren(false);
mDismissTargetContainer.addView(targetView);
mDismissTargetContainer.setVisibility(View.INVISIBLE);
+ mDismissTargetContainer.setBackgroundResource(
+ R.drawable.floating_dismiss_gradient_transition);
addView(mDismissTargetContainer);
// Start translated down so the target springs up.
@@ -961,6 +978,9 @@ public class BubbleStackView extends FrameLayout
mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon);
mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name);
+
+ // The menu itself should respect locale direction so the icons are on the correct side.
+ mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
addView(mManageMenu);
}
@@ -1084,6 +1104,16 @@ public class BubbleStackView extends FrameLayout
mShowingManage = false;
}
+ /** Tells the views with locale-dependent layout direction to resolve the new direction. */
+ public void onLayoutDirectionChanged() {
+ mManageMenu.resolveLayoutDirection();
+ mFlyout.resolveLayoutDirection();
+
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().resolveLayoutDirection();
+ }
+ }
+
/** Respond to the display size change by recalculating view size and location. */
public void onDisplaySizeChanged() {
setUpOverflow();
@@ -1865,6 +1895,9 @@ public class BubbleStackView extends FrameLayout
mDismissTargetContainer.setZ(Short.MAX_VALUE - 1);
mDismissTargetContainer.setVisibility(VISIBLE);
+ ((TransitionDrawable) mDismissTargetContainer.getBackground()).startTransition(
+ DISMISS_TRANSITION_DURATION_MS);
+
mDismissTargetAnimator.cancel();
mDismissTargetAnimator
.spring(DynamicAnimation.TRANSLATION_Y, 0f, mDismissTargetSpring)
@@ -1882,6 +1915,9 @@ public class BubbleStackView extends FrameLayout
mShowingDismiss = false;
+ ((TransitionDrawable) mDismissTargetContainer.getBackground()).reverseTransition(
+ DISMISS_TRANSITION_DURATION_MS);
+
mDismissTargetAnimator
.spring(DynamicAnimation.TRANSLATION_Y, mDismissTargetContainer.getHeight(),
mDismissTargetSpring)
@@ -2095,16 +2131,21 @@ public class BubbleStackView extends FrameLayout
mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
- // When the menu is open, it should be at these coordinates. This will make the menu's
- // bottom left corner match up with the button's bottom left corner.
- final float targetX = mTempRect.left;
+ final boolean isLtr =
+ getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR;
+
+ // When the menu is open, it should be at these coordinates. The menu pops out to the right
+ // in LTR and to the left in RTL.
+ final float targetX = isLtr ? mTempRect.left : mTempRect.right - mManageMenu.getWidth();
final float targetY = mTempRect.bottom - mManageMenu.getHeight();
+ final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
+
if (show) {
mManageMenu.setScaleX(0.5f);
mManageMenu.setScaleY(0.5f);
- mManageMenu.setTranslationX(targetX - mManageMenu.getWidth() / 4);
- mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4);
+ mManageMenu.setTranslationX(targetX - xOffsetForAnimation);
+ mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4f);
mManageMenu.setAlpha(0f);
PhysicsAnimator.getInstance(mManageMenu)
@@ -2121,8 +2162,8 @@ public class BubbleStackView extends FrameLayout
.spring(DynamicAnimation.ALPHA, 0f)
.spring(DynamicAnimation.SCALE_X, 0.5f)
.spring(DynamicAnimation.SCALE_Y, 0.5f)
- .spring(DynamicAnimation.TRANSLATION_X, targetX - mManageMenu.getWidth() / 4)
- .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4)
+ .spring(DynamicAnimation.TRANSLATION_X, targetX - xOffsetForAnimation)
+ .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4f)
.withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE))
.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index fd9fda3662a3..93f0c7f41ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -44,6 +44,7 @@ import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
+import com.android.systemui.globalactions.GlobalActionsDialog
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -79,6 +80,7 @@ class ControlsControllerImpl @Inject constructor (
}
private var userChanging: Boolean = true
+ private var userStructure: UserStructure
private var seedingInProgress = false
private val seedingCallbacks = mutableListOf<Consumer<Boolean>>()
@@ -97,7 +99,7 @@ class ControlsControllerImpl @Inject constructor (
internal var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper
init {
- val userStructure = UserStructure(context, currentUser)
+ userStructure = UserStructure(context, currentUser)
persistenceWrapper = optionalWrapper.orElseGet {
ControlsFavoritePersistenceWrapper(
@@ -116,7 +118,7 @@ class ControlsControllerImpl @Inject constructor (
private fun setValuesForUser(newUser: UserHandle) {
Log.d(TAG, "Changing to user: $newUser")
currentUser = newUser
- val userStructure = UserStructure(context, currentUser)
+ userStructure = UserStructure(context, currentUser)
persistenceWrapper.changeFileAndBackupManager(
userStructure.file,
BackupManager(userStructure.userContext)
@@ -192,6 +194,16 @@ class ControlsControllerImpl @Inject constructor (
it.componentName
}.toSet()
+ // When a component is uninstalled, allow seeding to happen again if the user
+ // reinstalls the app
+ val prefs = userStructure.userContext.getSharedPreferences(
+ GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
+ val completedSeedingPackageSet = prefs.getStringSet(
+ GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
+ val favoritePackageSet = favoriteComponentSet.map { it.packageName }
+ prefs.edit().putStringSet(GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED,
+ completedSeedingPackageSet.intersect(favoritePackageSet)).apply()
+
var changed = false
favoriteComponentSet.subtract(serviceInfoSet).forEach {
changed = true
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 10e913743224..2a40b76eb326 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -58,16 +58,14 @@ class ControlActionCoordinatorImpl @Inject constructor(
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
bouncerOrRun {
- val effect = if (!isChecked) Vibrations.toggleOnEffect else Vibrations.toggleOffEffect
- vibrate(effect)
+ cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
cvh.action(BooleanAction(templateId, !isChecked))
}
}
override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
- vibrate(Vibrations.toggleOnEffect)
-
bouncerOrRun {
+ cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
if (cvh.usePanel()) {
showDialog(cvh, control.getAppIntent().getIntent())
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index f07f3168d246..994557cf696b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -118,6 +118,7 @@ class ControlViewHolder(
var behavior: Behavior? = null
var lastAction: ControlAction? = null
var isLoading = false
+ var visibleDialog: Dialog? = null
private var lastChallengeDialog: Dialog? = null
private val onDialogCancel: () -> Unit = { lastChallengeDialog = null }
@@ -197,18 +198,24 @@ class ControlViewHolder(
fun dismiss() {
lastChallengeDialog?.dismiss()
lastChallengeDialog = null
+ visibleDialog?.dismiss()
+ visibleDialog = null
}
fun setTransientStatus(tempStatus: String) {
val previousText = status.getText()
cancelUpdate = uiExecutor.executeDelayed({
- setStatusText(previousText)
- updateContentDescription()
+ animateStatusChange(/* animated */ true, {
+ setStatusText(previousText, /* immediately */ true)
+ updateContentDescription()
+ })
}, UPDATE_DELAY_IN_MILLIS)
- setStatusText(tempStatus)
- updateContentDescription()
+ animateStatusChange(/* animated */ true, {
+ setStatusText(tempStatus, /* immediately */ true)
+ updateContentDescription()
+ })
}
private fun updateContentDescription() =
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 35ebac5b1ed5..d31b6eb9d857 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -129,7 +129,10 @@ class ControlsUiControllerImpl @Inject constructor (
SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName)
}
uiExecutor.execute {
- onResult(lastItems)
+ parent.removeAllViews()
+ if (lastItems.size > 0) {
+ onResult(lastItems)
+ }
}
}
}
@@ -189,8 +192,6 @@ class ControlsUiControllerImpl @Inject constructor (
}
private fun showSeedingView(items: List<SelectionItem>) {
- parent.removeAllViews()
-
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_no_favorites, parent, true)
val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
@@ -198,8 +199,6 @@ class ControlsUiControllerImpl @Inject constructor (
}
private fun showInitialSetupView(items: List<SelectionItem>) {
- parent.removeAllViews()
-
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_no_favorites, parent, true)
@@ -263,7 +262,6 @@ class ControlsUiControllerImpl @Inject constructor (
}
private fun showControlsView(items: List<SelectionItem>) {
- parent.removeAllViews()
controlViewsById.clear()
createListView()
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index f97015282222..9ec14523a809 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -64,6 +64,10 @@ class DetailDialog(
}
override fun onActivityViewDestroyed(view: ActivityView) {}
+
+ override fun onTaskRemovalStarted(taskId: Int) {
+ dismiss()
+ }
}
init {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
index bf3835dba4fd..6bf189744033 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
@@ -16,7 +16,13 @@
package com.android.systemui.controls.ui
+import android.app.AlertDialog
+import android.app.PendingIntent
+import android.content.DialogInterface
+import android.content.pm.PackageManager
import android.service.controls.Control
+import android.view.View
+import android.view.WindowManager
import com.android.systemui.R
@@ -31,7 +37,17 @@ class StatusBehavior : Behavior {
val status = cws.control?.status ?: Control.STATUS_UNKNOWN
val msg = when (status) {
Control.STATUS_ERROR -> R.string.controls_error_generic
- Control.STATUS_NOT_FOUND -> R.string.controls_error_removed
+ Control.STATUS_DISABLED -> R.string.controls_error_timeout
+ Control.STATUS_NOT_FOUND -> {
+ cvh.layout.setOnClickListener(View.OnClickListener() {
+ showNotFoundDialog(cvh, cws)
+ })
+ cvh.layout.setOnLongClickListener(View.OnLongClickListener() {
+ showNotFoundDialog(cvh, cws)
+ true
+ })
+ R.string.controls_error_removed
+ }
else -> {
cvh.isLoading = true
com.android.internal.R.string.loading
@@ -40,4 +56,42 @@ class StatusBehavior : Behavior {
cvh.setStatusText(cvh.context.getString(msg))
cvh.applyRenderInfo(false, colorOffset)
}
+
+ private fun showNotFoundDialog(cvh: ControlViewHolder, cws: ControlWithState) {
+ val pm = cvh.context.getPackageManager()
+ val ai = pm.getApplicationInfo(cws.componentName.packageName, PackageManager.GET_META_DATA)
+ val appLabel = pm.getApplicationLabel(ai)
+ val builder = AlertDialog.Builder(
+ cvh.context,
+ android.R.style.Theme_DeviceDefault_Dialog_Alert
+ ).apply {
+ val res = cvh.context.resources
+ setTitle(res.getString(R.string.controls_error_removed_title))
+ setMessage(res.getString(
+ R.string.controls_error_removed_message, cvh.title.getText(), appLabel))
+ setPositiveButton(
+ R.string.controls_open_app,
+ DialogInterface.OnClickListener { dialog, _ ->
+ try {
+ cws.control?.getAppIntent()?.send()
+ } catch (e: PendingIntent.CanceledException) {
+ cvh.setTransientStatus(
+ cvh.context.resources.getString(R.string.controls_error_failed))
+ }
+ dialog.dismiss()
+ })
+ setNegativeButton(
+ android.R.string.cancel,
+ DialogInterface.OnClickListener { dialog, _ ->
+ dialog.cancel()
+ }
+ )
+ }
+ cvh.visibleDialog = builder.create().apply {
+ getWindow().apply {
+ setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ show()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 1f0ca9be2ecc..4003f41b33dc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -231,9 +231,11 @@ class ToggleRangeBehavior : Behavior {
rangeAnimator?.cancel()
if (isDragging) {
- clipLayer.level = newLevel
val isEdge = newLevel == MIN_LEVEL || newLevel == MAX_LEVEL
- cvh.controlActionCoordinator.drag(isEdge)
+ if (clipLayer.level != newLevel) {
+ cvh.controlActionCoordinator.drag(isEdge)
+ clipLayer.level = newLevel
+ }
} else if (newLevel != clipLayer.level) {
rangeAnimator = ValueAnimator.ofInt(cvh.clipLayer.level, newLevel).apply {
addUpdateListener {
@@ -266,7 +268,7 @@ class ToggleRangeBehavior : Behavior {
private fun format(primaryFormat: String, backupFormat: String, value: Float): String {
return try {
- String.format(primaryFormat, value)
+ String.format(primaryFormat, findNearestStep(value))
} catch (e: IllegalFormatException) {
Log.w(ControlsUiController.TAG, "Illegal format in range template", e)
if (backupFormat == "") {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt
index c0f6aabe2427..29b7e985fabc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt
@@ -20,35 +20,9 @@ import android.os.VibrationEffect
import android.os.VibrationEffect.Composition.PRIMITIVE_TICK
object Vibrations {
- private const val TOGGLE_TICK_COUNT = 40
-
- val toggleOnEffect = initToggleOnEffect()
- val toggleOffEffect = initToggleOffEffect()
val rangeEdgeEffect = initRangeEdgeEffect()
val rangeMiddleEffect = initRangeMiddleEffect()
- private fun initToggleOnEffect(): VibrationEffect {
- val composition = VibrationEffect.startComposition()
- composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 200)
- var i = 0
- while (i++ < TOGGLE_TICK_COUNT) {
- composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 0)
- }
- composition.addPrimitive(PRIMITIVE_TICK, 0.5f, 100)
- return composition.compose()
- }
-
- private fun initToggleOffEffect(): VibrationEffect {
- val composition = VibrationEffect.startComposition()
- composition.addPrimitive(PRIMITIVE_TICK, 0.5f, 0)
- composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 100)
- var i = 0
- while (i++ < TOGGLE_TICK_COUNT) {
- composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 0)
- }
- return composition.compose()
- }
-
private fun initRangeEdgeEffect(): VibrationEffect {
val composition = VibrationEffect.startComposition()
composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 95a9006c854a..951dc9936e1c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -71,7 +71,7 @@ public class DozeFactory {
DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
ProximitySensor proximitySensor,
DelayedWakeLock.Builder delayedWakeLockBuilder, @Main Handler handler,
- DelayableExecutor delayableExecutor,
+ @Main DelayableExecutor delayableExecutor,
BiometricUnlockController biometricUnlockController,
BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) {
mFalsingManager = falsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 1b13d4a49fec..7e009b459ede 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -183,8 +183,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
- private static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted";
- private static final String PREFS_CONTROLS_FILE = "controls_prefs";
+ public static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted";
+ public static final String PREFS_CONTROLS_FILE = "controls_prefs";
private static final int SEEDING_MAX = 2;
private final Context mContext;
@@ -391,7 +391,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
if (controlsComponent.getControlsListingController().isPresent()) {
controlsComponent.getControlsListingController().get()
- .addCallback(list -> mControlsServiceInfos = list);
+ .addCallback(list -> {
+ mControlsServiceInfos = list;
+ // This callback may occur after the dialog has been shown.
+ // If so, add controls into the already visible space
+ if (mDialog != null && !mDialog.isShowingControls()
+ && shouldShowControls()) {
+ mDialog.showControls(mControlsUiControllerOptional.get());
+ }
+ });
}
// Need to be user-specific with the context to make sure we read the correct prefs
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 5595201a670f..b8c1842a1780 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -17,12 +17,8 @@
package com.android.systemui.media;
import android.app.PendingIntent;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -35,7 +31,6 @@ import android.graphics.drawable.RippleDrawable;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.service.media.MediaBrowserService;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
@@ -55,7 +50,6 @@ import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.qs.QSMediaBrowser;
import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -81,7 +75,6 @@ public class MediaControlPanel {
private final SeekBarViewModel mSeekBarViewModel;
private SeekBarObserver mSeekBarObserver;
- private final Executor mForegroundExecutor;
protected final Executor mBackgroundExecutor;
private final ActivityStarter mActivityStarter;
@@ -91,48 +84,18 @@ public class MediaControlPanel {
private MediaSession.Token mToken;
private MediaController mController;
private int mBackgroundColor;
- protected ComponentName mServiceComponent;
- private boolean mIsRegistered = false;
- private String mKey;
private int mAlbumArtSize;
private int mAlbumArtRadius;
- private int mViewWidth;
-
- public static final String MEDIA_PREFERENCES = "media_control_prefs";
- public static final String MEDIA_PREFERENCE_KEY = "browser_components";
- private SharedPreferences mSharedPrefs;
- private boolean mCheckedForResumption = false;
- private QSMediaBrowser mQSMediaBrowser;
-
- private final MediaController.Callback mSessionCallback = new MediaController.Callback() {
- @Override
- public void onSessionDestroyed() {
- Log.d(TAG, "session destroyed");
- mController.unregisterCallback(mSessionCallback);
- clearControls();
- }
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- final int s = state != null ? state.getState() : PlaybackState.STATE_NONE;
- if (s == PlaybackState.STATE_NONE) {
- Log.d(TAG, "playback state change will trigger resumption, state=" + state);
- clearControls();
- }
- }
- };
/**
* Initialize a new control panel
* @param context
- * @param foregroundExecutor foreground executor
* @param backgroundExecutor background executor, used for processing artwork
* @param activityStarter activity starter
*/
- public MediaControlPanel(Context context, Executor foregroundExecutor,
- DelayableExecutor backgroundExecutor, ActivityStarter activityStarter,
- MediaHostStatesManager mediaHostStatesManager) {
+ public MediaControlPanel(Context context, DelayableExecutor backgroundExecutor,
+ ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager) {
mContext = context;
- mForegroundExecutor = foregroundExecutor;
mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter;
mSeekBarViewModel = new SeekBarViewModel(backgroundExecutor);
@@ -214,45 +177,18 @@ public class MediaControlPanel {
MediaSession.Token token = data.getToken();
mBackgroundColor = data.getBackgroundColor();
if (mToken == null || !mToken.equals(token)) {
- if (mQSMediaBrowser != null) {
- Log.d(TAG, "Disconnecting old media browser");
- mQSMediaBrowser.disconnect();
- mQSMediaBrowser = null;
- }
mToken = token;
- mServiceComponent = null;
- mCheckedForResumption = false;
}
- mController = new MediaController(mContext, mToken);
+ if (mToken != null) {
+ mController = new MediaController(mContext, mToken);
+ } else {
+ mController = null;
+ }
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
- // Try to find a browser service component for this app
- // TODO also check for a media button receiver intended for restarting (b/154127084)
- // Only check if we haven't tried yet or the session token changed
- final String pkgName = data.getPackageName();
- if (mServiceComponent == null && !mCheckedForResumption) {
- Log.d(TAG, "Checking for service component");
- PackageManager pm = mContext.getPackageManager();
- Intent resumeIntent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
- List<ResolveInfo> resumeInfo = pm.queryIntentServices(resumeIntent, 0);
- // TODO: look into this resumption
- if (resumeInfo != null) {
- for (ResolveInfo inf : resumeInfo) {
- if (inf.serviceInfo.packageName.equals(mController.getPackageName())) {
- mBackgroundExecutor.execute(() ->
- tryUpdateResumptionList(inf.getComponentInfo().getComponentName()));
- break;
- }
- }
- }
- mCheckedForResumption = true;
- }
-
- mController.registerCallback(mSessionCallback);
-
mViewHolder.getPlayer().setBackgroundTintList(
ColorStateList.valueOf(mBackgroundColor));
@@ -267,12 +203,22 @@ public class MediaControlPanel {
ImageView albumView = mViewHolder.getAlbumView();
// TODO: migrate this to a view with rounded corners instead of baking the rounding
// into the bitmap
- Drawable artwork = createRoundedBitmap(data.getArtwork());
- albumView.setImageDrawable(artwork);
+ boolean hasArtwork = data.getArtwork() != null;
+ if (hasArtwork) {
+ Drawable artwork = createRoundedBitmap(data.getArtwork());
+ albumView.setImageDrawable(artwork);
+ }
+ setVisibleAndAlpha(collapsedSet, R.id.album_art, hasArtwork);
+ setVisibleAndAlpha(expandedSet, R.id.album_art, hasArtwork);
// App icon
ImageView appIcon = mViewHolder.getAppIcon();
- appIcon.setImageDrawable(data.getAppIcon());
+ if (data.getAppIcon() != null) {
+ appIcon.setImageDrawable(data.getAppIcon());
+ } else {
+ Drawable iconDrawable = mContext.getDrawable(R.drawable.ic_music_note);
+ appIcon.setImageDrawable(iconDrawable);
+ }
// Song name
TextView titleText = mViewHolder.getTitleText();
@@ -294,7 +240,7 @@ public class MediaControlPanel {
final Intent intent = new Intent()
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
- mController.getPackageName())
+ data.getPackageName())
.putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken);
mActivityStarter.startActivity(intent, false, true /* dismissShade */,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -350,15 +296,11 @@ public class MediaControlPanel {
MediaAction mediaAction = actionIcons.get(i);
button.setImageDrawable(mediaAction.getDrawable());
button.setContentDescription(mediaAction.getContentDescription());
- PendingIntent actionIntent = mediaAction.getIntent();
+ Runnable action = mediaAction.getAction();
button.setOnClickListener(v -> {
- if (actionIntent != null) {
- try {
- actionIntent.send();
- } catch (PendingIntent.CanceledException e) {
- e.printStackTrace();
- }
+ if (action != null) {
+ action.run();
}
});
boolean visibleInCompat = actionsWhenCollapsed.contains(i);
@@ -444,14 +386,6 @@ public class MediaControlPanel {
}
/**
- * Return the original notification's key
- * @return The notification key
- */
- public String getKey() {
- return mKey;
- }
-
- /**
* Check whether this player has an attached media session.
* @return whether there is a controller with a current media session.
*/
@@ -485,150 +419,8 @@ public class MediaControlPanel {
return (state.getState() == PlaybackState.STATE_PLAYING);
}
- /**
- * Puts controls into a resumption state if possible, or calls removePlayer if no component was
- * found that could resume playback
- */
- public void clearControls() {
- Log.d(TAG, "clearControls to resumption state package=" + getMediaPlayerPackage());
- if (mServiceComponent == null) {
- // If we don't have a way to resume, just remove the player altogether
- Log.d(TAG, "Removing unresumable controls");
- removePlayer();
- return;
- }
- resetButtons();
- }
-
- /**
- * Hide the media buttons and show only a restart button
- */
- protected void resetButtons() {
- if (mViewHolder == null) {
- return;
- }
- // Hide all the old buttons
-
- ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
- for (int i = 1; i < ACTION_IDS.length; i++) {
- setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /*visible */);
- setVisibleAndAlpha(collapsedSet, ACTION_IDS[i], false /*visible */);
- }
-
- // Add a restart button
- ImageButton btn = mViewHolder.getAction0();
- btn.setOnClickListener(v -> {
- Log.d(TAG, "Attempting to restart session");
- if (mQSMediaBrowser != null) {
- mQSMediaBrowser.disconnect();
- }
- mQSMediaBrowser = new QSMediaBrowser(mContext, new QSMediaBrowser.Callback(){
- @Override
- public void onConnected() {
- Log.d(TAG, "Successfully restarted");
- }
- @Override
- public void onError() {
- Log.e(TAG, "Error restarting");
- mQSMediaBrowser.disconnect();
- mQSMediaBrowser = null;
- }
- }, mServiceComponent);
- mQSMediaBrowser.restart();
- });
- btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
- setVisibleAndAlpha(expandedSet, ACTION_IDS[0], true /*visible */);
- setVisibleAndAlpha(collapsedSet, ACTION_IDS[0], true /*visible */);
-
- mSeekBarViewModel.clearController();
- // TODO: fix guts
- // View guts = mMediaNotifView.findViewById(R.id.media_guts);
- View options = mViewHolder.getOptions();
-
- mViewHolder.getPlayer().setOnLongClickListener(v -> {
- // Replace player view with close/cancel view
-// guts.setVisibility(View.GONE);
- options.setVisibility(View.VISIBLE);
- return true; // consumed click
- });
- mMediaViewController.refreshState();
- }
-
private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
set.setVisibility(actionId, visible? ConstraintSet.VISIBLE : ConstraintSet.GONE);
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
}
-
- /**
- * Verify that we can connect to the given component with a MediaBrowser, and if so, add that
- * component to the list of resumption components
- */
- private void tryUpdateResumptionList(ComponentName componentName) {
- Log.d(TAG, "Testing if we can connect to " + componentName);
- if (mQSMediaBrowser != null) {
- mQSMediaBrowser.disconnect();
- }
- mQSMediaBrowser = new QSMediaBrowser(mContext,
- new QSMediaBrowser.Callback() {
- @Override
- public void onConnected() {
- Log.d(TAG, "yes we can resume with " + componentName);
- mServiceComponent = componentName;
- updateResumptionList(componentName);
- mQSMediaBrowser.disconnect();
- mQSMediaBrowser = null;
- }
-
- @Override
- public void onError() {
- Log.d(TAG, "Cannot resume with " + componentName);
- mServiceComponent = null;
- if (!hasMediaSession()) {
- // If it's not active and we can't resume, remove
- removePlayer();
- }
- mQSMediaBrowser.disconnect();
- mQSMediaBrowser = null;
- }
- },
- componentName);
- mQSMediaBrowser.testConnection();
- }
-
- /**
- * Add the component to the saved list of media browser services, checking for duplicates and
- * removing older components that exceed the maximum limit
- * @param componentName
- */
- private synchronized void updateResumptionList(ComponentName componentName) {
- // Add to front of saved list
- if (mSharedPrefs == null) {
- mSharedPrefs = mContext.getSharedPreferences(MEDIA_PREFERENCES, 0);
- }
- String componentString = componentName.flattenToString();
- String listString = mSharedPrefs.getString(MEDIA_PREFERENCE_KEY, null);
- if (listString == null) {
- listString = componentString;
- } else {
- String[] components = listString.split(QSMediaBrowser.DELIMITER);
- StringBuilder updated = new StringBuilder(componentString);
- int nBrowsers = 1;
- for (int i = 0; i < components.length
- && nBrowsers < QSMediaBrowser.MAX_RESUMPTION_CONTROLS; i++) {
- if (componentString.equals(components[i])) {
- continue;
- }
- updated.append(QSMediaBrowser.DELIMITER).append(components[i]);
- nBrowsers++;
- }
- listString = updated.toString();
- }
- mSharedPrefs.edit().putString(MEDIA_PREFERENCE_KEY, listString).apply();
- }
-
- /**
- * Called when a player can't be resumed to give it an opportunity to hide or remove itself
- */
- protected void removePlayer() { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index a94f6a87d58a..5d28178a3b1b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -32,17 +32,19 @@ data class MediaData(
val artwork: Icon?,
val actions: List<MediaAction>,
val actionsToShowInCompact: List<Int>,
- val packageName: String?,
+ val packageName: String,
val token: MediaSession.Token?,
val clickIntent: PendingIntent?,
val device: MediaDeviceData?,
- val notificationKey: String = "INVALID"
+ var resumeAction: Runnable?,
+ val notificationKey: String = "INVALID",
+ var hasCheckedForResume: Boolean = false
)
/** State of a media action. */
data class MediaAction(
val drawable: Drawable?,
- val intent: PendingIntent?,
+ val action: Runnable?,
val contentDescription: CharSequence?
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index cce9838bb8e2..11cbc482459a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -32,9 +32,15 @@ class MediaDataCombineLatest @Inject constructor(
init {
dataSource.addListener(object : MediaDataManager.Listener {
- override fun onMediaDataLoaded(key: String, data: MediaData) {
- entries[key] = data to entries[key]?.second
- update(key)
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ if (oldKey != null && !oldKey.equals(key)) {
+ val s = entries[oldKey]?.second
+ entries[key] = data to entries[oldKey]?.second
+ entries.remove(oldKey)
+ } else {
+ entries[key] = data to entries[key]?.second
+ }
+ update(key, oldKey)
}
override fun onMediaDataRemoved(key: String) {
remove(key)
@@ -43,7 +49,7 @@ class MediaDataCombineLatest @Inject constructor(
deviceSource.addListener(object : MediaDeviceManager.Listener {
override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) {
entries[key] = entries[key]?.first to data
- update(key)
+ update(key, key)
}
override fun onKeyRemoved(key: String) {
remove(key)
@@ -61,19 +67,21 @@ class MediaDataCombineLatest @Inject constructor(
*/
fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener)
- private fun update(key: String) {
+ private fun update(key: String, oldKey: String?) {
val (entry, device) = entries[key] ?: null to null
if (entry != null && device != null) {
val data = entry.copy(device = device)
- listeners.forEach {
- it.onMediaDataLoaded(key, data)
+ val listenersCopy = listeners.toSet()
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(key, oldKey, data)
}
}
}
private fun remove(key: String) {
entries.remove(key)?.let {
- listeners.forEach {
+ val listenersCopy = listeners.toSet()
+ listenersCopy.forEach {
it.onMediaDataRemoved(key)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index d94985703083..094c5bef3c18 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media
import android.app.Notification
+import android.app.PendingIntent
import android.content.ContentResolver
import android.content.Context
import android.graphics.Bitmap
@@ -25,6 +26,7 @@ import android.graphics.Color
import android.graphics.ImageDecoder
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
+import android.media.MediaDescription
import android.media.MediaMetadata
import android.media.session.MediaSession
import android.net.Uri
@@ -32,8 +34,10 @@ import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
import com.android.internal.graphics.ColorUtils
+import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON
@@ -58,7 +62,7 @@ private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
private val LOADING = MediaData(false, 0, null, null, null, null, null,
- emptyList(), emptyList(), null, null, null, null)
+ emptyList(), emptyList(), "INVALID", null, null, null, null)
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
if (!sbn.notification.hasMediaSession()) {
@@ -81,34 +85,92 @@ class MediaDataManager @Inject constructor(
private val mediaControllerFactory: MediaControllerFactory,
private val mediaTimeoutListener: MediaTimeoutListener,
private val notificationEntryManager: NotificationEntryManager,
+ private val mediaResumeListener: MediaResumeListener,
@Background private val backgroundExecutor: Executor,
@Main private val foregroundExecutor: Executor
) {
private val listeners: MutableSet<Listener> = mutableSetOf()
private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
+ private val useMediaResumption: Boolean = Utils.useMediaResumption(context)
init {
mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
setTimedOut(token, timedOut) }
addListener(mediaTimeoutListener)
+
+ if (useMediaResumption) {
+ mediaResumeListener.addTrackToResumeCallback = { desc: MediaDescription,
+ resumeAction: Runnable, token: MediaSession.Token, appName: String,
+ appIntent: PendingIntent, packageName: String ->
+ addResumptionControls(desc, resumeAction, token, appName, appIntent, packageName)
+ }
+ mediaResumeListener.resumeComponentFoundCallback = { key: String, action: Runnable? ->
+ mediaEntries.get(key)?.resumeAction = action
+ mediaEntries.get(key)?.hasCheckedForResume = true
+ }
+ addListener(mediaResumeListener)
+ }
}
fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) {
Assert.isMainThread()
- if (!mediaEntries.containsKey(key)) {
- mediaEntries.put(key, LOADING)
+ val oldKey = findExistingEntry(key, sbn.packageName)
+ if (oldKey == null) {
+ val temp = LOADING.copy(packageName = sbn.packageName)
+ mediaEntries.put(key, temp)
+ } else if (oldKey != key) {
+ // Move to new key
+ val oldData = mediaEntries.remove(oldKey)!!
+ mediaEntries.put(key, oldData)
}
- loadMediaData(key, sbn)
+ loadMediaData(key, sbn, oldKey)
} else {
onNotificationRemoved(key)
}
}
- private fun loadMediaData(key: String, sbn: StatusBarNotification) {
+ private fun addResumptionControls(
+ desc: MediaDescription,
+ action: Runnable,
+ token: MediaSession.Token,
+ appName: String,
+ appIntent: PendingIntent,
+ packageName: String
+ ) {
+ // Resume controls don't have a notification key, so store by package name instead
+ if (!mediaEntries.containsKey(packageName)) {
+ val resumeData = LOADING.copy(packageName = packageName, resumeAction = action)
+ mediaEntries.put(packageName, resumeData)
+ }
backgroundExecutor.execute {
- loadMediaDataInBg(key, sbn)
+ loadMediaDataInBg(desc, action, token, appName, appIntent, packageName)
+ }
+ }
+
+ /**
+ * Check if there is an existing entry that matches the key or package name.
+ * Returns the key that matches, or null if not found.
+ */
+ private fun findExistingEntry(key: String, packageName: String): String? {
+ if (mediaEntries.containsKey(key)) {
+ return key
+ }
+ // Check if we already had a resume player
+ if (mediaEntries.containsKey(packageName)) {
+ return packageName
+ }
+ return null
+ }
+
+ private fun loadMediaData(
+ key: String,
+ sbn: StatusBarNotification,
+ oldKey: String?
+ ) {
+ backgroundExecutor.execute {
+ loadMediaDataInBg(key, sbn, oldKey)
}
}
@@ -132,7 +194,50 @@ class MediaDataManager @Inject constructor(
}
}
- private fun loadMediaDataInBg(key: String, sbn: StatusBarNotification) {
+ private fun loadMediaDataInBg(
+ desc: MediaDescription,
+ resumeAction: Runnable,
+ token: MediaSession.Token,
+ appName: String,
+ appIntent: PendingIntent,
+ packageName: String
+ ) {
+ if (resumeAction == null) {
+ Log.e(TAG, "Resume action cannot be null")
+ return
+ }
+
+ if (TextUtils.isEmpty(desc.title)) {
+ Log.e(TAG, "Description incomplete")
+ return
+ }
+
+ Log.d(TAG, "adding track from browser: $desc")
+
+ // Album art
+ var artworkBitmap = desc.iconBitmap
+ if (artworkBitmap == null && desc.iconUri != null) {
+ artworkBitmap = loadBitmapFromUri(desc.iconUri!!)
+ }
+ val artworkIcon = if (artworkBitmap != null) {
+ Icon.createWithBitmap(artworkBitmap)
+ } else {
+ null
+ }
+
+ val mediaAction = getResumeMediaAction(resumeAction)
+ foregroundExecutor.execute {
+ onMediaDataLoaded(packageName, null, MediaData(true, Color.DKGRAY, appName,
+ null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
+ packageName, token, appIntent, null, resumeAction, packageName))
+ }
+ }
+
+ private fun loadMediaDataInBg(
+ key: String,
+ sbn: StatusBarNotification,
+ oldKey: String?
+ ) {
val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION)
as MediaSession.Token?
val metadata = mediaControllerFactory.create(token).metadata
@@ -234,16 +339,23 @@ class MediaDataManager @Inject constructor(
}
val mediaAction = MediaAction(
action.getIcon().loadDrawable(packageContext),
- action.actionIntent,
+ Runnable {
+ try {
+ action.actionIntent.send()
+ } catch (e: PendingIntent.CanceledException) {
+ Log.d(TAG, "Intent canceled", e)
+ }
+ },
action.title)
actionIcons.add(mediaAction)
}
}
+ val resumeAction: Runnable? = mediaEntries.get(key)?.resumeAction
foregroundExecutor.execute {
- onMediaDataLoaded(key, MediaData(true, bgColor, app, smallIconDrawable, artist, song,
- artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
- notif.contentIntent, null, key))
+ onMediaDataLoaded(key, oldKey, MediaData(true, bgColor, app, smallIconDrawable, artist,
+ song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
+ notif.contentIntent, null, resumeAction, key))
}
}
@@ -257,7 +369,7 @@ class MediaDataManager @Inject constructor(
val albumArt = loadBitmapFromUri(Uri.parse(uriString))
if (albumArt != null) {
Log.d(TAG, "loaded art from $uri")
- break
+ return albumArt
}
}
}
@@ -283,27 +395,52 @@ class MediaDataManager @Inject constructor(
val source = ImageDecoder.createSource(context.getContentResolver(), uri)
return try {
- ImageDecoder.decodeBitmap(source)
+ ImageDecoder.decodeBitmap(source) {
+ decoder, info, source -> decoder.isMutableRequired = true
+ }
} catch (e: IOException) {
e.printStackTrace()
null
}
}
- fun onMediaDataLoaded(key: String, data: MediaData) {
+ private fun getResumeMediaAction(action: Runnable): MediaAction {
+ return MediaAction(
+ context.getDrawable(R.drawable.lb_ic_play),
+ action,
+ context.getString(R.string.controls_media_resume)
+ )
+ }
+
+ fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
Assert.isMainThread()
if (mediaEntries.containsKey(key)) {
// Otherwise this was removed already
mediaEntries.put(key, data)
val listenersCopy = listeners.toSet()
listenersCopy.forEach {
- it.onMediaDataLoaded(key, data)
+ it.onMediaDataLoaded(key, oldKey, data)
}
}
}
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
+ if (useMediaResumption && mediaEntries.get(key)?.resumeAction != null) {
+ Log.d(TAG, "Not removing $key because resumable")
+ // Move to resume key aka package name
+ val data = mediaEntries.remove(key)!!
+ val resumeAction = getResumeMediaAction(data.resumeAction!!)
+ val updated = data.copy(token = null, actions = listOf(resumeAction),
+ actionsToShowInCompact = listOf(0))
+ mediaEntries.put(data.packageName, updated)
+ // Notify listeners of "new" controls
+ val listenersCopy = listeners.toSet()
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(data.packageName, key, updated)
+ }
+ return
+ }
val removed = mediaEntries.remove(key)
if (removed != null) {
val listenersCopy = listeners.toSet()
@@ -316,19 +453,32 @@ class MediaDataManager @Inject constructor(
/**
* Are there any media notifications active?
*/
- fun hasActiveMedia() = mediaEntries.isNotEmpty()
+ fun hasActiveMedia() = mediaEntries.any({ isActive(it.value) })
- fun hasAnyMedia(): Boolean {
- // TODO: implement this when we implemented resumption
- return hasActiveMedia()
+ fun isActive(data: MediaData): Boolean {
+ if (data.token == null) {
+ return false
+ }
+ val controller = mediaControllerFactory.create(data.token)
+ val state = controller?.playbackState?.state
+ return state != null && NotificationMediaManager.isActiveState(state)
}
+ /**
+ * Are there any media entries, including resume controls?
+ */
+ fun hasAnyMedia() = mediaEntries.isNotEmpty()
+
interface Listener {
/**
- * Called whenever there's new MediaData Loaded for the consumption in views
+ * Called whenever there's new MediaData Loaded for the consumption in views.
+ *
+ * oldKey is provided to check whether the view has changed keys, which can happen when a
+ * player has gone from resume state (key is package name) to active state (key is
+ * notification key) or vice versa.
*/
- fun onMediaDataLoaded(key: String, data: MediaData) {}
+ fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {}
/**
* Called whenever a previously existing Media notification was removed
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 552fea63a278..2f521ea39242 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -16,11 +16,8 @@
package com.android.systemui.media
-import android.app.Notification
import android.content.Context
-import android.service.notification.StatusBarNotification
import android.media.MediaRouter2Manager
-import android.media.session.MediaSession
import android.media.session.MediaController
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
@@ -38,11 +35,16 @@ class MediaDeviceManager @Inject constructor(
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
private val featureFlag: MediaFeatureFlag,
- @Main private val fgExecutor: Executor
-) {
+ @Main private val fgExecutor: Executor,
+ private val mediaDataManager: MediaDataManager
+) : MediaDataManager.Listener {
private val listeners: MutableSet<Listener> = mutableSetOf()
private val entries: MutableMap<String, Token> = mutableMapOf()
+ init {
+ mediaDataManager.addListener(this)
+ }
+
/**
* Add a listener for changes to the media route (ie. device).
*/
@@ -53,23 +55,25 @@ class MediaDeviceManager @Inject constructor(
*/
fun removeListener(listener: Listener) = listeners.remove(listener)
- fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
- if (featureFlag.enabled && isMediaNotification(sbn)) {
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ if (featureFlag.enabled) {
+ if (oldKey != null && oldKey != key) {
+ val oldToken = entries.remove(oldKey)
+ oldToken?.stop()
+ }
var tok = entries[key]
- if (tok == null) {
- val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION)
- as MediaSession.Token?
- val controller = MediaController(context, token)
- tok = Token(key, controller, localMediaManagerFactory.create(sbn.packageName))
+ if (tok == null && data.token != null) {
+ val controller = MediaController(context, data.token!!)
+ tok = Token(key, controller, localMediaManagerFactory.create(data.packageName))
entries[key] = tok
tok.start()
}
} else {
- onNotificationRemoved(key)
+ onMediaDataRemoved(key)
}
}
- fun onNotificationRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String) {
val token = entries.remove(key)
token?.stop()
token?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index e904e935b0e0..2bd8c0cbeab2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -50,7 +50,7 @@ class MediaHost @Inject constructor(
}
private val listener = object : MediaDataManager.Listener {
- override fun onMediaDataLoaded(key: String, data: MediaData) {
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
updateViewVisibility()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
new file mode 100644
index 000000000000..6bbe0d1651dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -0,0 +1,252 @@
+/*
+ * 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
+
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.media.MediaDescription
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.os.UserHandle
+import android.service.media.MediaBrowserService
+import android.util.Log
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Utils
+import java.util.concurrent.ConcurrentLinkedQueue
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private const val TAG = "MediaResumeListener"
+
+private const val MEDIA_PREFERENCES = "media_control_prefs"
+private const val MEDIA_PREFERENCE_KEY = "browser_components"
+
+@Singleton
+class MediaResumeListener @Inject constructor(
+ private val context: Context,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ @Background private val backgroundExecutor: Executor
+) : MediaDataManager.Listener {
+
+ private val useMediaResumption: Boolean = Utils.useMediaResumption(context)
+ private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue()
+
+ lateinit var addTrackToResumeCallback: (
+ MediaDescription,
+ Runnable,
+ MediaSession.Token,
+ String,
+ PendingIntent,
+ String
+ ) -> Unit
+ lateinit var resumeComponentFoundCallback: (String, Runnable?) -> Unit
+
+ private var mediaBrowser: ResumeMediaBrowser? = null
+
+ private val unlockReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (Intent.ACTION_USER_UNLOCKED == intent.action) {
+ loadMediaResumptionControls()
+ }
+ }
+ }
+
+ private val mediaBrowserCallback = object : ResumeMediaBrowser.Callback() {
+ override fun addTrack(
+ desc: MediaDescription,
+ component: ComponentName,
+ browser: ResumeMediaBrowser
+ ) {
+ val token = browser.token
+ val appIntent = browser.appIntent
+ val pm = context.getPackageManager()
+ var appName: CharSequence = component.packageName
+ val resumeAction = getResumeAction(component)
+ try {
+ appName = pm.getApplicationLabel(
+ pm.getApplicationInfo(component.packageName, 0))
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Error getting package information", e)
+ }
+
+ Log.d(TAG, "Adding resume controls $desc")
+ addTrackToResumeCallback(desc, resumeAction, token, appName.toString(), appIntent,
+ component.packageName)
+ }
+ }
+
+ init {
+ if (useMediaResumption) {
+ val unlockFilter = IntentFilter()
+ unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
+ broadcastDispatcher.registerReceiver(unlockReceiver, unlockFilter, null, UserHandle.ALL)
+ loadSavedComponents()
+ }
+ }
+
+ private fun loadSavedComponents() {
+ val userContext = context.createContextAsUser(context.getUser(), 0)
+ val prefs = userContext.getSharedPreferences(MEDIA_PREFERENCES, Context.MODE_PRIVATE)
+ val listString = prefs.getString(MEDIA_PREFERENCE_KEY, null)
+ val components = listString?.split(ResumeMediaBrowser.DELIMITER.toRegex())
+ ?.dropLastWhile { it.isEmpty() }
+ components?.forEach {
+ val info = it.split("/")
+ val packageName = info[0]
+ val className = info[1]
+ val component = ComponentName(packageName, className)
+ resumeComponents.add(component)
+ }
+ Log.d(TAG, "loaded resume components ${resumeComponents.toArray().contentToString()}")
+ }
+
+ /**
+ * Load controls for resuming media, if available
+ */
+ private fun loadMediaResumptionControls() {
+ if (!useMediaResumption) {
+ return
+ }
+
+ resumeComponents.forEach {
+ val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it)
+ browser.findRecentMedia()
+ }
+ broadcastDispatcher.unregisterReceiver(unlockReceiver) // only need to load once
+ }
+
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ if (useMediaResumption) {
+ // If this had been started from a resume state, disconnect now that it's live
+ mediaBrowser?.disconnect()
+ // If we don't have a resume action, check if we haven't already
+ if (data.resumeAction == null && !data.hasCheckedForResume) {
+ // TODO also check for a media button receiver intended for restarting (b/154127084)
+ Log.d(TAG, "Checking for service component for " + data.packageName)
+ val pm = context.packageManager
+ val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE)
+ val resumeInfo = pm.queryIntentServices(serviceIntent, 0)
+
+ val inf = resumeInfo?.filter {
+ it.serviceInfo.packageName == data.packageName
+ }
+ if (inf != null && inf.size > 0) {
+ backgroundExecutor.execute {
+ tryUpdateResumptionList(key, inf!!.get(0).componentInfo.componentName)
+ }
+ } else {
+ // No service found
+ resumeComponentFoundCallback(key, null)
+ }
+ }
+ }
+ }
+
+ /**
+ * Verify that we can connect to the given component with a MediaBrowser, and if so, add that
+ * component to the list of resumption components
+ */
+ private fun tryUpdateResumptionList(key: String, componentName: ComponentName) {
+ Log.d(TAG, "Testing if we can connect to $componentName")
+ mediaBrowser?.disconnect()
+ mediaBrowser = ResumeMediaBrowser(context,
+ object : ResumeMediaBrowser.Callback() {
+ override fun onConnected() {
+ Log.d(TAG, "yes we can resume with $componentName")
+ resumeComponentFoundCallback(key, getResumeAction(componentName))
+ updateResumptionList(componentName)
+ mediaBrowser?.disconnect()
+ mediaBrowser = null
+ }
+
+ override fun onError() {
+ Log.e(TAG, "Cannot resume with $componentName")
+ resumeComponentFoundCallback(key, null)
+ mediaBrowser?.disconnect()
+ mediaBrowser = null
+ }
+ },
+ componentName)
+ mediaBrowser?.testConnection()
+ }
+
+ /**
+ * Add the component to the saved list of media browser services, checking for duplicates and
+ * removing older components that exceed the maximum limit
+ * @param componentName
+ */
+ private fun updateResumptionList(componentName: ComponentName) {
+ // Remove if exists
+ resumeComponents.remove(componentName)
+ // Insert at front of queue
+ resumeComponents.add(componentName)
+ // Remove old components if over the limit
+ if (resumeComponents.size > ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS) {
+ resumeComponents.remove()
+ }
+
+ // Save changes
+ val sb = StringBuilder()
+ resumeComponents.forEach {
+ sb.append(it.flattenToString())
+ sb.append(ResumeMediaBrowser.DELIMITER)
+ }
+ val userContext = context.createContextAsUser(context.getUser(), 0)
+ val prefs = userContext.getSharedPreferences(MEDIA_PREFERENCES, Context.MODE_PRIVATE)
+ prefs.edit().putString(MEDIA_PREFERENCE_KEY, sb.toString()).apply()
+ }
+
+ /**
+ * Get a runnable which will resume media playback
+ */
+ private fun getResumeAction(componentName: ComponentName): Runnable {
+ return Runnable {
+ mediaBrowser?.disconnect()
+ mediaBrowser = ResumeMediaBrowser(context,
+ object : ResumeMediaBrowser.Callback() {
+ override fun onConnected() {
+ if (mediaBrowser?.token == null) {
+ Log.e(TAG, "Error after connect")
+ mediaBrowser?.disconnect()
+ mediaBrowser = null
+ return
+ }
+ Log.d(TAG, "Connected for restart $componentName")
+ val controller = MediaController(context, mediaBrowser!!.token)
+ val controls = controller.transportControls
+ controls.prepare()
+ controls.play()
+ }
+
+ override fun onError() {
+ Log.e(TAG, "Resume failed for $componentName")
+ mediaBrowser?.disconnect()
+ mediaBrowser = null
+ }
+ },
+ componentName)
+ mediaBrowser?.restart()
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 92a1ab1b1871..359c2f5e297c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -45,7 +45,7 @@ class MediaTimeoutListener @Inject constructor(
lateinit var timeoutCallback: (String, Boolean) -> Unit
- override fun onMediaDataLoaded(key: String, data: MediaData) {
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
if (mediaListeners.containsKey(key)) {
return
}
@@ -67,15 +67,20 @@ class MediaTimeoutListener @Inject constructor(
var timedOut = false
- private val mediaController = mediaControllerFactory.create(data.token)
+ // Resume controls may have null token
+ private val mediaController = if (data.token != null) {
+ mediaControllerFactory.create(data.token)
+ } else {
+ null
+ }
private var cancellation: Runnable? = null
init {
- mediaController.registerCallback(this)
+ mediaController?.registerCallback(this)
}
fun destroy() {
- mediaController.unregisterCallback(this)
+ mediaController?.unregisterCallback(this)
}
override fun onPlaybackStateChanged(state: PlaybackState?) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
index 8ab30c75c7eb..3557b04a57bc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
@@ -12,14 +12,12 @@ import android.widget.LinearLayout
import androidx.core.view.GestureDetectorCompat
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.PageIndicator
import com.android.systemui.statusbar.notification.VisualStabilityManager
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.animation.requiresRemeasuring
import com.android.systemui.util.concurrency.DelayableExecutor
-import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
@@ -32,7 +30,6 @@ private const val FLING_SLOP = 1000000
@Singleton
class MediaViewManager @Inject constructor(
private val context: Context,
- @Main private val foregroundExecutor: Executor,
@Background private val backgroundExecutor: DelayableExecutor,
private val visualStabilityManager: VisualStabilityManager,
private val activityStarter: ActivityStarter,
@@ -147,8 +144,8 @@ class MediaViewManager @Inject constructor(
visualStabilityManager.addReorderingAllowedCallback(visualStabilityCallback,
true /* persistent */)
mediaManager.addListener(object : MediaDataManager.Listener {
- override fun onMediaDataLoaded(key: String, data: MediaData) {
- updateView(key, data)
+ override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ updateView(key, oldKey, data)
updatePlayerVisibilities()
mediaCarousel.requiresRemeasuring = true
}
@@ -259,11 +256,17 @@ class MediaViewManager @Inject constructor(
}
}
- private fun updateView(key: String, data: MediaData) {
+ private fun updateView(key: String, oldKey: String?, data: MediaData) {
+ // If the key was changed, update entry
+ val oldData = mediaPlayers[oldKey]
+ if (oldData != null) {
+ val oldData = mediaPlayers.remove(oldKey)
+ mediaPlayers.put(key, oldData!!)
+ }
var existingPlayer = mediaPlayers[key]
if (existingPlayer == null) {
- existingPlayer = MediaControlPanel(context, foregroundExecutor, backgroundExecutor,
- activityStarter, mediaHostStatesManager)
+ existingPlayer = MediaControlPanel(context, backgroundExecutor, activityStarter,
+ mediaHostStatesManager)
existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context),
mediaContent))
mediaPlayers[key] = existingPlayer
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index a5b73dcbd289..1e9a30364607 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.media;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -27,14 +27,17 @@ import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Bundle;
import android.service.media.MediaBrowserService;
+import android.text.TextUtils;
import android.util.Log;
+import com.android.systemui.util.Utils;
+
import java.util.List;
/**
- * Media browser for managing resumption in QS media controls
+ * Media browser for managing resumption in media controls
*/
-public class QSMediaBrowser {
+public class ResumeMediaBrowser {
/** Maximum number of controls to show on boot */
public static final int MAX_RESUMPTION_CONTROLS = 5;
@@ -42,7 +45,8 @@ public class QSMediaBrowser {
/** Delimiter for saved component names */
public static final String DELIMITER = ":";
- private static final String TAG = "QSMediaBrowser";
+ private static final String TAG = "ResumeMediaBrowser";
+ private boolean mIsEnabled = false;
private final Context mContext;
private final Callback mCallback;
private MediaBrowser mMediaBrowser;
@@ -54,21 +58,25 @@ public class QSMediaBrowser {
* @param callback used to report media items found
* @param componentName Component name of the MediaBrowserService this browser will connect to
*/
- public QSMediaBrowser(Context context, Callback callback, ComponentName componentName) {
+ public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName) {
+ mIsEnabled = Utils.useMediaResumption(context);
mContext = context;
mCallback = callback;
mComponentName = componentName;
}
/**
- * Connects to the MediaBrowserService and looks for valid media. If a media item is returned
- * by the service, QSMediaBrowser.Callback#addTrack will be called with its MediaDescription.
- * QSMediaBrowser.Callback#onConnected and QSMediaBrowser.Callback#onError will also be called
- * when the initial connection is successful, or an error occurs. Note that it is possible for
- * the service to connect but for no playable tracks to be found later.
- * QSMediaBrowser#disconnect will be called automatically with this function.
+ * Connects to the MediaBrowserService and looks for valid media. If a media item is returned,
+ * ResumeMediaBrowser.Callback#addTrack will be called with the MediaDescription.
+ * ResumeMediaBrowser.Callback#onConnected and ResumeMediaBrowser.Callback#onError will also be
+ * called when the initial connection is successful, or an error occurs.
+ * Note that it is possible for the service to connect but for no playable tracks to be found.
+ * ResumeMediaBrowser#disconnect will be called automatically with this function.
*/
public void findRecentMedia() {
+ if (!mIsEnabled) {
+ return;
+ }
Log.d(TAG, "Connecting to " + mComponentName);
disconnect();
Bundle rootHints = new Bundle();
@@ -86,7 +94,7 @@ public class QSMediaBrowser {
public void onChildrenLoaded(String parentId,
List<MediaBrowser.MediaItem> children) {
if (children.size() == 0) {
- Log.e(TAG, "No children found for " + mComponentName);
+ Log.d(TAG, "No children found for " + mComponentName);
return;
}
// We ask apps to return a playable item as the first child when sending
@@ -94,23 +102,24 @@ public class QSMediaBrowser {
MediaBrowser.MediaItem child = children.get(0);
MediaDescription desc = child.getDescription();
if (child.isPlayable()) {
- mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(), QSMediaBrowser.this);
+ mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
+ ResumeMediaBrowser.this);
} else {
- Log.e(TAG, "Child found but not playable for " + mComponentName);
+ Log.d(TAG, "Child found but not playable for " + mComponentName);
}
disconnect();
}
@Override
public void onError(String parentId) {
- Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId);
+ Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId);
mCallback.onError();
disconnect();
}
@Override
public void onError(String parentId, Bundle options) {
- Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId
+ Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId
+ ", options: " + options);
mCallback.onError();
disconnect();
@@ -149,7 +158,7 @@ public class QSMediaBrowser {
*/
@Override
public void onConnectionFailed() {
- Log.e(TAG, "Connection failed for " + mComponentName);
+ Log.d(TAG, "Connection failed for " + mComponentName);
mCallback.onError();
disconnect();
}
@@ -167,11 +176,15 @@ public class QSMediaBrowser {
}
/**
- * Connects to the MediaBrowserService and starts playback. QSMediaBrowser.Callback#onError or
- * QSMediaBrowser.Callback#onConnected will be called depending on whether it was successful.
- * QSMediaBrowser#disconnect should be called after this to ensure the connection is closed.
+ * Connects to the MediaBrowserService and starts playback.
+ * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called
+ * depending on whether it was successful.
+ * ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed.
*/
public void restart() {
+ if (!mIsEnabled) {
+ return;
+ }
disconnect();
Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
@@ -224,18 +237,21 @@ public class QSMediaBrowser {
/**
* Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser.
- * QSMediaBrowser.Callback#onError or QSMediaBrowser.Callback#onConnected will be called
+ * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called
* depending on whether it was successful.
- * QSMediaBrowser#disconnect should be called after this to ensure the connection is closed.
+ * ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed.
*/
public void testConnection() {
+ if (!mIsEnabled) {
+ return;
+ }
disconnect();
final MediaBrowser.ConnectionCallback connectionCallback =
new MediaBrowser.ConnectionCallback() {
@Override
public void onConnected() {
Log.d(TAG, "connected");
- if (mMediaBrowser.getRoot() == null) {
+ if (TextUtils.isEmpty(mMediaBrowser.getRoot())) {
mCallback.onError();
} else {
mCallback.onConnected();
@@ -264,7 +280,7 @@ public class QSMediaBrowser {
}
/**
- * Interface to handle results from QSMediaBrowser
+ * Interface to handle results from ResumeMediaBrowser
*/
public static class Callback {
/**
@@ -286,7 +302,7 @@ public class QSMediaBrowser {
* @param browser reference to the browser
*/
public void addTrack(MediaDescription track, ComponentName component,
- QSMediaBrowser browser) {
+ ResumeMediaBrowser browser) {
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 06821cd615a5..75ad06962afe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -91,8 +91,14 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
playbackState = state
if (shouldPollPlaybackPosition()) {
checkPlaybackPosition()
+ } else if (PlaybackState.STATE_NONE.equals(playbackState)) {
+ clearController()
}
}
+
+ override fun onSessionDestroyed() {
+ clearController()
+ }
}
/** Listening state (QS open or closed) is used to control polling of progress. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b0775f6c2d7..1c3b6850afc1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -50,6 +50,8 @@ public class QSContainerImpl extends FrameLayout {
private int mSideMargins;
private boolean mQsDisabled;
+ private int mContentPaddingStart = -1;
+ private int mContentPaddingEnd = -1;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -66,10 +68,9 @@ public class QSContainerImpl extends FrameLayout {
mBackground = findViewById(R.id.quick_settings_background);
mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background);
mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view);
- mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
+ updateResources();
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- setMargins();
}
@Override
@@ -103,10 +104,15 @@ public class QSContainerImpl extends FrameLayout {
if (navBelow) {
maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
}
+
+ int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ + layoutParams.rightMargin;
+ final int qsPanelWidthSpec = getChildMeasureSpec(widthMeasureSpec, padding,
+ layoutParams.width);
// Measure with EXACTLY. That way, PagedTileLayout will only use excess height and will be
// measured last, after other views and padding is accounted for.
- mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.EXACTLY));
- int width = mQSPanel.getMeasuredWidth();
+ mQSPanel.measure(qsPanelWidthSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.EXACTLY));
+ int width = mQSPanel.getMeasuredWidth() + padding;
int height = layoutParams.topMargin + layoutParams.bottomMargin
+ mQSPanel.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -148,8 +154,18 @@ public class QSContainerImpl extends FrameLayout {
LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
-
mQSPanel.setLayoutParams(layoutParams);
+
+ mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
+ mContentPaddingStart = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_start);
+ int newPaddingEnd = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_end);
+ boolean marginsChanged = newPaddingEnd != mContentPaddingEnd;
+ mContentPaddingEnd = newPaddingEnd;
+ if (marginsChanged) {
+ updatePaddingsAndMargins();
+ }
}
/**
@@ -196,17 +212,32 @@ public class QSContainerImpl extends FrameLayout {
updateExpansion();
}
- private void setMargins() {
- setMargins(mQSDetail);
- setMargins(mBackground);
- mQSPanel.setMargins(mSideMargins);
- mHeader.setMargins(mSideMargins);
- }
-
- private void setMargins(View view) {
- FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams();
- lp.rightMargin = mSideMargins;
- lp.leftMargin = mSideMargins;
+ private void updatePaddingsAndMargins() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View view = getChildAt(i);
+ if (view == mStatusBarBackground || view == mBackgroundGradient
+ || view == mQSCustomizer) {
+ // Some views are always full width
+ continue;
+ }
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.rightMargin = mSideMargins;
+ lp.leftMargin = mSideMargins;
+ if (view == mQSPanel) {
+ // QS panel lays out some of its content full width
+ mQSPanel.setContentMargins(mContentPaddingStart, mContentPaddingEnd);
+ } else if (view == mHeader) {
+ // The header contains the QQS panel which needs to have special padding, to
+ // visually align them.
+ mHeader.setContentMargins(mContentPaddingStart, mContentPaddingEnd);
+ } else {
+ view.setPaddingRelative(
+ mContentPaddingStart,
+ view.getPaddingTop(),
+ mContentPaddingEnd,
+ view.getPaddingBottom());
+ }
+ }
}
private int getDisplayHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 48ba1b99d714..78448785fe2f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -88,6 +88,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private final H mHandler = new H();
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final QSTileRevealController mQsTileRevealController;
+ /** Whether or not the QS media player feature is enabled. */
+ protected boolean mUsingMediaPlayer;
protected boolean mExpanded;
protected boolean mListening;
@@ -102,6 +104,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
protected QSSecurityFooter mFooter;
private PageIndicator mFooterPageIndicator;
private boolean mGridContentVisible = true;
+ private int mContentMarginStart;
+ private int mContentMarginEnd;
+ private int mVisualTilePadding;
protected QSTileLayout mTileLayout;
@@ -122,6 +127,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
UiEventLogger uiEventLogger
) {
super(context, attrs);
+ mUsingMediaPlayer = useQsMediaPlayer(context);
mMediaHost = mediaHost;
mContext = context;
mQSLogger = qsLogger;
@@ -170,8 +176,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
ViewGroup hostView = mMediaHost.getHostView();
addView(hostView);
- int sidePaddings = getResources().getDimensionPixelSize(
- R.dimen.quick_settings_side_margins);
int bottomPadding = getResources().getDimensionPixelSize(
R.dimen.quick_settings_expanded_bottom_margin);
MarginLayoutParams layoutParams = (MarginLayoutParams) hostView.getLayoutParams();
@@ -179,8 +183,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
layoutParams.bottomMargin = bottomPadding;
hostView.setLayoutParams(layoutParams);
- hostView.setPadding(sidePaddings, hostView.getPaddingTop(), sidePaddings,
- hostView.getPaddingBottom());
+ updateMediaHostContentMargins();
}
protected void addDivider() {
@@ -359,8 +362,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
public void updateResources() {
- final Resources res = mContext.getResources();
- setPadding(0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_top), 0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
+ int tileSize = getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
+ int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
+ mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f);
+ updatePadding();
updatePageIndicator();
@@ -372,6 +377,14 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
}
+ protected void updatePadding() {
+ final Resources res = mContext.getResources();
+ setPaddingRelative(getPaddingStart(),
+ res.getDimensionPixelSize(R.dimen.qs_panel_padding_top),
+ getPaddingEnd(),
+ res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -723,17 +736,51 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mFooter.showDeviceMonitoringDialog();
}
- public void setMargins(int sideMargins) {
- for (int i = 0; i < getChildCount(); i++) {
- View view = getChildAt(i);
- if (view != mTileLayout) {
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
- lp.leftMargin = sideMargins;
- lp.rightMargin = sideMargins;
- }
+ public void setContentMargins(int startMargin, int endMargin) {
+ // Only some views actually want this content padding, others want to go all the way
+ // to the edge like the brightness slider
+ mContentMarginStart = startMargin;
+ mContentMarginEnd = endMargin;
+ updateTileLayoutMargins(mContentMarginStart - mVisualTilePadding,
+ mContentMarginEnd - mVisualTilePadding);
+ updateMediaHostContentMargins();
+ }
+
+ /**
+ * Update the margins of all tile Layouts.
+ *
+ * @param visualMarginStart the visual start margin of the tile, adjusted for local insets
+ * to the tile. This can be set on a tileLayout
+ * @param visualMarginEnd the visual end margin of the tile, adjusted for local insets
+ * to the tile. This can be set on a tileLayout
+ */
+ protected void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
+ updateMargins((View) mTileLayout, visualMarginStart, visualMarginEnd);
+ }
+
+ /**
+ * Update the margins of the media hosts
+ */
+ protected void updateMediaHostContentMargins() {
+ if (mUsingMediaPlayer && mMediaHost != null) {
+ updateMargins(mMediaHost.getHostView(), mContentMarginStart, mContentMarginEnd);
}
}
+ /**
+ * Update the margins of a view.
+ *
+ * @param view the view to adjust
+ * @param start the start margin to set
+ * @param end the end margin to set
+ */
+ protected void updateMargins(View view, int start, int end) {
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.setMarginStart(start);
+ lp.setMarginEnd(end);
+ view.setLayoutParams(lp);
+ }
+
public MediaHost getMediaHost() {
return mMediaHost;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 75507beba7ae..94b4cee92965 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -41,7 +41,6 @@ import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.Collection;
@@ -62,8 +61,6 @@ public class QuickQSPanel extends QSPanel {
private boolean mDisabledByPolicy;
private int mMaxTiles;
protected QSPanel mFullPanel;
- /** Whether or not the QS media player feature is enabled. */
- private boolean mUsingMediaPlayer;
/** Whether or not the QuickQSPanel currently contains a media player. */
private boolean mShowHorizontalTileLayout;
private LinearLayout mHorizontalLinearLayout;
@@ -84,8 +81,7 @@ public class QuickQSPanel extends QSPanel {
MediaHost mediaHost,
UiEventLogger uiEventLogger
) {
- super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, mediaHost,
- uiEventLogger);
+ super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, mediaHost, uiEventLogger);
if (mFooter != null) {
removeView(mFooter.getView());
}
@@ -97,8 +93,6 @@ public class QuickQSPanel extends QSPanel {
}
mMediaBottomMargin = getResources().getDimensionPixelSize(
R.dimen.quick_settings_media_extra_bottom_margin);
-
- mUsingMediaPlayer = Utils.useQsMediaPlayer(context);
if (mUsingMediaPlayer) {
mHorizontalLinearLayout = new LinearLayout(mContext);
mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
@@ -132,7 +126,6 @@ public class QuickQSPanel extends QSPanel {
mHorizontalLinearLayout.setVisibility(useHorizontal ? View.VISIBLE : View.GONE);
addView((View) mRegularTileLayout, 0);
super.setPadding(0, 0, 0, 0);
- applySideMargins(mHorizontalLinearLayout);
applyBottomMargin((View) mRegularTileLayout);
} else {
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -151,14 +144,6 @@ public class QuickQSPanel extends QSPanel {
view.setLayoutParams(layoutParams);
}
- private void applySideMargins(View view) {
- int margin = getResources().getDimensionPixelSize(R.dimen.qs_header_tile_margin_horizontal);
- MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
- layoutParams.setMarginStart(margin);
- layoutParams.setMarginEnd(margin);
- view.setLayoutParams(layoutParams);
- }
-
private void reAttachMediaHost() {
if (mMediaHost == null) {
return;
@@ -177,10 +162,6 @@ public class QuickQSPanel extends QSPanel {
layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
layoutParams.weight = horizontal ? 1.5f : 0;
layoutParams.bottomMargin = mMediaBottomMargin;
- int marginStart = horizontal
- ? getResources().getDimensionPixelSize(R.dimen.qs_header_tile_margin_horizontal)
- : 0;
- layoutParams.setMarginStart(marginStart);
}
}
@@ -194,11 +175,22 @@ public class QuickQSPanel extends QSPanel {
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
reAttachMediaHost();
+ updateMediaHostContentMargins();
+ }
+
+ @Override
+ protected void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
+ if (mUsingMediaPlayer) {
+ updateMargins((View) mRegularTileLayout, visualMarginStart, visualMarginEnd);
+ updateMargins((View) mHorizontalTileLayout, visualMarginStart, 0);
+ } else {
+ updateMargins((View) mTileLayout, visualMarginStart, visualMarginEnd);
+ }
}
@Override
- public void setPadding(int left, int top, int right, int bottom) {
- // Always have no padding.
+ protected void updatePadding() {
+ // QS Panel is setting a top padding by default, which we don't need.
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 29b3436c0b72..20e47b2f2fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -42,6 +42,7 @@ import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -143,6 +144,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private boolean mHasTopCutout = false;
private int mRoundedCornerPadding = 0;
+ private int mContentMarginStart;
+ private int mContentMarginEnd;
+ private int mWaterfallTopInset;
+ private int mCutOutPaddingLeft;
+ private int mCutOutPaddingRight;
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -422,33 +428,42 @@ public class QuickStatusBarHeader extends RelativeLayout implements
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- // Handle padding of QuickStatusBarHeader
- setPadding(mRoundedCornerPadding, getPaddingTop(), mRoundedCornerPadding,
- getPaddingBottom());
-
- // Handle padding of SystemIconsView
+ // Handle padding of the clock
DisplayCutout cutout = insets.getDisplayCutout();
Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
cutout, getDisplay());
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
- cutout, cornerCutoutPadding, mRoundedCornerPadding);
- final int waterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
- int statusBarPaddingLeft = isLayoutRtl()
- ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end)
- : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start);
- int statusBarPaddingRight = isLayoutRtl()
- ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start)
- : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end);
- mSystemIconsView.setPadding(
- Math.max(padding.first + statusBarPaddingLeft - mRoundedCornerPadding, 0),
- waterfallTopInset,
- Math.max(padding.second + statusBarPaddingRight - mRoundedCornerPadding, 0),
- 0);
-
+ cutout, cornerCutoutPadding, -1);
+ mCutOutPaddingLeft = padding.first;
+ mCutOutPaddingRight = padding.second;
+ mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
+ updateClockPadding();
return super.onApplyWindowInsets(insets);
}
+ private void updateClockPadding() {
+ int clockPaddingLeft = 0;
+ int clockPaddingRight = 0;
+ // The clock might collide with cutouts, let's shift it out of the way.
+ // We only do that if the inset is bigger than our own padding, since it's nicer to
+ // align with
+ if (mCutOutPaddingLeft > 0) {
+ // if there's a cutout, let's use at least the rounded corner inset
+ int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
+ int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart;
+ clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft, 0);
+ }
+ if (mCutOutPaddingRight > 0) {
+ // if there's a cutout, let's use at least the rounded corner inset
+ int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
+ int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd;
+ clockPaddingRight = Math.max(cutoutPadding - contentMarginRight, 0);
+ }
+
+ mSystemIconsView.setPadding(clockPaddingLeft, mWaterfallTopInset, clockPaddingRight, 0);
+ }
+
@Override
@VisibleForTesting
public void onDetachedFromWindow() {
@@ -558,24 +573,27 @@ public class QuickStatusBarHeader extends RelativeLayout implements
return color == Color.WHITE ? 0 : 1;
}
- public void setMargins(int sideMargins) {
- for (int i = 0; i < getChildCount(); i++) {
- View v = getChildAt(i);
- // Prevents these views from getting set a margin.
- // The Icon views all have the same padding set in XML to be aligned.
- if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel
- || v == mHeaderTextContainerView) {
- continue;
- }
- RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
- lp.leftMargin = sideMargins;
- lp.rightMargin = sideMargins;
- }
- }
-
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycle;
}
+
+ public void setContentMargins(int marginStart, int marginEnd) {
+ mContentMarginStart = marginStart;
+ mContentMarginEnd = marginEnd;
+ for (int i = 0; i < getChildCount(); i++) {
+ View view = getChildAt(i);
+ if (view == mHeaderQsPanel) {
+ // QS panel doesn't lays out some of its content full width
+ mHeaderQsPanel.setContentMargins(marginStart, marginEnd);
+ } else {
+ MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
+ lp.setMarginStart(marginStart);
+ lp.setMarginEnd(marginEnd);
+ view.setLayoutParams(lp);
+ }
+ }
+ updateClockPadding();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 098431658e6a..383c29d90a22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -96,7 +96,6 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
- mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
if (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1);
if (mColumns != columns) {
@@ -120,7 +119,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
mRows = (numTiles + mColumns - 1) / mColumns;
}
mCellWidth =
- (availableWidth - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
+ (availableWidth - (mCellMarginHorizontal * mColumns)) / mColumns;
// Measure each QS tile.
View previousView = this;
@@ -204,7 +203,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
}
protected int getColumnStart(int column) {
- return getPaddingStart() + mSidePadding + mCellMarginHorizontal / 2 +
+ return getPaddingStart() + mCellMarginHorizontal / 2 +
column * (mCellWidth + mCellMarginHorizontal);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index b272b60f3593..baa2dfdcebf3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -64,13 +64,16 @@ import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActiv
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipUI;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.stackdivider.Divider;
@@ -83,8 +86,6 @@ import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -101,8 +102,9 @@ import dagger.Lazy;
* Class to send information from overview to launcher with a binder.
*/
@Singleton
-public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
- NavigationModeController.ModeChangedListener, Dumpable {
+public class OverviewProxyService extends CurrentUserTracker implements
+ CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener,
+ Dumpable {
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
@@ -123,7 +125,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private final NotificationShadeWindowController mStatusBarWinController;
private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
private final ComponentName mRecentsComponentName;
- private final DeviceProvisionedController mDeviceProvisionedController;
private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
private final Intent mQuickStepIntent;
private final ScreenshotHelper mScreenshotHelper;
@@ -383,8 +384,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@Override
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
Insets visibleInsets, int taskId) {
- mScreenshotHelper.provideScreenshot(screenImage, locationInScreen, visibleInsets,
- taskId, SCREENSHOT_OVERVIEW, mHandler, null);
+ // Deprecated
}
@Override
@@ -434,6 +434,21 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
+ @Override
+ public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
+ Insets visibleInsets, Task.TaskKey task) {
+ mScreenshotHelper.provideScreenshot(
+ screenImageBundle,
+ locationInScreen,
+ visibleInsets,
+ task.id,
+ task.userId,
+ task.sourceComponent,
+ SCREENSHOT_OVERVIEW,
+ mHandler,
+ null);
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -480,7 +495,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
return;
}
- mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
+ mCurrentBoundedUserId = getCurrentUserId();
mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
Bundle params = new Bundle();
@@ -523,22 +538,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
};
- private final DeviceProvisionedListener mDeviceProvisionedCallback =
- new DeviceProvisionedListener() {
- @Override
- public void onUserSetupChanged() {
- if (mDeviceProvisionedController.isCurrentUserSetup()) {
- internalConnectToCurrentUser();
- }
- }
-
- @Override
- public void onUserSwitched() {
- mConnectionBackoffAttempts = 0;
- internalConnectToCurrentUser();
- }
- };
-
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
// This is the death handler for the binder from the launcher service
@@ -548,18 +547,18 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
- DeviceProvisionedController provisionController,
NavigationBarController navBarController, NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
PipUI pipUI, Optional<Divider> dividerOptional,
- Optional<Lazy<StatusBar>> statusBarOptionalLazy) {
+ Optional<Lazy<StatusBar>> statusBarOptionalLazy,
+ BroadcastDispatcher broadcastDispatcher) {
+ super(broadcastDispatcher);
mContext = context;
mPipUI = pipUI;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mHandler = new Handler();
mNavBarController = navBarController;
mStatusBarWinController = statusBarWinController;
- mDeviceProvisionedController = provisionController;
mConnectionBackoffAttempts = 0;
mDividerOptional = dividerOptional;
mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
@@ -580,7 +579,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
// Listen for device provisioned/user setup
updateEnabledState();
- mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
+ startTracking();
// Listen for launcher package changes
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -604,6 +603,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
});
}
+ @Override
+ public void onUserSwitched(int newUserId) {
+ mConnectionBackoffAttempts = 0;
+ internalConnectToCurrentUser();
+ }
+
public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
boolean gestureSwipeLeft) {
try {
@@ -709,10 +714,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
disconnectFromLauncherService();
// If user has not setup yet or already connected, do not try to connect
- if (!mDeviceProvisionedController.isCurrentUserSetup() || !isEnabled()) {
- Log.v(TAG_OPS, "Cannot attempt connection, is setup "
- + mDeviceProvisionedController.isCurrentUserSetup() + ", is enabled "
- + isEnabled());
+ if (!isEnabled()) {
+ Log.v(TAG_OPS, "Cannot attempt connection, is enabled " + isEnabled());
return;
}
mHandler.removeCallbacks(mConnectionRunnable);
@@ -722,7 +725,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
mBound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
- UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+ UserHandle.of(getCurrentUserId()));
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
@@ -881,8 +884,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
pw.println(TAG_OPS + " state:");
pw.print(" recentsComponentName="); pw.println(mRecentsComponentName);
pw.print(" isConnected="); pw.println(mOverviewProxy != null);
- pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
- .isCurrentUserSetup());
pw.print(" connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
pw.print(" quickStepIntent="); pw.println(mQuickStepIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 960c50129a56..3e268f63d65e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -225,8 +225,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
res.getString(R.string.screenrecord_name));
String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
- ? res.getString(R.string.screenrecord_ongoing_screen_and_audio)
- : res.getString(R.string.screenrecord_ongoing_screen_only);
+ ? res.getString(R.string.screenrecord_ongoing_screen_only)
+ : res.getString(R.string.screenrecord_ongoing_screen_and_audio);
mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
index 752f4fddf24b..edbc3cfdece5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
@@ -38,6 +38,7 @@ import java.nio.ByteBuffer;
public class ScreenInternalAudioRecorder {
private static String TAG = "ScreenAudioRecorder";
private static final int TIMEOUT = 500;
+ private static final float MIC_VOLUME_SCALE = 1.4f;
private final Context mContext;
private AudioRecord mAudioRecord;
private AudioRecord mAudioRecordMic;
@@ -148,6 +149,10 @@ public class ScreenInternalAudioRecorder {
readShortsInternal = mAudioRecord.read(bufferInternal, 0,
bufferInternal.length);
readShortsMic = mAudioRecordMic.read(bufferMic, 0, bufferMic.length);
+
+ // modify the volume
+ bufferMic = scaleValues(bufferMic,
+ readShortsMic, MIC_VOLUME_SCALE);
readBytes = Math.min(readShortsInternal, readShortsMic) * 2;
buffer = addAndConvertBuffers(bufferInternal, readShortsInternal, bufferMic,
readShortsMic);
@@ -168,6 +173,19 @@ public class ScreenInternalAudioRecorder {
});
}
+ private short[] scaleValues(short[] buff, int len, float scale) {
+ for (int i = 0; i < len; i++) {
+ int oldValue = buff[i];
+ int newValue = (int) (buff[i] * scale);
+ if (newValue > Short.MAX_VALUE) {
+ newValue = Short.MAX_VALUE;
+ } else if (newValue < Short.MIN_VALUE) {
+ newValue = Short.MIN_VALUE;
+ }
+ buff[i] = (short) (newValue);
+ }
+ return buff;
+ }
private byte[] addAndConvertBuffers(short[] a1, int a1Limit, short[] a2, int a2Limit) {
int size = Math.max(a1Limit, a2Limit);
if (size < 0) return new byte[0];
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index c967648c544e..8551c88d133a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -55,9 +55,9 @@ import java.util.Date;
*/
public class ScreenMediaRecorder {
private static final int TOTAL_NUM_TRACKS = 1;
- private static final int VIDEO_BIT_RATE = 10000000;
private static final int VIDEO_FRAME_RATE = 30;
- private static final int AUDIO_BIT_RATE = 16;
+ private static final int VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO = 6;
+ private static final int AUDIO_BIT_RATE = 196000;
private static final int AUDIO_SAMPLE_RATE = 44100;
private static final int MAX_DURATION_MS = 60 * 60 * 1000;
private static final long MAX_FILESIZE_BYTES = 5000000000L;
@@ -108,7 +108,7 @@ public class ScreenMediaRecorder {
// Set up audio source
if (mAudioSource == MIC) {
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
}
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
@@ -121,10 +121,13 @@ public class ScreenMediaRecorder {
wm.getDefaultDisplay().getRealMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
+ int refereshRate = (int) wm.getDefaultDisplay().getRefreshRate();
+ int vidBitRate = screenHeight * screenWidth * refereshRate / VIDEO_FRAME_RATE
+ * VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO;
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setVideoSize(screenWidth, screenHeight);
- mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);
- mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE);
+ mMediaRecorder.setVideoFrameRate(refereshRate);
+ mMediaRecorder.setVideoEncodingBitRate(vidBitRate);
mMediaRecorder.setMaxDuration(MAX_DURATION_MS);
mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index abd7e7159260..d057a8a43c43 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -24,12 +24,9 @@ import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE;
import android.app.Activity;
import android.app.PendingIntent;
import android.os.Bundle;
-import android.util.Log;
import android.view.Gravity;
-import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
-import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
@@ -88,8 +85,8 @@ public class ScreenRecordDialog extends Activity {
});
mModes = new ArrayList<>();
- mModes.add(INTERNAL);
mModes.add(MIC);
+ mModes.add(INTERNAL);
mModes.add(MIC_AND_INTERNAL);
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
index 2e0e746594b4..3e78489e5707 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
@@ -88,12 +88,6 @@ public class ScreenRecordingAdapter extends ArrayAdapter<ScreenRecordingAudioSou
return layout;
}
- private void setDescription(LinearLayout layout, int description) {
- if (description != Resources.ID_NULL) {
- ((TextView) layout.getChildAt(1)).setText(description);
- }
- }
-
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
switch (getItem(position)) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index a9d3772a0fdb..9b1734d40674 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -35,6 +35,7 @@ import android.app.ActivityOptions;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -494,8 +495,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, Consumer<Uri> finisher, Runnable onComplete) {
- // TODO use taskId and visibleInsets
+ Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+ Consumer<Uri> finisher, Runnable onComplete) {
+ // TODO: use task Id, userId, topComponent for smart handler
+ // TODO: use visibleInsets for animation
mOnCompleteRunnable = onComplete;
takeScreenshot(screenshot, finisher, screenshotScreenBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
index 095c32f4a2ce..0017b1f79b74 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -24,6 +24,7 @@ import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -234,8 +235,10 @@ public class GlobalScreenshotLegacy {
}
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
- // TODO use taskId and visibleInsets
+ Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+ Consumer<Uri> finisher) {
+ // TODO: use task Id, userId, topComponent for smart handler
+ // TODO: use visibleInsets for animation
takeScreenshot(screenshot, finisher, false, false, screenshotScreenBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 98030d45b05e..8322fe08d3c2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
import android.app.Service;
+import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Insets;
@@ -37,6 +38,7 @@ import android.view.WindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotHelper;
+import com.android.systemui.shared.recents.utilities.BitmapUtil;
import java.util.function.Consumer;
@@ -107,16 +109,19 @@ public class TakeScreenshotService extends Service {
}
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
- Bitmap screenshot = screenshotRequest.getBitmap();
+ Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
+ screenshotRequest.getBitmapBundle());
Rect screenBounds = screenshotRequest.getBoundsInScreen();
Insets insets = screenshotRequest.getInsets();
int taskId = screenshotRequest.getTaskId();
+ int userId = screenshotRequest.getUserId();
+ ComponentName topComponent = screenshotRequest.getTopComponent();
if (useCornerFlow) {
- mScreenshot.handleImageAsScreenshot(
- screenshot, screenBounds, insets, taskId, uriConsumer, onComplete);
+ mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
+ taskId, userId, topComponent, uriConsumer, onComplete);
} else {
- mScreenshotLegacy.handleImageAsScreenshot(
- screenshot, screenBounds, insets, taskId, uriConsumer);
+ mScreenshotLegacy.handleImageAsScreenshot(screenshot, screenBounds, insets,
+ taskId, userId, topComponent, uriConsumer);
}
break;
default:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 217148df60e2..5628a24f40ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -52,7 +52,6 @@ import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
-import com.android.systemui.media.MediaDeviceManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -102,6 +101,12 @@ public class NotificationMediaManager implements Dumpable {
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_PAUSED);
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
}
+ private static final HashSet<Integer> INACTIVE_MEDIA_STATES = new HashSet<>();
+ static {
+ INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_NONE);
+ INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_STOPPED);
+ INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
+ }
private final NotificationEntryManager mEntryManager;
private final MediaDataManager mMediaDataManager;
@@ -190,8 +195,7 @@ public class NotificationMediaManager implements Dumpable {
KeyguardBypassController keyguardBypassController,
@Main DelayableExecutor mainExecutor,
DeviceConfigProxy deviceConfig,
- MediaDataManager mediaDataManager,
- MediaDeviceManager mediaDeviceManager) {
+ MediaDataManager mediaDataManager) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -212,13 +216,11 @@ public class NotificationMediaManager implements Dumpable {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
- mediaDeviceManager.onNotificationAdded(entry.getKey(), entry.getSbn());
}
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
- mediaDeviceManager.onNotificationAdded(entry.getKey(), entry.getSbn());
}
@Override
@@ -239,7 +241,6 @@ public class NotificationMediaManager implements Dumpable {
int reason) {
onNotificationRemoved(entry.getKey());
mediaDataManager.onNotificationRemoved(entry.getKey());
- mediaDeviceManager.onNotificationRemoved(entry.getKey());
}
});
@@ -252,10 +253,24 @@ public class NotificationMediaManager implements Dumpable {
mPropertiesChangedListener);
}
+ /**
+ * Check if a state should be considered actively playing
+ * @param state a PlaybackState
+ * @return true if playing
+ */
public static boolean isPlayingState(int state) {
return !PAUSED_MEDIA_STATES.contains(state);
}
+ /**
+ * Check if a state should be considered active (playing or paused)
+ * @param state a PlaybackState
+ * @return true if active
+ */
+ public static boolean isActiveState(int state) {
+ return !INACTIVE_MEDIA_STATES.contains(state);
+ }
+
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index c988e1251d3f..84c8db3218e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -24,7 +24,6 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
-import com.android.systemui.media.MediaDeviceManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -51,8 +50,6 @@ import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.concurrency.DelayableExecutor;
-import java.util.concurrent.Executor;
-
import javax.inject.Singleton;
import dagger.Lazy;
@@ -105,8 +102,7 @@ public interface StatusBarDependenciesModule {
KeyguardBypassController keyguardBypassController,
@Main DelayableExecutor mainExecutor,
DeviceConfigProxy deviceConfigProxy,
- MediaDataManager mediaDataManager,
- MediaDeviceManager mediaDeviceManager) {
+ MediaDataManager mediaDataManager) {
return new NotificationMediaManager(
context,
statusBarLazy,
@@ -116,8 +112,7 @@ public interface StatusBarDependenciesModule {
keyguardBypassController,
mainExecutor,
deviceConfigProxy,
- mediaDataManager,
- mediaDeviceManager);
+ mediaDataManager);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
index 9dcc187cb0ef..87612f15ed3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
@@ -52,7 +52,7 @@ public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsCon
private NotificationGuts mGutsContainer;
private OnClickListener mOnOk = v -> {
- closeControls(v);
+ mGutsContainer.closeControls(v, false);
};
public AppOpsInfo(Context context, AttributeSet attrs) {
@@ -117,6 +117,7 @@ public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsCon
});
TextView ok = findViewById(R.id.ok);
ok.setOnClickListener(mOnOk);
+ ok.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
}
private String getPrompt() {
@@ -160,19 +161,6 @@ public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsCon
}
}
- private void closeControls(View v) {
- mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
- int[] parentLoc = new int[2];
- int[] targetLoc = new int[2];
- mGutsContainer.getLocationOnScreen(parentLoc);
- v.getLocationOnScreen(targetLoc);
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- final int x = targetLoc[0] - parentLoc[0] + centerX;
- final int y = targetLoc[1] - parentLoc[1] + centerY;
- mGutsContainer.closeControls(x, y, false, false);
- }
-
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
@@ -200,6 +188,7 @@ public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsCon
@Override
public boolean handleCloseControls(boolean save, boolean force) {
+ mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
return false;
}
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 f7ad50edb2f6..94e12e82f850 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
@@ -1122,9 +1122,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void setGutsView(MenuItem item) {
- if (mGuts != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
- ((NotificationGuts.GutsContent) item.getGutsView()).setGutsParent(mGuts);
- mGuts.setGutsContent((NotificationGuts.GutsContent) item.getGutsView());
+ if (getGuts() != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
+ getGuts().setGutsContent((NotificationGuts.GutsContent) item.getGutsView());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 9925909c3e16..8a4fdc24dc1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -557,9 +557,9 @@ public class NotificationContentView extends FrameLayout {
private void focusExpandButtonIfNecessary() {
if (mFocusOnVisibilityChange) {
- NotificationHeaderView header = getVisibleNotificationHeader();
- if (header != null) {
- ImageView expandButton = header.getExpandButton();
+ NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
+ if (wrapper != null) {
+ View expandButton = wrapper.getExpandButton();
if (expandButton != null) {
expandButton.requestAccessibilityFocus();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 2e7b17587085..e9d89589172e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -53,6 +53,7 @@ import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
@@ -115,6 +116,7 @@ public class NotificationConversationInfo extends LinearLayout implements
private OnSnoozeClickListener mOnSnoozeClickListener;
private OnSettingsClickListener mOnSettingsClickListener;
private NotificationGuts mGutsContainer;
+ private OnConversationSettingsClickListener mOnConversationSettingsClickListener;
@VisibleForTesting
boolean mSkipPost = false;
@@ -137,13 +139,13 @@ public class NotificationConversationInfo extends LinearLayout implements
mSelectedAction = ACTION_HOME;
mShortcutManager.requestPinShortcut(mShortcutInfo, null);
mShadeController.animateCollapsePanels();
- closeControls(v, true);
+ mGutsContainer.closeControls(v, true);
};
private OnClickListener mOnSnoozeClick = v -> {
mSelectedAction = ACTION_SNOOZE;
mOnSnoozeClickListener.onClick(v, 1);
- closeControls(v, true);
+ mGutsContainer.closeControls(v, true);
};
*/
@@ -164,7 +166,11 @@ public class NotificationConversationInfo extends LinearLayout implements
private OnClickListener mOnDone = v -> {
mPressedApply = true;
- closeControls(v, true);
+ // If the user selected Priority, maybe show the priority onboarding
+ if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) {
+ showPriorityOnboarding();
+ }
+ mGutsContainer.closeControls(v, true);
};
public NotificationConversationInfo(Context context, AttributeSet attrs) {
@@ -175,6 +181,10 @@ public class NotificationConversationInfo extends LinearLayout implements
void onClick(View v, NotificationChannel channel, int appUid);
}
+ public interface OnConversationSettingsClickListener {
+ void onClick();
+ }
+
public interface OnAppSettingsClickListener {
void onClick(View v, Intent intent);
}
@@ -190,14 +200,6 @@ public class NotificationConversationInfo extends LinearLayout implements
}
mSelectedAction = selectedAction;
- onSelectedActionChanged();
- }
-
- private void onSelectedActionChanged() {
- // If the user selected Priority, maybe show the priority onboarding
- if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) {
- showPriorityOnboarding();
- }
}
public void bindNotification(
@@ -216,7 +218,8 @@ public class NotificationConversationInfo extends LinearLayout implements
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
boolean isDeviceProvisioned,
@Main Handler mainHandler,
- @Background Handler bgHandler) {
+ @Background Handler bgHandler,
+ OnConversationSettingsClickListener onConversationSettingsClickListener) {
mSelectedAction = -1;
mINotificationManager = iNotificationManager;
mVisualStabilityManager = visualStabilityManager;
@@ -231,6 +234,7 @@ public class NotificationConversationInfo extends LinearLayout implements
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mOnSnoozeClickListener = onSnoozeClickListener;
+ mOnConversationSettingsClickListener = onConversationSettingsClickListener;
mIconFactory = conversationIconFactory;
mUserContext = userContext;
mBubbleMetadata = bubbleMetadata;
@@ -258,6 +262,7 @@ public class NotificationConversationInfo extends LinearLayout implements
View done = findViewById(R.id.done);
done.setOnClickListener(mOnDone);
+ done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
}
private void bindActions() {
@@ -322,7 +327,6 @@ public class NotificationConversationInfo extends LinearLayout implements
ImageView image = findViewById(R.id.conversation_icon);
image.setImageDrawable(mIconFactory.getConversationDrawable(
mShortcutInfo, mPackageName, mAppUid, important));
-
}
private void bindPackage() {
@@ -520,9 +524,9 @@ public class NotificationConversationInfo extends LinearLayout implements
boolean ignoreDnd = false;
try {
- ignoreDnd = (mINotificationManager
- .getConsolidatedNotificationPolicy().priorityConversationSenders
- & NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT) != 0;
+ ignoreDnd = mINotificationManager
+ .getConsolidatedNotificationPolicy().priorityConversationSenders ==
+ NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
} catch (RemoteException e) {
Log.e(TAG, "Could not check conversation senders", e);
}
@@ -537,31 +541,14 @@ public class NotificationConversationInfo extends LinearLayout implements
.setView(onboardingView)
.setIgnoresDnd(ignoreDnd)
.setShowsAsBubble(showAsBubble)
+ .setIcon(((ImageView) findViewById(R.id.conversation_icon)).getDrawable())
+ .setOnSettingsClick(mOnConversationSettingsClickListener)
.build();
controller.init();
controller.show();
}
- /**
- * Closes the controls and commits the updated importance values (indirectly).
- *
- * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
- * user does not have the ability to undo the action anymore.
- */
- @VisibleForTesting
- void closeControls(View v, boolean save) {
- int[] parentLoc = new int[2];
- int[] targetLoc = new int[2];
- mGutsContainer.getLocationOnScreen(parentLoc);
- v.getLocationOnScreen(targetLoc);
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- final int x = targetLoc[0] - parentLoc[0] + centerX;
- final int y = targetLoc[1] - parentLoc[1] + centerY;
- mGutsContainer.closeControls(x, y, save, false /* force */);
- }
-
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
@@ -631,8 +618,7 @@ public class NotificationConversationInfo extends LinearLayout implements
try {
switch (mAction) {
case ACTION_FAVORITE:
- mChannelToUpdate.setImportantConversation(
- !mChannelToUpdate.isImportantConversation());
+ mChannelToUpdate.setImportantConversation(true);
if (mChannelToUpdate.isImportantConversation()) {
mChannelToUpdate.setAllowBubbles(true);
if (mAppBubble == BUBBLE_PREFERENCE_NONE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index c762b73a1648..eeac46a60ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -22,12 +22,14 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -59,6 +61,31 @@ public class NotificationGuts extends FrameLayout {
private GutsContent mGutsContent;
+ private View.AccessibilityDelegate mGutsContentAccessibilityDelegate =
+ new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(
+ View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (super.performAccessibilityAction(host, action, args)) {
+ return true;
+ }
+
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_LONG_CLICK:
+ closeControls(host, false);
+ return true;
+ }
+
+ return false;
+ }
+ };
+
public interface GutsContent {
public void setGutsParent(NotificationGuts listener);
@@ -110,6 +137,11 @@ public class NotificationGuts extends FrameLayout {
* view on the lockscreen
*/
boolean needsFalsingProtection();
+
+ /**
+ * Equivalent to {@link View#setAccessibilityDelegate(AccessibilityDelegate)}
+ */
+ void setAccessibilityDelegate(AccessibilityDelegate gutsContentAccessibilityDelegate);
}
public interface OnGutsClosedListener {
@@ -146,6 +178,8 @@ public class NotificationGuts extends FrameLayout {
}
public void setGutsContent(GutsContent content) {
+ content.setGutsParent(this);
+ content.setAccessibilityDelegate(mGutsContentAccessibilityDelegate);
mGutsContent = content;
removeAllViews();
addView(mGutsContent.getContentView());
@@ -237,13 +271,29 @@ public class NotificationGuts extends FrameLayout {
/**
* Closes any exposed guts/views.
+ */
+ public void closeControls(View eventSource, boolean save) {
+ int[] parentLoc = new int[2];
+ int[] targetLoc = new int[2];
+ getLocationOnScreen(parentLoc);
+ eventSource.getLocationOnScreen(targetLoc);
+ final int centerX = eventSource.getWidth() / 2;
+ final int centerY = eventSource.getHeight() / 2;
+ final int x = targetLoc[0] - parentLoc[0] + centerX;
+ final int y = targetLoc[1] - parentLoc[1] + centerY;
+
+ closeControls(x, y, save, false);
+ }
+
+ /**
+ * Closes any exposed guts/views.
*
* @param x x coordinate to animate the close circular reveal with
* @param y y coordinate to animate the close circular reveal with
* @param save whether the state should be saved
* @param force whether the guts should be force-closed regardless of state.
*/
- public void closeControls(int x, int y, boolean save, boolean force) {
+ private void closeControls(int x, int y, boolean save, boolean force) {
// First try to dismiss any blocking helper.
boolean wasBlockingHelperDismissed =
Dependency.get(NotificationBlockingHelperManager.class)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index a64dcdffff1e..1074adc3383d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -216,6 +216,11 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
}
}
+ private void startConversationSettingsActivity(int uid, ExpandableNotificationRow row) {
+ final Intent intent = new Intent(Settings.ACTION_CONVERSATION_SETTINGS);
+ mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
+ }
+
private boolean bindGuts(final ExpandableNotificationRow row) {
row.ensureGutsInflated();
return bindGuts(row, mGutsMenuItem);
@@ -438,6 +443,12 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mListContainer.getSwipeActionHelper().snooze(sbn, hours);
};
+ final NotificationConversationInfo.OnConversationSettingsClickListener
+ onConversationSettingsListener =
+ () -> {
+ startConversationSettingsActivity(sbn.getUid(), row);
+ };
+
if (!userHandle.equals(UserHandle.ALL)
|| mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
@@ -468,7 +479,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mBuilderProvider,
mDeviceProvisionedController.isDeviceProvisioned(),
mMainHandler,
- mBgHandler);
+ mBgHandler,
+ onConversationSettingsListener);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index f434fbbcd916..a131ebef77db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -141,7 +141,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
// used by standard ui
private OnClickListener mOnDismissSettings = v -> {
mPressedApply = true;
- closeControls(v, true);
+ mGutsContainer.closeControls(v, true);
};
public NotificationInfo(Context context, AttributeSet attrs) {
@@ -250,7 +250,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
View done = findViewById(R.id.done);
done.setOnClickListener(mOnDismissSettings);
-
+ done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
View silent = findViewById(R.id.silence);
View alert = findViewById(R.id.alert);
@@ -330,7 +330,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
- closeControls(this, false);
+ mGutsContainer.closeControls(this, false);
});
mChannelEditorDialogController.show();
}
@@ -528,25 +528,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
return intent;
}
- /**
- * Closes the controls and commits the updated importance values (indirectly).
- *
- * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
- * user does not have the ability to undo the action anymore.
- */
- @VisibleForTesting
- void closeControls(View v, boolean save) {
- int[] parentLoc = new int[2];
- int[] targetLoc = new int[2];
- mGutsContainer.getLocationOnScreen(parentLoc);
- v.getLocationOnScreen(targetLoc);
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- final int x = targetLoc[0] - parentLoc[0] + centerX;
- final int y = targetLoc[1] - parentLoc[1] + centerY;
- mGutsContainer.closeControls(x, y, save, false /* force */);
- }
-
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index cde3dfd66aaf..1ffb244b51c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -380,16 +380,8 @@ public class NotificationSnooze extends LinearLayout
private void undoSnooze(View v) {
mSelectedOption = null;
- int[] parentLoc = new int[2];
- int[] targetLoc = new int[2];
- mGutsContainer.getLocationOnScreen(parentLoc);
- v.getLocationOnScreen(targetLoc);
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- final int x = targetLoc[0] - parentLoc[0] + centerX;
- final int y = targetLoc[1] - parentLoc[1] + centerY;
showSnoozeOptions(false);
- mGutsContainer.closeControls(x, y, false /* save */, false /* force */);
+ mGutsContainer.closeControls(v, false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index eb28c58d95b7..f1fe54ad4024 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -89,7 +89,7 @@ public class PartialConversationInfo extends LinearLayout implements
private OnClickListener mOnDone = v -> {
mPressedApply = true;
- closeControls(v, true);
+ mGutsContainer.closeControls(v, true);
};
public PartialConversationInfo(Context context, AttributeSet attrs) {
@@ -132,6 +132,7 @@ public class PartialConversationInfo extends LinearLayout implements
View done = findViewById(R.id.done);
done.setOnClickListener(mOnDone);
+ done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
}
private void bindActions() {
@@ -172,7 +173,7 @@ public class PartialConversationInfo extends LinearLayout implements
mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
- closeControls(this, false);
+ mGutsContainer.closeControls(this, false);
});
mChannelEditorDialogController.show();
}
@@ -240,7 +241,6 @@ public class PartialConversationInfo extends LinearLayout implements
} catch (PackageManager.NameNotFoundException e) {
mPkgIcon = mPm.getDefaultActivityIcon();
}
- ((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
}
private void bindDelegate() {
@@ -317,25 +317,6 @@ public class PartialConversationInfo extends LinearLayout implements
}
}
- /**
- * Closes the controls and commits the updated importance values (indirectly).
- *
- * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
- * user does not have the ability to undo the action anymore.
- */
- @VisibleForTesting
- void closeControls(View v, boolean save) {
- int[] parentLoc = new int[2];
- int[] targetLoc = new int[2];
- mGutsContainer.getLocationOnScreen(parentLoc);
- v.getLocationOnScreen(targetLoc);
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- final int x = targetLoc[0] - parentLoc[0] + centerX;
- final int y = targetLoc[1] - parentLoc[1] + centerY;
- mGutsContainer.closeControls(x, y, save, false /* force */);
- }
-
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
index d1b405256f39..88c325880241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
@@ -21,19 +21,21 @@ import android.content.Context
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.text.SpannableStringBuilder
+import android.text.style.BulletSpan
import android.view.Gravity
import android.view.View
-import android.view.View.GONE
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.Window
import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
-import android.widget.LinearLayout
+import android.widget.ImageView
import android.widget.TextView
import com.android.systemui.Prefs
import com.android.systemui.R
-import java.lang.IllegalStateException
+import com.android.systemui.statusbar.notification.row.NotificationConversationInfo.OnConversationSettingsClickListener
import javax.inject.Inject
/**
@@ -43,7 +45,9 @@ class PriorityOnboardingDialogController @Inject constructor(
val view: View,
val context: Context,
val ignoresDnd: Boolean,
- val showsAsBubble: Boolean
+ val showsAsBubble: Boolean,
+ val icon : Drawable,
+ val onConversationSettingsClickListener : OnConversationSettingsClickListener
) {
private lateinit var dialog: Dialog
@@ -62,11 +66,21 @@ class PriorityOnboardingDialogController @Inject constructor(
dialog.dismiss()
}
+ private fun settings() {
+ // Log that the user has seen the onboarding
+ Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true)
+ dialog.dismiss()
+ onConversationSettingsClickListener?.onClick()
+ }
+
class Builder @Inject constructor() {
private lateinit var view: View
private lateinit var context: Context
private var ignoresDnd = false
private var showAsBubble = false
+ private lateinit var icon: Drawable
+ private lateinit var onConversationSettingsClickListener
+ : OnConversationSettingsClickListener
fun setView(v: View): Builder {
view = v
@@ -88,9 +102,20 @@ class PriorityOnboardingDialogController @Inject constructor(
return this
}
+ fun setIcon(draw : Drawable) : Builder {
+ icon = draw
+ return this
+ }
+
+ fun setOnSettingsClick(onClick : OnConversationSettingsClickListener) : Builder {
+ onConversationSettingsClickListener = onClick
+ return this
+ }
+
fun build(): PriorityOnboardingDialogController {
val controller = PriorityOnboardingDialogController(
- view, context, ignoresDnd, showAsBubble)
+ view, context, ignoresDnd, showAsBubble, icon,
+ onConversationSettingsClickListener)
return controller
}
}
@@ -113,13 +138,32 @@ class PriorityOnboardingDialogController @Inject constructor(
done()
}
- if (!ignoresDnd) {
- findViewById<LinearLayout>(R.id.ignore_dnd_tip).visibility = GONE
+ findViewById<TextView>(R.id.settings_button)?.setOnClickListener {
+ settings()
}
- if (!showsAsBubble) {
- findViewById<LinearLayout>(R.id.floating_bubble_tip).visibility = GONE
+ findViewById<ImageView>(R.id.conversation_icon)?.setImageDrawable(icon)
+
+ val gapWidth = dialog.context.getResources().getDimensionPixelSize(
+ R.dimen.conversation_onboarding_bullet_gap_width)
+ val description = SpannableStringBuilder()
+ description.append(context.getText(R.string.priority_onboarding_show_at_top_text),
+ BulletSpan(gapWidth), /* flags */0)
+ description.append(System.lineSeparator())
+ description.append(context.getText(R.string.priority_onboarding_show_avatar_text),
+ BulletSpan(gapWidth), /* flags */0)
+ if (showsAsBubble) {
+ description.append(System.lineSeparator())
+ description.append(context.getText(
+ R.string.priority_onboarding_appear_as_bubble_text),
+ BulletSpan(gapWidth), /* flags */0)
+ }
+ if (ignoresDnd) {
+ description.append(System.lineSeparator())
+ description.append(context.getText(R.string.priority_onboarding_ignores_dnd_text),
+ BulletSpan(gapWidth), /* flags */0)
}
+ findViewById<TextView>(R.id.behaviors).setText(description)
window?.apply {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
@@ -129,7 +173,7 @@ class PriorityOnboardingDialogController @Inject constructor(
attributes = attributes.apply {
format = PixelFormat.TRANSLUCENT
- title = ChannelEditorDialogController::class.java.simpleName
+ title = PriorityOnboardingDialogController::class.java.simpleName
gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
fitInsetsTypes = attributes.fitInsetsTypes and statusBars().inv()
width = MATCH_PARENT
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 15499b87d56d..fe70c818216e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -50,6 +50,7 @@ class NotificationConversationTemplateViewWrapper constructor(
private lateinit var conversationBadgeBg: View
private lateinit var expandButton: View
private lateinit var expandButtonContainer: View
+ private lateinit var expandButtonInnerContainer: View
private lateinit var imageMessageContainer: ViewGroup
private lateinit var messagingLinearLayout: MessagingLinearLayout
private lateinit var conversationTitleView: View
@@ -69,6 +70,8 @@ class NotificationConversationTemplateViewWrapper constructor(
expandButton = requireViewById(com.android.internal.R.id.expand_button)
expandButtonContainer =
requireViewById(com.android.internal.R.id.expand_button_container)
+ expandButtonInnerContainer =
+ requireViewById(com.android.internal.R.id.expand_button_inner_container)
importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
appName = requireViewById(com.android.internal.R.id.app_name_text)
conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
@@ -134,6 +137,8 @@ class NotificationConversationTemplateViewWrapper constructor(
)
}
+ override fun getExpandButton() = expandButtonInnerContainer
+
override fun setShelfIconVisible(visible: Boolean) {
if (conversationLayout.isImportantConversation) {
if (conversationIconView.visibility != GONE) {
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 f8b783113ccb..4c9cb209424a 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
@@ -317,6 +317,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
@Override
+ public View getExpandButton() {
+ return mExpandButton;
+ }
+
+ @Override
public int getOriginalIconColor() {
return mIcon.getOriginalIconColor();
}
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 02e537d2879f..30080e3d8cc2 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
@@ -240,6 +240,13 @@ public abstract class NotificationViewWrapper implements TransformableView {
return null;
}
+ /**
+ * @return the expand button if it exists
+ */
+ public @Nullable View getExpandButton() {
+ return null;
+ }
+
public int getOriginalIconColor() {
return Notification.COLOR_INVALID;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 46c873db8a08..4337e20c0a39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -40,6 +40,7 @@ import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseRelativeLayout;
import com.android.systemui.statusbar.policy.KeyButtonView;
+import java.io.PrintWriter;
import java.util.Objects;
public class NavigationBarInflaterView extends FrameLayout
@@ -469,4 +470,10 @@ public class NavigationBarInflaterView extends FrameLayout
private static float convertDpToPx(Context context, float dp) {
return dp * context.getResources().getDisplayMetrics().density;
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("NavigationBarInflaterView {");
+ pw.println(" mCurrentLayout: " + mCurrentLayout);
+ pw.println(" }");
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 2978772cac5e..6b37ac317cdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -1198,6 +1198,9 @@ public class NavigationBarView extends FrameLayout implements
pw.println(" }");
+ if (mNavigationInflaterView != null) {
+ mNavigationInflaterView.dump(pw);
+ }
mContextualButtonGroup.dump(pw);
mRecentsOnboarding.dump(pw);
mRegionSamplingHelper.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index f58cce58af74..76c51d61459a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -572,6 +572,9 @@ public class NotificationIconAreaController implements DarkReceiver,
.setInterpolator(Interpolators.LINEAR)
.setDuration(AOD_ICONS_APPEAR_DURATION)
.start();
+ } else {
+ mAodIcons.setAlpha(1.0f);
+ mAodIcons.setTranslationY(0);
}
}
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 b4de3cd5d43b..18a7adda3f7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -640,8 +640,7 @@ public class MobileSignalController extends SignalController<
+ " dataState=" + state.getDataRegistrationState());
}
mServiceState = state;
- // onDisplayInfoChanged is invoked directly after onServiceStateChanged, so not calling
- // updateTelephony() to prevent icon flickering in case of overrides.
+ updateTelephony();
}
@Override
@@ -651,12 +650,6 @@ public class MobileSignalController extends SignalController<
+ " type=" + networkType);
}
mDataState = state;
- if (networkType != mTelephonyDisplayInfo.getNetworkType()) {
- Log.d(mTag, "onDataConnectionStateChanged:"
- + " network type change and reset displayInfo. type=" + networkType);
- mTelephonyDisplayInfo = new TelephonyDisplayInfo(networkType,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
- }
updateTelephony();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java b/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
index 6c3538cb6142..a31ea7c3ab17 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
+++ b/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
@@ -40,7 +40,7 @@ public class DismissCircleView extends FrameLayout {
setBackground(res.getDrawable(R.drawable.dismiss_circle_background));
- mIconView.setImageDrawable(res.getDrawable(R.drawable.dismiss_target_x));
+ mIconView.setImageDrawable(res.getDrawable(R.drawable.ic_close_white));
addView(mIconView);
setViewSizes();
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index b1792d003290..5c9db54a0f00 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -133,4 +133,13 @@ public class Utils {
Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1);
return flag > 0;
}
+
+ /**
+ * Allow media resumption controls. Requires {@link #useQsMediaPlayer(Context)} to be enabled.
+ * Off by default, but can be enabled by setting to 1
+ */
+ public static boolean useMediaResumption(Context context) {
+ int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_resumption", 0);
+ return useQsMediaPlayer(context) && flag > 0;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 9d2b6f4deb14..1ba36e19b404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -67,7 +67,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var player: MediaControlPanel
- private lateinit var fgExecutor: FakeExecutor
private lateinit var bgExecutor: FakeExecutor
@Mock private lateinit var activityStarter: ActivityStarter
@@ -97,14 +96,12 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Before
fun setUp() {
- fgExecutor = FakeExecutor(FakeSystemClock())
bgExecutor = FakeExecutor(FakeSystemClock())
activityStarter = mock(ActivityStarter::class.java)
mediaHostStatesManager = mock(MediaHostStatesManager::class.java)
- player = MediaControlPanel(context, fgExecutor, bgExecutor, activityStarter,
- mediaHostStatesManager)
+ player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager)
// Mock out a view holder for the player to attach to.
holder = mock(PlayerViewHolder::class.java)
@@ -171,7 +168,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindWhenUnattached() {
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, null, null, device)
+ emptyList(), PACKAGE, null, null, device, null)
player.bind(state)
assertThat(player.isPlaying()).isFalse()
}
@@ -180,7 +177,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindText() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device)
+ emptyList(), PACKAGE, session.getSessionToken(), null, device, null)
player.bind(state)
assertThat(appName.getText()).isEqualTo(APP)
assertThat(titleText.getText()).isEqualTo(TITLE)
@@ -191,7 +188,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindBackgroundColor() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device)
+ emptyList(), PACKAGE, session.getSessionToken(), null, device, null)
player.bind(state)
val list = ArgumentCaptor.forClass(ColorStateList::class.java)
verify(view).setBackgroundTintList(list.capture())
@@ -202,7 +199,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindDevice() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device)
+ emptyList(), PACKAGE, session.getSessionToken(), null, device, null)
player.bind(state)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
assertThat(seamless.isEnabled()).isTrue()
@@ -212,7 +209,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindDisabledDevice() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice)
+ emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, null)
player.bind(state)
assertThat(seamless.isEnabled()).isFalse()
assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
@@ -223,7 +220,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindNullDevice() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, null)
+ emptyList(), PACKAGE, session.getSessionToken(), null, null, null)
player.bind(state)
assertThat(seamless.isEnabled()).isTrue()
assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 48e3b0a9d993..bed5c9eb6df5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -79,16 +79,16 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
mManager.addListener(mListener);
mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, KEY);
+ new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, null, KEY, false);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
@Test
public void eventNotEmittedWithoutDevice() {
// WHEN data source emits an event without device data
- mDataListener.onMediaDataLoaded(KEY, mMediaData);
+ mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// THEN an event isn't emitted
- verify(mListener, never()).onMediaDataLoaded(eq(KEY), any());
+ verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any());
}
@Test
@@ -96,7 +96,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
// WHEN device source emits an event without media data
mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
// THEN an event isn't emitted
- verify(mListener, never()).onMediaDataLoaded(eq(KEY), any());
+ verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any());
}
@Test
@@ -104,22 +104,22 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
// GIVEN that a device event has already been received
mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
// WHEN media event is received
- mDataListener.onMediaDataLoaded(KEY, mMediaData);
+ mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), captor.capture());
+ verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@Test
public void emitEventAfterMediaFirst() {
// GIVEN that media event has already been received
- mDataListener.onMediaDataLoaded(KEY, mMediaData);
+ mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// WHEN device event is received
mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), captor.capture());
+ verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -133,7 +133,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test
public void mediaDataRemovedAfterMediaEvent() {
- mDataListener.onMediaDataLoaded(KEY, mMediaData);
+ mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
mDataListener.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@@ -145,6 +145,18 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
verify(mListener).onMediaDataRemoved(eq(KEY));
}
+ @Test
+ public void mediaDataKeyUpdated() {
+ // GIVEN that device and media events have already been received
+ mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ // WHEN the key is changed
+ mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData);
+ // THEN the listener gets a load event with the correct keys
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq("NEW_KEY"), any(), captor.capture());
+ }
+
private MediaDataManager.Listener captureDataListener() {
ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass(
MediaDataManager.Listener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index c0aef8adc4af..3a3140f2ff53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -23,8 +23,6 @@ import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import android.media.session.MediaSession
import android.media.session.PlaybackState
-import android.os.Process
-import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -67,6 +65,7 @@ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
public class MediaDeviceManagerTest : SysuiTestCase() {
private lateinit var manager: MediaDeviceManager
+ @Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
@@ -80,13 +79,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
private lateinit var metadataBuilder: MediaMetadata.Builder
private lateinit var playbackBuilder: PlaybackState.Builder
private lateinit var notifBuilder: Notification.Builder
- private lateinit var sbn: StatusBarNotification
+ private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Before
fun setUp() {
fakeExecutor = FakeExecutor(FakeSystemClock())
- manager = MediaDeviceManager(context, lmmFactory, mr2, featureFlag, fakeExecutor)
+ manager = MediaDeviceManager(context, lmmFactory, mr2, featureFlag, fakeExecutor,
+ mediaDataManager)
manager.addListener(listener)
// Configure mocks.
@@ -117,8 +117,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
setSmallIcon(android.R.drawable.ic_media_pause)
setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken()))
}
- sbn = StatusBarNotification(PACKAGE, PACKAGE, 0, "TAG", Process.myUid(), 0, 0,
- notifBuilder.build(), Process.myUserHandle(), 0)
+ mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null,
+ emptyList(), emptyList(), PACKAGE, session.sessionToken, null, null, null)
}
@After
@@ -128,33 +128,33 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun removeUnknown() {
- manager.onNotificationRemoved("unknown")
+ manager.onMediaDataRemoved("unknown")
}
@Test
fun addNotification() {
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
verify(lmmFactory).create(PACKAGE)
}
@Test
fun featureDisabled() {
whenever(featureFlag.enabled).thenReturn(false)
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
verify(lmmFactory, never()).create(PACKAGE)
}
@Test
fun addAndRemoveNotification() {
- manager.onNotificationAdded(KEY, sbn)
- manager.onNotificationRemoved(KEY)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ manager.onMediaDataRemoved(KEY)
verify(lmm).unregisterCallback(any())
}
@Test
fun deviceEventOnAddNotification() {
// WHEN a notification is added
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
val deviceCallback = captureCallback()
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
@@ -165,7 +165,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun deviceListUpdate() {
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
val deviceCallback = captureCallback()
// WHEN the device list changes
deviceCallback.onDeviceListUpdate(mutableListOf(device))
@@ -179,7 +179,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun selectedDeviceStateChanged() {
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
val deviceCallback = captureCallback()
// WHEN the selected device changes state
deviceCallback.onSelectedDeviceStateChanged(device, 1)
@@ -193,9 +193,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun listenerReceivesKeyRemoved() {
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
// WHEN the notification is removed
- manager.onNotificationRemoved(KEY)
+ manager.onMediaDataRemoved(KEY)
// THEN the listener receives key removed event
verify(listener).onKeyRemoved(eq(KEY))
}
@@ -205,7 +205,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
// GIVEN that MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN a notification is added
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
@@ -216,7 +216,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
// GIVEN a notif is added
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
reset(listener)
// AND MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
@@ -234,7 +234,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
// GIVEN a notif is added
- manager.onNotificationAdded(KEY, sbn)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
reset(listener)
// GIVEN that MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index c21343cb5423..643a3352c30c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.media
+import android.media.MediaMetadata
import android.media.session.MediaController
+import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -41,6 +43,10 @@ import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
private const val KEY = "KEY"
+private const val PACKAGE = "PKG"
+private const val SESSION_KEY = "SESSION_KEY"
+private const val SESSION_ARTIST = "SESSION_ARTIST"
+private const val SESSION_TITLE = "SESSION_TITLE"
private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
private fun <T> anyObject(): T {
@@ -54,12 +60,15 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
@Mock private lateinit var mediaControllerFactory: MediaControllerFactory
@Mock private lateinit var mediaController: MediaController
@Mock private lateinit var executor: DelayableExecutor
- @Mock private lateinit var mediaData: MediaData
@Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
@Mock private lateinit var cancellationRunnable: Runnable
@Captor private lateinit var timeoutCaptor: ArgumentCaptor<Runnable>
@Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback>
@JvmField @Rule val mockito = MockitoJUnit.rule()
+ private lateinit var metadataBuilder: MediaMetadata.Builder
+ private lateinit var playbackBuilder: PlaybackState.Builder
+ private lateinit var session: MediaSession
+ private lateinit var mediaData: MediaData
private lateinit var mediaTimeoutListener: MediaTimeoutListener
@Before
@@ -68,22 +77,39 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
`when`(executor.executeDelayed(any(), anyLong())).thenReturn(cancellationRunnable)
mediaTimeoutListener = MediaTimeoutListener(mediaControllerFactory, executor)
mediaTimeoutListener.timeoutCallback = timeoutCallback
+
+ // Create a media session and notification for testing.
+ metadataBuilder = MediaMetadata.Builder().apply {
+ putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+ putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+ }
+ playbackBuilder = PlaybackState.Builder().apply {
+ setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
+ setActions(PlaybackState.ACTION_PLAY)
+ }
+ session = MediaSession(context, SESSION_KEY).apply {
+ setMetadata(metadataBuilder.build())
+ setPlaybackState(playbackBuilder.build())
+ }
+ session.setActive(true)
+ mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null,
+ emptyList(), emptyList(), PACKAGE, session.sessionToken, null, null, null)
}
@Test
fun testOnMediaDataLoaded_registersPlaybackListener() {
- mediaTimeoutListener.onMediaDataLoaded(KEY, mediaData)
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
// Ignores is same key
clearInvocations(mediaController)
- mediaTimeoutListener.onMediaDataLoaded(KEY, mediaData)
+ mediaTimeoutListener.onMediaDataLoaded(KEY, KEY, mediaData)
verify(mediaController, never()).registerCallback(anyObject())
}
@Test
fun testOnMediaDataRemoved_unregistersPlaybackListener() {
- mediaTimeoutListener.onMediaDataLoaded(KEY, mediaData)
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
mediaTimeoutListener.onMediaDataRemoved(KEY)
verify(mediaController).unregisterCallback(anyObject())
@@ -124,7 +150,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
@Test
fun testIsTimedOut() {
- mediaTimeoutListener.onMediaDataLoaded(KEY, mediaData)
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
assertThat(mediaTimeoutListener.isTimedOut(KEY)).isFalse()
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
index dd5cb585d0aa..ec73a7571969 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
@@ -144,7 +144,7 @@ public class AppOpsInfoTest extends SysuiTestCase {
final View okButton = mAppOpsInfo.findViewById(R.id.ok);
okButton.performClick();
assertEquals(1, latch.getCount());
- verify(mGutsParent, times(1)).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index b018b59e4389..ed4f8b330e23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -30,11 +29,13 @@ import android.app.AppOpsManager;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
import android.view.View;
+import android.view.ViewPropertyAnimator;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -98,4 +99,42 @@ public class NotificationContentViewTest extends SysuiTestCase {
verify(mockExpanded, times(1)).setVisibility(View.VISIBLE);
verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE);
}
+
+ @Test
+ @UiThreadTest
+ public void testExpandButtonFocusIsCalled() {
+ View mockContractedEB = mock(NotificationExpandButton.class);
+ View mockContracted = mock(NotificationHeaderView.class);
+ when(mockContracted.animate()).thenReturn(mock(ViewPropertyAnimator.class));
+ when(mockContracted.findViewById(com.android.internal.R.id.expand_button)).thenReturn(
+ mockContractedEB);
+
+ View mockExpandedEB = mock(NotificationExpandButton.class);
+ View mockExpanded = mock(NotificationHeaderView.class);
+ when(mockExpanded.animate()).thenReturn(mock(ViewPropertyAnimator.class));
+ when(mockExpanded.findViewById(com.android.internal.R.id.expand_button)).thenReturn(
+ mockExpandedEB);
+
+ View mockHeadsUpEB = mock(NotificationExpandButton.class);
+ View mockHeadsUp = mock(NotificationHeaderView.class);
+ when(mockHeadsUp.animate()).thenReturn(mock(ViewPropertyAnimator.class));
+ when(mockHeadsUp.findViewById(com.android.internal.R.id.expand_button)).thenReturn(
+ mockHeadsUpEB);
+
+ // Set up all 3 child forms
+ mView.setContractedChild(mockContracted);
+ mView.setExpandedChild(mockExpanded);
+ mView.setHeadsUpChild(mockHeadsUp);
+
+ // This is required to call requestAccessibilityFocus()
+ mView.setFocusOnVisibilityChange();
+
+ // The following will initialize the view and switch from not visible to expanded.
+ // (heads-up is actually an alternate form of contracted, hence this enters expanded state)
+ mView.setHeadsUp(true);
+
+ verify(mockContractedEB, times(0)).requestAccessibilityFocus();
+ verify(mockExpandedEB, times(1)).requestAccessibilityFocus();
+ verify(mockHeadsUpEB, times(0)).requestAccessibilityFocus();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index b39de34f6c63..4122cf5466e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -173,7 +173,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
doAnswer((Answer<Object>) invocation -> {
mNotificationInfo.handleCloseControls(true, false);
return null;
- }).when(mNotificationGuts).closeControls(anyInt(), anyInt(), eq(true), eq(false));
+ }).when(mNotificationGuts).closeControls(any(View.class), eq(true));
// Our view is never attached to a window so the View#post methods in NotificationInfo never
// get called. Setting this will skip the post and do the action immediately.
mNotificationInfo.mSkipPost = true;
@@ -256,7 +256,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
assertEquals(mIconDrawable, view.getDrawable());
}
@@ -280,7 +280,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -331,7 +331,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertTrue(textView.getText().toString().contains(group.getName()));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -356,7 +356,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
assertEquals(GONE, textView.getVisibility());
@@ -380,7 +380,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -415,7 +415,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -443,7 +443,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -469,7 +469,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -496,7 +496,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
false,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -521,7 +521,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View view = mNotificationInfo.findViewById(R.id.silence);
assertThat(view.isSelected()).isTrue();
}
@@ -549,7 +549,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -580,7 +580,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -610,7 +610,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -654,7 +654,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mTestableLooper.processAllMessages();
@@ -697,7 +697,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -741,7 +741,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -778,7 +778,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -793,6 +793,45 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
}
@Test
+ public void testFavorite_thenDefaultThenFavorite_andSave_nothingChanged() throws Exception {
+ mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH);
+ mConversationChannel.setImportance(IMPORTANCE_HIGH);
+ mConversationChannel.setImportantConversation(true);
+
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ mBubbleMetadata,
+ null,
+ null,
+ mIconFactory,
+ mContext,
+ mBuilderProvider,
+ true,
+ mTestHandler,
+ mTestHandler, null);
+
+ View fave = mNotificationInfo.findViewById(R.id.priority);
+ fave.performClick();
+ mNotificationInfo.findViewById(R.id.default_behavior).performClick();
+ fave.performClick();
+ mNotificationInfo.findViewById(R.id.done).performClick();
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<NotificationChannel> captor =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), anyInt(), captor.capture());
+ assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance());
+ assertTrue(captor.getValue().isImportantConversation());
+ }
+
+ @Test
public void testDefault_andSave() throws Exception {
mConversationChannel.setAllowBubbles(true);
mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH);
@@ -813,7 +852,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -849,7 +888,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -885,7 +924,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -920,7 +959,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View silence = mNotificationInfo.findViewById(R.id.silence);
silence.performClick();
@@ -954,7 +993,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
@@ -979,7 +1018,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
@@ -1014,10 +1053,14 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
+ verify(controller, never()).show();
+
+ // and then done
+ mNotificationInfo.findViewById(R.id.done).performClick();
// THEN the user is presented with the priority onboarding screen
verify(controller, atLeastOnce()).show();
@@ -1050,7 +1093,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index e56ef5b92f87..f327967ebd73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -161,25 +161,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
}
@Test
- public void testBindNotification_SetsTextApplicationName() throws Exception {
- when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
- mInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- mNotificationChannelSet,
- mEntry,
- null,
- true,
- false);
- final TextView textView = mInfo.findViewById(R.id.pkg_name);
- assertTrue(textView.getText().toString().contains("App Name"));
- assertEquals(VISIBLE, mInfo.findViewById(R.id.header).getVisibility());
- }
-
- @Test
public void testBindNotification_SetsName() {
mInfo.bindNotification(
mMockPackageManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index be43e19cfc70..177e845bfead 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -19,6 +19,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -56,6 +57,8 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase {
@Mock
NotificationMediaManager mNotificationMediaManager;
@Mock
+ NotificationIconContainer mNotificationIconContainer;
+ @Mock
DozeParameters mDozeParameters;
@Mock
NotificationShadeWindowView mNotificationShadeWindowView;
@@ -67,7 +70,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase {
when(mStatusBar.getNotificationShadeWindowView()).thenReturn(mNotificationShadeWindowView);
when(mNotificationShadeWindowView.findViewById(anyInt())).thenReturn(
- mock(NotificationIconContainer.class));
+ mNotificationIconContainer);
mController = new NotificationIconAreaController(mContext, mStatusBar,
mStatusBarStateController, mWakeUpCoordinator, mKeyguardBypassController,
@@ -87,4 +90,12 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase {
assertTrue(mController.shouldShouldLowPriorityIcons());
}
+
+ @Test
+ public void testAppearResetsTranslation() {
+ when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
+ mController.appearAodIcons();
+ verify(mNotificationIconContainer).setTranslationY(0);
+ verify(mNotificationIconContainer).setAlpha(1.0f);
+ }
}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index f08429bb0696..3fd9ee9a330b 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -730,12 +730,7 @@ public class IpServer extends StateMachine {
final String upstreamIface = v6only.getInterfaceName();
params = new RaParams();
- // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest
- // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering
- // offload happy. This hack should be reverted once we have the kernel fixed up.
- // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
- // see RouterAdvertisementDaemon.java putMtu()
- params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
+ params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index e2330ca6ffe9..0ec8654f2a20 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -75,9 +75,7 @@ final class AutofillInlineSessionController {
@NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
// TODO(b/151123764): rename the method to better reflect what it does.
if (mSession != null) {
- // Send an empty response to IME and destroy the existing session.
- mSession.onInlineSuggestionsResponseLocked(
- InlineFillUi.emptyUi(mSession.getAutofillIdLocked()));
+ // Destroy the existing session.
mSession.destroySessionLocked();
mInlineFillUi = null;
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3c0d880916ee..0d4efed25da3 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -954,7 +954,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
/**
- * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on.
+ * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on,
+ * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off.
*/
private void continueFromBleOnState() {
if (DBG) {
@@ -966,11 +967,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!");
return;
}
- if (!mEnableExternal && !isBleAppPresent() && isAirplaneModeOn()) {
- // Airplane mode is turned on while enabling BLE only mode, disable
- // BLE now.
- disableBleScanMode();
- sendBrEdrDownCallback();
+ if (!mEnableExternal && !isBleAppPresent()) {
+ Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now");
+ mEnable = false;
+ mBluetooth.onBrEdrDown();
return;
}
if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1634f6e62897..0ab571854c72 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1374,10 +1374,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
return;
}
- String action = blocked ? "BLOCKED" : "UNBLOCKED";
- log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
- nri.mUid, nri.request.requestId, net.netId));
- mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+ final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ mNetworkInfoBlockingLogs.log(String.format(
+ "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.netId));
}
/**
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index e066d99147ba..a153d4191a5c 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1894,6 +1894,8 @@ public class VibratorService extends IVibratorService.Stub
return runWaveform();
} else if ("prebaked".equals(cmd)) {
return runPrebaked();
+ } else if ("capabilities".equals(cmd)) {
+ return runCapabilities();
} else if ("cancel".equals(cmd)) {
cancelVibrate(mToken);
return 0;
@@ -2016,10 +2018,15 @@ public class VibratorService extends IVibratorService.Stub
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
try {
CommonOptions commonOptions = new CommonOptions();
+ boolean shouldFallback = false;
String opt;
while ((opt = getNextOption()) != null) {
- commonOptions.check(opt);
+ if ("-b".equals(opt)) {
+ shouldFallback = true;
+ } else {
+ commonOptions.check(opt);
+ }
}
if (checkDoNotDisturb(commonOptions)) {
@@ -2033,8 +2040,7 @@ public class VibratorService extends IVibratorService.Stub
description = "Shell command";
}
- VibrationEffect effect =
- VibrationEffect.get(id, false);
+ VibrationEffect effect = VibrationEffect.get(id, shouldFallback);
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
mToken);
@@ -2044,12 +2050,39 @@ public class VibratorService extends IVibratorService.Stub
}
}
+ private int runCapabilities() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runCapabilities");
+ try (PrintWriter pw = getOutPrintWriter();) {
+ pw.println("Vibrator capabilities:");
+ if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
+ pw.println(" Always on effects");
+ }
+ if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+ pw.println(" Compose effects");
+ }
+ if (mSupportsAmplitudeControl || hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
+ pw.println(" Amplitude control");
+ }
+ if (mSupportsExternalControl || hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+ pw.println(" External control");
+ }
+ if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
+ pw.println(" External amplitude control");
+ }
+ pw.println("");
+ return 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
final int flags = commonOptions.force
? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
: 0;
return new VibrationAttributes.Builder()
- .setUsage(VibrationAttributes.USAGE_UNKNOWN)
+ // Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects.
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
.replaceFlags(flags)
.build();
}
@@ -2062,19 +2095,26 @@ public class VibratorService extends IVibratorService.Stub
pw.println(" Prints this help text.");
pw.println("");
pw.println(" vibrate duration [description]");
- pw.println(" Vibrates for duration milliseconds; ignored when device is on DND ");
- pw.println(" (Do Not Disturb) mode.");
+ pw.println(" Vibrates for duration milliseconds; ignored when device is on ");
+ pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale amplitude.");
pw.println(" waveform [-d description] [-r index] [-a] duration [amplitude] ...");
- pw.println(" Vibrates for durations and amplitudes in list;");
- pw.println(" ignored when device is on DND (Do Not Disturb) mode.");
+ pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
+ pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
+ pw.println(" user setting will be used to scale amplitude.");
pw.println(" If -r is provided, the waveform loops back to the specified");
pw.println(" index (e.g. 0 loops from the beginning)");
pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
pw.println(" otherwise, it accepts durations only and alternates off/on");
pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
- pw.println(" prebaked effect-id [description]");
+ pw.println(" prebaked [-b] effect-id [description]");
pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
- pw.println(" (Do Not Disturb) mode.");
+ pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
+ pw.println(" will be used to scale amplitude.");
+ pw.println(" If -b is provided, the prebaked fallback effect will be played if");
+ pw.println(" the device doesn't support the given effect-id.");
+ pw.println(" capabilities");
+ pw.println(" Prints capabilities of this device.");
pw.println(" cancel");
pw.println(" Cancels any active vibration");
pw.println("Common Options:");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index efd3c3e9bfc1..27d9ba08e4a2 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1908,8 +1908,11 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
+ boolean hasModifyAudioSettings =
+ mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED;
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
- caller, Binder.getCallingUid(), hasModifyAudioSettings(), VOL_ADJUST_NORMAL);
+ caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
@@ -2014,10 +2017,13 @@ public class AudioService extends IAudioService.Stub
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
return;
}
+ final boolean hasModifyAudioSettings =
+ mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED;
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), hasModifyAudioSettings(), VOL_ADJUST_NORMAL);
+ Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
protected void adjustStreamVolume(int streamType, int direction, int flags,
@@ -2528,10 +2534,13 @@ public class AudioService extends IAudioService.Stub
+ " MODIFY_AUDIO_ROUTING callingPackage=" + callingPackage);
return;
}
+ final boolean hasModifyAudioSettings =
+ mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED;
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), hasModifyAudioSettings());
+ Binder.getCallingUid(), hasModifyAudioSettings);
}
private boolean canChangeAccessibilityVolume() {
@@ -3197,7 +3206,8 @@ public class AudioService extends IAudioService.Stub
ensureValidStreamType(streamType);
final boolean isPrivileged =
Binder.getCallingUid() == Process.SYSTEM_UID
- || (hasModifyAudioSettings())
+ || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED)
|| (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
== PackageManager.PERMISSION_GRANTED);
return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
@@ -4755,18 +4765,9 @@ public class AudioService extends IAudioService.Stub
handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
}
- private boolean hasModifyAudioSettings() {
- return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- private boolean hasModifyAudioSettings(int pid, int uid) {
- return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
boolean checkAudioSettingsPermission(String method) {
- if (hasModifyAudioSettings()) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED) {
return true;
}
String msg = "Audio Settings Permission Denial: " + method + " from pid="
@@ -7688,10 +7689,13 @@ public class AudioService extends IAudioService.Stub
@Override
public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
String callingPackage, int uid, int pid) {
+ final boolean hasModifyAudioSettings =
+ mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
// direction and stream type swap here because the public
// adjustSuggested has a different order than the other methods.
adjustSuggestedStreamVolume(direction, streamType, flags, callingPackage,
- callingPackage, uid, hasModifyAudioSettings(pid, uid), VOL_ADJUST_NORMAL);
+ callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
@Override
@@ -7702,15 +7706,21 @@ public class AudioService extends IAudioService.Stub
direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
.append(" uid:").append(uid).toString()));
}
+ final boolean hasModifyAudioSettings =
+ mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
adjustStreamVolume(streamType, direction, flags, callingPackage,
- callingPackage, uid, hasModifyAudioSettings(pid, uid), VOL_ADJUST_NORMAL);
+ callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
@Override
public void setStreamVolumeForUid(int streamType, int direction, int flags,
String callingPackage, int uid, int pid) {
+ final boolean hasModifyAudioSettings =
+ mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
setStreamVolume(streamType, direction, flags, callingPackage, callingPackage, uid,
- hasModifyAudioSettings(pid, uid));
+ hasModifyAudioSettings);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9de95abafdda..b9669c74a6df 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -40,6 +40,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
@@ -63,6 +64,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -268,6 +270,11 @@ public class HdmiControlService extends SystemService {
private final ArrayList<HdmiControlStatusChangeListenerRecord>
mHdmiControlStatusChangeListenerRecords = new ArrayList<>();
+ // List of records for HDMI control volume control status change listener for death monitoring.
+ @GuardedBy("mLock")
+ private final RemoteCallbackList<IHdmiCecVolumeControlFeatureListener>
+ mHdmiCecVolumeControlFeatureListenerRecords = new RemoteCallbackList<>();
+
// List of records for hotplug event listener to handle the the caller killed in action.
@GuardedBy("mLock")
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
@@ -1814,6 +1821,21 @@ public class HdmiControlService extends SystemService {
}
@Override
+ public void addHdmiCecVolumeControlFeatureListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.addHdmiCecVolumeControlFeatureListener(listener);
+ }
+
+ @Override
+ public void removeHdmiCecVolumeControlFeatureListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.removeHdmiControlVolumeControlStatusChangeListener(listener);
+ }
+
+
+ @Override
public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
HdmiControlService.this.addHotplugEventListener(listener);
@@ -2409,6 +2431,33 @@ public class HdmiControlService extends SystemService {
}
}
+ @VisibleForTesting
+ void addHdmiCecVolumeControlFeatureListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ mHdmiCecVolumeControlFeatureListenerRecords.register(listener);
+
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ // Return the current status of mHdmiCecVolumeControlEnabled;
+ synchronized (mLock) {
+ try {
+ listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControlEnabled);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report HdmiControlVolumeControlStatusChange: "
+ + mHdmiCecVolumeControlEnabled, e);
+ }
+ }
+ }
+ });
+ }
+
+ @VisibleForTesting
+ void removeHdmiControlVolumeControlStatusChangeListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ mHdmiCecVolumeControlFeatureListenerRecords.unregister(listener);
+ }
+
private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
try {
@@ -2682,6 +2731,19 @@ public class HdmiControlService extends SystemService {
}
}
+ private void announceHdmiCecVolumeControlFeatureChange(boolean isEnabled) {
+ assertRunOnServiceThread();
+ mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
+ try {
+ listener.onHdmiCecVolumeControlFeature(isEnabled);
+ } catch (RemoteException e) {
+ Slog.e(TAG,
+ "Failed to report HdmiControlVolumeControlStatusChange: "
+ + isEnabled);
+ }
+ });
+ }
+
public HdmiCecLocalDeviceTv tv() {
return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
}
@@ -3026,6 +3088,7 @@ public class HdmiControlService extends SystemService {
isHdmiCecVolumeControlEnabled);
}
}
+ announceHdmiCecVolumeControlFeatureChange(isHdmiCecVolumeControlEnabled);
}
boolean isHdmiCecVolumeControlEnabled() {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index ccbe96f30e04..067bdcb111fb 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -28,6 +28,9 @@ import static android.os.PowerManager.locationPowerSaveModeToString;
import static com.android.server.location.CallerIdentity.PERMISSION_COARSE;
import static com.android.server.location.CallerIdentity.PERMISSION_FINE;
+import static com.android.server.location.UserInfoHelper.UserListener.CURRENT_USER_CHANGED;
+import static com.android.server.location.UserInfoHelper.UserListener.USER_STARTED;
+import static com.android.server.location.UserInfoHelper.UserListener.USER_STOPPED;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -64,6 +67,7 @@ import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
@@ -101,7 +105,7 @@ import com.android.server.location.AbstractLocationProvider.State;
import com.android.server.location.CallerIdentity.PermissionLevel;
import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
import com.android.server.location.LocationRequestStatistics.PackageStatistics;
-import com.android.server.location.UserInfoHelper.UserListener;
+import com.android.server.location.UserInfoHelper.UserListener.UserChange;
import com.android.server.location.gnss.GnssManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -132,11 +136,13 @@ public class LocationManagerService extends ILocationManager.Stub {
*/
public static class Lifecycle extends SystemService {
+ private final UserInfoHelper mUserInfoHelper;
private final LocationManagerService mService;
public Lifecycle(Context context) {
super(context);
- mService = new LocationManagerService(context);
+ mUserInfoHelper = new SystemUserInfoHelper(context);
+ mService = new LocationManagerService(context, mUserInfoHelper);
}
@Override
@@ -161,6 +167,29 @@ public class LocationManagerService extends ILocationManager.Stub {
mService.onSystemThirdPartyAppsCanStart();
}
}
+
+ @Override
+ public void onUserStarting(TargetUser user) {
+ mUserInfoHelper.dispatchOnUserStarted(user.getUserIdentifier());
+ }
+
+ @Override
+ public void onUserSwitching(TargetUser from, TargetUser to) {
+ mUserInfoHelper.dispatchOnCurrentUserChanged(from.getUserIdentifier(),
+ to.getUserIdentifier());
+ }
+
+ @Override
+ public void onUserStopped(TargetUser user) {
+ mUserInfoHelper.dispatchOnUserStopped(user.getUserIdentifier());
+ }
+
+ private static class SystemUserInfoHelper extends UserInfoHelper {
+
+ SystemUserInfoHelper(Context context) {
+ super(context);
+ }
+ }
}
public static final String TAG = "LocationManagerService";
@@ -232,7 +261,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@PowerManager.LocationPowerSaveMode
private int mBatterySaverMode;
- private LocationManagerService(Context context) {
+ private LocationManagerService(Context context, UserInfoHelper userInfoHelper) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mHandler = FgThread.getHandler();
mLocalService = new LocalService();
@@ -240,7 +269,7 @@ public class LocationManagerService extends ILocationManager.Stub {
LocalServices.addService(LocationManagerInternal.class, mLocalService);
mAppOpsHelper = new AppOpsHelper(mContext);
- mUserInfoHelper = new UserInfoHelper(mContext);
+ mUserInfoHelper = userInfoHelper;
mSettingsHelper = new SettingsHelper(mContext, mHandler);
mAppForegroundHelper = new AppForegroundHelper(mContext);
mLocationUsageLogger = new LocationUsageLogger();
@@ -342,7 +371,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// initialize the current users. we would get the user started notifications for these
// users eventually anyways, but this takes care of it as early as possible.
for (int userId: mUserInfoHelper.getCurrentUserIds()) {
- onUserChanged(userId, UserListener.USER_STARTED);
+ onUserChanged(userId, USER_STARTED);
}
}
}
@@ -596,32 +625,23 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
+ private void onUserChanged(@UserIdInt int userId, @UserChange int change) {
switch (change) {
- case UserListener.USER_SWITCHED:
- if (D) {
- Log.d(TAG, "user " + userId + " current status changed");
- }
+ case CURRENT_USER_CHANGED:
synchronized (mLock) {
for (LocationProviderManager manager : mProviderManagers) {
manager.onEnabledChangedLocked(userId);
}
}
break;
- case UserListener.USER_STARTED:
- if (D) {
- Log.d(TAG, "user " + userId + " started");
- }
+ case USER_STARTED:
synchronized (mLock) {
for (LocationProviderManager manager : mProviderManagers) {
manager.onUserStarted(userId);
}
}
break;
- case UserListener.USER_STOPPED:
- if (D) {
- Log.d(TAG, "user " + userId + " stopped");
- }
+ case USER_STOPPED:
synchronized (mLock) {
for (LocationProviderManager manager : mProviderManagers) {
manager.onUserStopped(userId);
@@ -957,10 +977,22 @@ public class LocationManagerService extends ILocationManager.Stub {
pw.increaseIndent();
// for now we only dump for the parent user
- int userId = mUserInfoHelper.getCurrentUserIds()[0];
- pw.println("last location=" + mLastLocation.get(userId));
- pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
- pw.println("enabled=" + isEnabled(userId));
+ int[] userIds = mUserInfoHelper.getCurrentUserIds();
+ if (userIds.length == 1) {
+ int userId = userIds[0];
+ pw.println("last location=" + mLastLocation.get(userId));
+ pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
+ pw.println("enabled=" + isEnabled(userId));
+ } else {
+ for (int userId : userIds) {
+ pw.println("user " + userId + ":");
+ pw.increaseIndent();
+ pw.println("last location=" + mLastLocation.get(userId));
+ pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
+ pw.println("enabled=" + isEnabled(userId));
+ pw.decreaseIndent();
+ }
+ }
}
mProvider.dump(fd, pw, args);
@@ -1666,6 +1698,9 @@ public class LocationManagerService extends ILocationManager.Stub {
* Note: must be constructed with lock held.
*/
private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
mExpirationRealtimeMs = request.getExpirationRealtimeMs(SystemClock.elapsedRealtime());
mProvider = provider;
mRealRequest = request;
@@ -1703,6 +1738,10 @@ public class LocationManagerService extends ILocationManager.Stub {
* Method to be called when a record will no longer be used.
*/
private void disposeLocked(boolean removeReceiver) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
CallerIdentity identity = mReceiver.mCallerIdentity;
mRequestStatistics.stopRequesting(identity.packageName, identity.featureId, mProvider);
diff --git a/services/core/java/com/android/server/location/UserInfoHelper.java b/services/core/java/com/android/server/location/UserInfoHelper.java
index a3dcc40bdf2d..53bff8eacb4c 100644
--- a/services/core/java/com/android/server/location/UserInfoHelper.java
+++ b/services/core/java/com/android/server/location/UserInfoHelper.java
@@ -20,48 +20,48 @@ import static android.os.UserManager.DISALLOW_SHARE_LOCATION;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.UserInfoHelper.UserListener.CURRENT_USER_CHANGED;
+import static com.android.server.location.UserInfoHelper.UserListener.USER_STARTED;
+import static com.android.server.location.UserInfoHelper.UserListener.USER_STOPPED;
+import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
+import android.app.ActivityManagerInternal;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.Binder;
-import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import com.android.server.FgThread;
+import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Provides accessors and listeners for all user info.
*/
-public class UserInfoHelper {
+public abstract class UserInfoHelper {
/**
* Listener for current user changes.
*/
public interface UserListener {
- int USER_SWITCHED = 1;
+ int CURRENT_USER_CHANGED = 1;
int USER_STARTED = 2;
int USER_STOPPED = 3;
- @IntDef({USER_SWITCHED, USER_STARTED, USER_STOPPED})
+ @IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED})
@Retention(RetentionPolicy.SOURCE)
@interface UserChange {}
@@ -75,143 +75,101 @@ public class UserInfoHelper {
private final CopyOnWriteArrayList<UserListener> mListeners;
@GuardedBy("this")
- @Nullable private UserManager mUserManager;
-
- @UserIdInt private volatile int mCurrentUserId;
-
+ @Nullable private ActivityManagerInternal mActivityManagerInternal;
@GuardedBy("this")
- @UserIdInt private int mCachedParentUserId;
- @GuardedBy("this")
- private int[] mCachedProfileUserIds;
+ @Nullable private UserManager mUserManager;
public UserInfoHelper(Context context) {
mContext = context;
mListeners = new CopyOnWriteArrayList<>();
-
- mCurrentUserId = UserHandle.USER_NULL;
- mCachedParentUserId = UserHandle.USER_NULL;
- mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
}
/** Called when system is ready. */
+ @CallSuper
public synchronized void onSystemReady() {
- if (mUserManager != null) {
+ if (mActivityManagerInternal != null) {
return;
}
+ mActivityManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(ActivityManagerInternal.class));
mUserManager = mContext.getSystemService(UserManager.class);
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- intentFilter.addAction(Intent.ACTION_USER_STARTED);
- intentFilter.addAction(Intent.ACTION_USER_STOPPED);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action == null) {
- return;
- }
- int userId;
- switch (action) {
- case Intent.ACTION_USER_SWITCHED:
- userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- if (userId != UserHandle.USER_NULL) {
- onCurrentUserChanged(userId);
- }
- break;
- case Intent.ACTION_USER_STARTED:
- userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- if (userId != UserHandle.USER_NULL) {
- onUserStarted(userId);
- }
- break;
- case Intent.ACTION_USER_STOPPED:
- userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- if (userId != UserHandle.USER_NULL) {
- onUserStopped(userId);
- }
- break;
- case Intent.ACTION_MANAGED_PROFILE_ADDED:
- case Intent.ACTION_MANAGED_PROFILE_REMOVED:
- onUserProfilesChanged();
- break;
- }
- }
- }, UserHandle.ALL, intentFilter, null, FgThread.getHandler());
-
- mCurrentUserId = ActivityManager.getCurrentUser();
}
/**
* Adds a listener for user changed events. Callbacks occur on an unspecified thread.
*/
- public void addListener(UserListener listener) {
+ public final void addListener(UserListener listener) {
mListeners.add(listener);
}
/**
* Removes a listener for user changed events.
*/
- public void removeListener(UserListener listener) {
+ public final void removeListener(UserListener listener) {
mListeners.remove(listener);
}
- private void onCurrentUserChanged(@UserIdInt int newUserId) {
- if (newUserId == mCurrentUserId) {
- return;
- }
-
+ protected void dispatchOnUserStarted(@UserIdInt int userId) {
if (D) {
- Log.d(TAG, "current user switched from u" + mCurrentUserId + " to u" + newUserId);
+ Log.d(TAG, "u" + userId + " started");
}
- int oldUserId = mCurrentUserId;
- mCurrentUserId = newUserId;
-
- onUserChanged(oldUserId, UserListener.USER_SWITCHED);
- onUserChanged(newUserId, UserListener.USER_SWITCHED);
+ for (UserListener listener : mListeners) {
+ listener.onUserChanged(userId, USER_STARTED);
+ }
}
- private void onUserStarted(@UserIdInt int userId) {
+ protected void dispatchOnUserStopped(@UserIdInt int userId) {
if (D) {
- Log.d(TAG, "u" + userId + " started");
+ Log.d(TAG, "u" + userId + " stopped");
}
- onUserChanged(userId, UserListener.USER_STARTED);
+ for (UserListener listener : mListeners) {
+ listener.onUserChanged(userId, USER_STOPPED);
+ }
}
- private void onUserStopped(@UserIdInt int userId) {
+ protected void dispatchOnCurrentUserChanged(@UserIdInt int fromUserId,
+ @UserIdInt int toUserId) {
+ int[] fromUserIds = getProfileIds(fromUserId);
+ int[] toUserIds = getProfileIds(toUserId);
if (D) {
- Log.d(TAG, "u" + userId + " stopped");
+ Log.d(TAG, "current user changed from u" + Arrays.toString(fromUserIds) + " to u"
+ + Arrays.toString(toUserIds));
}
- onUserChanged(userId, UserListener.USER_STOPPED);
- }
-
- private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
for (UserListener listener : mListeners) {
- listener.onUserChanged(userId, change);
+ for (int userId : fromUserIds) {
+ listener.onUserChanged(userId, CURRENT_USER_CHANGED);
+ }
}
- }
- private synchronized void onUserProfilesChanged() {
- // this intent is only sent to the current user
- if (mCachedParentUserId == mCurrentUserId) {
- mCachedParentUserId = UserHandle.USER_NULL;
- mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
+ for (UserListener listener : mListeners) {
+ for (int userId : toUserIds) {
+ listener.onUserChanged(userId, CURRENT_USER_CHANGED);
+ }
}
}
/**
* Returns an array of current user ids. This will always include the current user, and will
- * also include any profiles of the current user.
+ * also include any profiles of the current user. The caller must never mutate the returned
+ * array.
*/
public int[] getCurrentUserIds() {
- return getProfileUserIdsForParentUser(mCurrentUserId);
+ synchronized (this) {
+ if (mActivityManagerInternal == null) {
+ return new int[] {};
+ }
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mActivityManagerInternal.getCurrentProfileIds();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
@@ -219,54 +177,47 @@ public class UserInfoHelper {
* user.
*/
public boolean isCurrentUserId(@UserIdInt int userId) {
- int currentUserId = mCurrentUserId;
- return userId == currentUserId || ArrayUtils.contains(
- getProfileUserIdsForParentUser(currentUserId), userId);
- }
+ synchronized (this) {
+ if (mActivityManagerInternal == null) {
+ return false;
+ }
+ }
- @GuardedBy("this")
- private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
- if (parentUserId != mCachedParentUserId) {
- long identity = Binder.clearCallingIdentity();
- try {
- Preconditions.checkState(mUserManager != null);
-
- // more expensive check - check that argument really is a parent user id
- if (Build.IS_DEBUGGABLE) {
- Preconditions.checkArgument(
- mUserManager.getProfileParent(parentUserId) == null);
- }
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mActivityManagerInternal.isCurrentProfile(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
- mCachedParentUserId = parentUserId;
- mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ private int[] getProfileIds(@UserIdInt int userId) {
+ synchronized (this) {
+ Preconditions.checkState(mUserManager != null);
}
- return mCachedProfileUserIds;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mUserManager.getEnabledProfileIds(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Dump info for debugging.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- boolean systemRunning;
- synchronized (this) {
- systemRunning = mUserManager != null;
- }
-
- if (systemRunning) {
- int[] currentUserIds = getProfileUserIdsForParentUser(mCurrentUserId);
- pw.println("current users: " + Arrays.toString(currentUserIds));
- for (int userId : currentUserIds) {
- if (mUserManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION,
+ int[] currentUserProfiles = getCurrentUserIds();
+ pw.println("current users: " + Arrays.toString(currentUserProfiles));
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager != null) {
+ for (int userId : currentUserProfiles) {
+ if (userManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION,
UserHandle.of(userId))) {
pw.println(" u" + userId + " restricted");
}
}
- } else {
- pw.println("current user: " + mCurrentUserId);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9d3385f20695..a95dc3035200 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -246,6 +246,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -2173,19 +2174,19 @@ public class NotificationManagerService extends SystemService {
mStatsManager.setPullAtomCallback(
PACKAGE_NOTIFICATION_PREFERENCES,
null, // use default PullAtomMetadata values
- BackgroundThread.getExecutor(),
+ ConcurrentUtils.DIRECT_EXECUTOR,
mPullAtomCallback
);
mStatsManager.setPullAtomCallback(
PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES,
null, // use default PullAtomMetadata values
- BackgroundThread.getExecutor(),
+ ConcurrentUtils.DIRECT_EXECUTOR,
mPullAtomCallback
);
mStatsManager.setPullAtomCallback(
PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES,
null, // use default PullAtomMetadata values
- BackgroundThread.getExecutor(),
+ ConcurrentUtils.DIRECT_EXECUTOR,
mPullAtomCallback
);
}
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 81ee7d9eeef7..52fdc7983636 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -21,7 +21,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderManager;
@@ -122,19 +121,7 @@ public class DataLoaderManagerService extends SystemService {
ri.serviceInfo.packageName, ri.serviceInfo.name);
// There should only be one matching provider inside the given package.
// If there's more than one, return the first one found.
- try {
- ApplicationInfo ai = pm.getApplicationInfo(resolved.getPackageName(), 0);
- if (!ai.isPrivilegedApp()) {
- Slog.w(TAG,
- "Data loader: " + resolved + " is not a privileged app, skipping.");
- continue;
- }
- return resolved;
- } catch (PackageManager.NameNotFoundException ex) {
- Slog.w(TAG,
- "Privileged data loader: " + resolved + " not found, skipping.");
- }
-
+ return resolved;
}
Slog.e(TAG, "Didn't find any matching data loader service provider.");
return null;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 236a6816b3e3..f827721be3b7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -40,6 +40,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.VersionedPackage;
@@ -126,8 +127,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
/** Automatically destroy staged sessions that have not changed state in this time */
private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 7 * DateUtils.DAY_IN_MILLIS;
- /** Upper bound on number of active sessions for a UID */
- private static final long MAX_ACTIVE_SESSIONS = 1024;
+ /** Upper bound on number of active sessions for a UID that has INSTALL_PACKAGES */
+ private static final long MAX_ACTIVE_SESSIONS_WITH_PERMISSION = 1024;
+ /** Upper bound on number of active sessions for a UID without INSTALL_PACKAGES */
+ private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50;
/** Upper bound on number of historical sessions for a UID */
private static final long MAX_HISTORICAL_SESSIONS = 1048576;
@@ -503,7 +506,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
+ "to use a data loader");
}
- String requestedInstallerPackageName = params.installerPackageName != null
+ // App package name and label length is restricted so that really long strings aren't
+ // written to disk.
+ if (params.appPackageName != null
+ && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+ params.appPackageName = null;
+ }
+
+ params.appLabel = TextUtils.trimToSize(params.appLabel,
+ PackageItemInfo.MAX_SAFE_LABEL_LENGTH);
+
+ String requestedInstallerPackageName = (params.installerPackageName != null
+ && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH)
? params.installerPackageName : installerPackageName;
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
@@ -635,12 +649,23 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ if (params.whitelistedRestrictedPermissions != null) {
+ mPermissionManager.retainHardAndSoftRestrictedPermissions(
+ params.whitelistedRestrictedPermissions);
+ }
+
final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
// Sanity check that installer isn't going crazy
final int activeCount = getSessionCount(mSessions, callingUid);
- if (activeCount >= MAX_ACTIVE_SESSIONS) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
+ == PackageManager.PERMISSION_GRANTED) {
+ if (activeCount >= MAX_ACTIVE_SESSIONS_WITH_PERMISSION) {
+ throw new IllegalStateException(
+ "Too many active sessions for UID " + callingUid);
+ }
+ } else if (activeCount >= MAX_ACTIVE_SESSIONS_NO_PERMISSION) {
throw new IllegalStateException(
"Too many active sessions for UID " + callingUid);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6bb10c79d382..766fae64f647 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -148,6 +148,8 @@ import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -639,6 +641,19 @@ public class PackageManagerService extends IPackageManager.Stub
*/
private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
+ /**
+ * Adding an installer package name to a package that does not have one set requires the
+ * INSTALL_PACKAGES permission.
+ *
+ * If the caller targets R, this will throw a SecurityException. Otherwise the request will
+ * fail silently. In both cases, and regardless of whether this change is enabled, the
+ * installer package will remain unchanged.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
+ 150857253;
+
public static final String PLATFORM_PACKAGE_NAME = "android";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
@@ -5264,15 +5279,17 @@ public class PackageManagerService extends IPackageManager.Stub
* </ul>
*/
int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
- boolean matchSystemOnly) {
+ boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
return updateFlagsForResolve(flags, userId, callingUid,
- wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/);
+ wantInstantApps, false /*onlyExposedExplicitly*/,
+ isImplicitImageCaptureIntentAndNotSetByDpc);
}
int updateFlagsForResolve(int flags, int userId, int callingUid,
- boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) {
+ boolean wantInstantApps, boolean onlyExposedExplicitly,
+ boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
// Safe mode means we shouldn't match any third-party components
- if (mSafeMode || matchSystemOnly) {
+ if (mSafeMode || isImplicitImageCaptureIntentAndNotSetByDpc) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
if (getInstantAppPackageName(callingUid) != null) {
@@ -6400,7 +6417,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
- intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+ flags));
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
@@ -6438,7 +6456,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
0, userId, callingUid, false /*includeInstantApps*/,
- intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, 0));
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mLock) {
@@ -6684,6 +6702,40 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
+ /**
+ * From Android R, camera intents have to match system apps. The only exception to this is if
+ * the DPC has set the camera persistent preferred activity. This case was introduced
+ * because it is important that the DPC has the ability to set both system and non-system
+ * camera persistent preferred activities.
+ *
+ * @return {@code true} if the intent is a camera intent and the persistent preferred
+ * activity was not set by the DPC.
+ */
+ @GuardedBy("mLock")
+ private boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
+ String resolvedType, int flags) {
+ return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
+ intent, userId, resolvedType, flags);
+ }
+
+ private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
+ String resolvedType, int flags) {
+ PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
+ .get(userId);
+ //TODO(b/158003772): Remove double query
+ List<PersistentPreferredActivity> pprefs = ppir != null
+ ? ppir.queryIntent(intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId)
+ : new ArrayList<>();
+ for (PersistentPreferredActivity ppa : pprefs) {
+ if (ppa.mIsSetByDpm) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@GuardedBy("mLock")
private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, boolean debug, int userId) {
@@ -6767,7 +6819,8 @@ public class PackageManagerService extends IPackageManager.Stub
android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
flags = updateFlagsForResolve(
flags, userId, callingUid, false /*includeInstantApps*/,
- intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+ flags));
intent = updateIntentForResolve(intent);
// writer
synchronized (mLock) {
@@ -6980,7 +7033,8 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
int flags = updateFlagsForResolve(0, parent.id, callingUid,
false /*includeInstantApps*/,
- intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, parent.id,
+ resolvedType, 0));
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
@@ -7067,7 +7121,8 @@ public class PackageManagerService extends IPackageManager.Stub
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/,
- intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+ flags));
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
@@ -7856,7 +7911,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
- intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+ flags));
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
@@ -8043,7 +8099,8 @@ public class PackageManagerService extends IPackageManager.Stub
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
- intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+ flags));
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -8134,7 +8191,7 @@ public class PackageManagerService extends IPackageManager.Stub
int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
- false /* matchSystemOnly */);
+ false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
@@ -8166,7 +8223,7 @@ public class PackageManagerService extends IPackageManager.Stub
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
- false /* matchSystemOnly */);
+ false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -8304,7 +8361,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
- false /* matchSystemOnly */);
+ false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -14130,19 +14187,38 @@ public class PackageManagerService extends IPackageManager.Stub
// be signed with the same cert as the caller.
String targetInstallerPackageName =
targetPackageSetting.installSource.installerPackageName;
- if (targetInstallerPackageName != null) {
- PackageSetting setting = mSettings.mPackages.get(
- targetInstallerPackageName);
- // If the currently set package isn't valid, then it's always
- // okay to change it.
- if (setting != null) {
- if (compareSignatures(callerSignature,
- setting.signatures.mSigningDetails.signatures)
- != PackageManager.SIGNATURE_MATCH) {
- throw new SecurityException(
- "Caller does not have same cert as old installer package "
- + targetInstallerPackageName);
+ PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null :
+ mSettings.mPackages.get(targetInstallerPackageName);
+
+ if (targetInstallerPkgSetting != null) {
+ if (compareSignatures(callerSignature,
+ targetInstallerPkgSetting.signatures.mSigningDetails.signatures)
+ != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException(
+ "Caller does not have same cert as old installer package "
+ + targetInstallerPackageName);
+ }
+ } else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ // This is probably an attempt to exploit vulnerability b/150857253 of taking
+ // privileged installer permissions when the installer has been uninstalled or
+ // was never set.
+ EventLog.writeEvent(0x534e4554, "150857253", callingUid, "");
+
+ long binderToken = Binder.clearCallingIdentity();
+ try {
+ if (mInjector.getCompatibility().isChangeEnabledByUid(
+ THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE,
+ callingUid)) {
+ throw new SecurityException("Neither user " + callingUid
+ + " nor current process has "
+ + Manifest.permission.INSTALL_PACKAGES);
+ } else {
+ // If change disabled, fail silently for backwards compatibility
+ return;
}
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
}
}
@@ -19840,7 +19916,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
synchronized (mLock) {
mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
- new PersistentPreferredActivity(filter, activity));
+ new PersistentPreferredActivity(filter, activity, true));
scheduleWritePackageRestrictionsLocked(userId);
}
updateDefaultHomeNotLocked(userId);
diff --git a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
index 0d4cdf9dee53..5a6fd0923f53 100644
--- a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
+++ b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
@@ -16,31 +16,34 @@
package com.android.server.pm;
+import android.content.ComponentName;
+import android.content.IntentFilter;
+import android.util.Log;
+
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import android.content.ComponentName;
-import android.content.IntentFilter;
-import android.util.Log;
-
import java.io.IOException;
class PersistentPreferredActivity extends IntentFilter {
private static final String ATTR_NAME = "name"; // component name
private static final String ATTR_FILTER = "filter"; // filter
+ private static final String ATTR_SET_BY_DPM = "set-by-dpm"; // set by DPM
private static final String TAG = "PersistentPreferredActivity";
private static final boolean DEBUG_FILTERS = false;
final ComponentName mComponent;
+ final boolean mIsSetByDpm;
- PersistentPreferredActivity(IntentFilter filter, ComponentName activity) {
+ PersistentPreferredActivity(IntentFilter filter, ComponentName activity, boolean isSetByDpm) {
super(filter);
mComponent = activity;
+ mIsSetByDpm = isSetByDpm;
}
PersistentPreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -52,6 +55,8 @@ class PersistentPreferredActivity extends IntentFilter {
"Bad activity name " + shortComponent +
" at " + parser.getPositionDescription());
}
+ mIsSetByDpm = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_SET_BY_DPM));
+
int outerDepth = parser.getDepth();
String tagName = parser.getName();
int type;
@@ -83,6 +88,7 @@ class PersistentPreferredActivity extends IntentFilter {
public void writeToXml(XmlSerializer serializer) throws IOException {
serializer.attribute(null, ATTR_NAME, mComponent.flattenToShortString());
+ serializer.attribute(null, ATTR_SET_BY_DPM, Boolean.toString(mIsSetByDpm));
serializer.startTag(null, ATTR_FILTER);
super.writeToXml(serializer);
serializer.endTag(null, ATTR_FILTER);
@@ -91,6 +97,7 @@ class PersistentPreferredActivity extends IntentFilter {
@Override
public String toString() {
return "PersistentPreferredActivity{0x" + Integer.toHexString(System.identityHashCode(this))
- + " " + mComponent.flattenToShortString() + "}";
+ + " " + mComponent.flattenToShortString()
+ + ", mIsSetByDpm=" + mIsSetByDpm + "}";
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 3ec139763e80..0c42ff6be520 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2419,6 +2419,9 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName,
IntentFilter filter, @UserIdInt int userId) {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Objects.requireNonNull(filter, "intentFilter");
+
verifyCaller(packageName, userId);
enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
"getShareTargets");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 79805e3b42ae..8ccf837f64dc 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -77,7 +77,11 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.rollback.WatchdogRollbackLogger;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -102,6 +106,9 @@ public class StagingManager {
private final PreRebootVerificationHandler mPreRebootVerificationHandler;
private final Supplier<PackageParser2> mPackageParserSupplier;
+ private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt");
+ private String mFailureReason;
+
@GuardedBy("mStagedSessions")
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
@@ -125,6 +132,12 @@ public class StagingManager {
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mPreRebootVerificationHandler = new PreRebootVerificationHandler(
BackgroundThread.get().getLooper());
+
+ if (mFailureReasonFile.exists()) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) {
+ mFailureReason = reader.readLine();
+ } catch (Exception ignore) { }
+ }
}
/**
@@ -383,10 +396,19 @@ public class StagingManager {
}
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
- private void abortCheckpoint(String errorMsg) {
- Slog.e(TAG, "Aborting checkpoint: " + errorMsg);
+ private void abortCheckpoint(int sessionId, String errorMsg) {
+ String failureReason = "Failed to install sessionId: " + sessionId + " Error: " + errorMsg;
+ Slog.e(TAG, failureReason);
try {
if (supportsCheckpoint() && needsCheckpoint()) {
+ // Store failure reason for next reboot
+ try (BufferedWriter writer =
+ new BufferedWriter(new FileWriter(mFailureReasonFile))) {
+ writer.write(failureReason);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to save failure reason: ", e);
+ }
+
// Only revert apex sessions if device supports updating apex
if (mApexManager.isApexSupported()) {
mApexManager.revertActiveSessions();
@@ -592,14 +614,12 @@ public class StagingManager {
// If checkpoint is supported, then we only resume sessions if we are in checkpointing
// mode. If not, we fail all sessions.
if (supportsCheckpoint() && !needsCheckpoint()) {
- // TODO(b/146343545): Persist failure reason across checkpoint reboot
- Slog.d(TAG, "Reverting back to safe state. Marking " + session.sessionId
- + " as failed.");
- String errorMsg = "Reverting back to safe state";
- if (!TextUtils.isEmpty(mNativeFailureReason)) {
- errorMsg = "Entered fs-rollback mode and reverted session due to crashing "
- + "native process: " + mNativeFailureReason;
+ String errorMsg = "Reverting back to safe state. Marking " + session.sessionId
+ + " as failed";
+ if (!TextUtils.isEmpty(mFailureReason)) {
+ errorMsg = errorMsg + ": " + mFailureReason;
}
+ Slog.d(TAG, errorMsg);
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, errorMsg);
return;
}
@@ -624,7 +644,7 @@ public class StagingManager {
+ "supposed to be activated";
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
errorMsg);
- abortCheckpoint(errorMsg);
+ abortCheckpoint(session.sessionId, errorMsg);
return;
}
if (isApexSessionFailed(apexSessionInfo)) {
@@ -636,7 +656,7 @@ public class StagingManager {
}
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
errorMsg);
- abortCheckpoint(errorMsg);
+ abortCheckpoint(session.sessionId, errorMsg);
return;
}
if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
@@ -647,7 +667,7 @@ public class StagingManager {
+ "didn't activate nor fail. Marking it as failed anyway.";
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
errorMsg);
- abortCheckpoint(errorMsg);
+ abortCheckpoint(session.sessionId, errorMsg);
return;
}
}
@@ -664,7 +684,7 @@ public class StagingManager {
installApksInSession(session);
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
- abortCheckpoint(e.getMessage());
+ abortCheckpoint(session.sessionId, e.getMessage());
// If checkpoint is not supported, we have to handle failure for one staged session.
if (!hasApex) {
@@ -1189,6 +1209,8 @@ public class StagingManager {
ctx.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+
+ mFailureReasonFile.delete();
}
private static class LocalIntentReceiverAsync {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d5c9424528bd..40fa798309c1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2250,9 +2250,6 @@ public class UserManagerService extends IUserManager.Stub {
// Managed profiles have their own specific rules.
final boolean isManagedProfile = type.isManagedProfile();
if (isManagedProfile) {
- if (ActivityManager.isLowRamDeviceStatic()) {
- return false;
- }
if (!mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_MANAGED_USERS)) {
return false;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 8f3bf39d4fc5..cd53fb9ba52f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -816,7 +816,7 @@ public final class DefaultPermissionGrantPolicy {
if (!TextUtils.isEmpty(contentCapturePackageName)) {
grantPermissionsToSystemPackage(pm, contentCapturePackageName, userId,
PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
+ CONTACTS_PERMISSIONS);
}
// Atthention Service
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 b0d4d957fc21..d3f3ba1dc6bb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,6 +26,7 @@ import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -1804,8 +1805,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
continue;
}
- // If this permission was granted by default, make sure it is.
- if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) {
+ // If this permission was granted by default or role, make sure it is.
+ if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
+ || (oldFlags & FLAG_PERMISSION_GRANTED_BY_ROLE) != 0) {
// PermissionPolicyService will handle the app op for runtime permissions later.
grantRuntimePermissionInternal(permName, packageName, false,
Process.SYSTEM_UID, userId, delayingPermCallback);
@@ -4948,6 +4950,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback);
}
}
+
+ @Override
+ public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions) {
+ synchronized (mLock) {
+ Iterator<String> iterator = permissions.iterator();
+ while (iterator.hasNext()) {
+ String permission = iterator.next();
+ BasePermission basePermission = mSettings.mPermissions.get(permission);
+ if (basePermission == null || !basePermission.isHardOrSoftRestricted()) {
+ iterator.remove();
+ }
+ }
+ }
+ }
}
private static final class OnPermissionChangeListeners extends Handler {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 57a25eddf7ce..4412162a5cc8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -36,6 +36,7 @@ import java.util.function.Consumer;
* TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes.
*/
public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal {
+
/**
* Provider for package names.
*/
@@ -455,4 +456,10 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
/** Called when a new user has been created. */
public abstract void onNewUserCreated(@UserIdInt int userId);
+
+ /**
+ * Removes invalid permissions which are not {@link PermissionInfo#FLAG_HARD_RESTRICTED} or
+ * {@link PermissionInfo#FLAG_SOFT_RESTRICTED} from the input.
+ */
+ public abstract void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 49c781905898..2f963b7e6b35 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -20,11 +20,16 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_2.ISoundTriggerHw;
+import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioOffloadInfo;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
import android.media.soundtrigger_middleware.PhraseSoundModel;
import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
@@ -540,20 +545,20 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
switch (mModelType) {
case SoundModelType.GENERIC: {
android.media.soundtrigger_middleware.RecognitionEvent event =
- new android.media.soundtrigger_middleware.RecognitionEvent();
+ newEmptyRecognitionEvent();
event.status =
android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
+ event.type = SoundModelType.GENERIC;
mCallback.onRecognition(mHandle, event);
}
break;
case SoundModelType.KEYPHRASE: {
android.media.soundtrigger_middleware.PhraseRecognitionEvent event =
- new android.media.soundtrigger_middleware.PhraseRecognitionEvent();
- event.common =
- new android.media.soundtrigger_middleware.RecognitionEvent();
+ newEmptyPhraseRecognitionEvent();
event.common.status =
android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
+ event.common.type = SoundModelType.KEYPHRASE;
mCallback.onPhraseRecognition(mHandle, event);
}
break;
@@ -614,4 +619,35 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
}
}
}
+
+ /**
+ * Creates a default-initialized recognition event.
+ *
+ * Object fields are default constructed.
+ * Array fields are initialized to 0 length.
+ *
+ * @return The event.
+ */
+ private static RecognitionEvent newEmptyRecognitionEvent() {
+ RecognitionEvent result = new RecognitionEvent();
+ result.audioConfig = new AudioConfig();
+ result.audioConfig.offloadInfo = new AudioOffloadInfo();
+ result.data = new byte[0];
+ return result;
+ }
+
+ /**
+ * Creates a default-initialized phrase recognition event.
+ *
+ * Object fields are default constructed.
+ * Array fields are initialized to 0 length.
+ *
+ * @return The event.
+ */
+ private static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() {
+ PhraseRecognitionEvent result = new PhraseRecognitionEvent();
+ result.common = newEmptyRecognitionEvent();
+ result.phraseExtras = new PhraseRecognitionExtra[0];
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 6c1ff728e6b9..ab459fdadd21 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3319,8 +3319,8 @@ public class StatsPullAtomService extends SystemService {
public void run() {
try {
estimateAppOpsSamplingRate();
- } catch (Exception e) {
- Slog.e(TAG, "AppOps sampling ratio estimation failed");
+ } catch (Throwable e) {
+ Slog.e(TAG, "AppOps sampling ratio estimation failed: ", e);
synchronized (mAppOpsSamplingRateLock) {
mAppOpsSamplingRate = min(mAppOpsSamplingRate, 10);
}
@@ -3361,7 +3361,7 @@ public class StatsPullAtomService extends SystemService {
Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli(),
Long.MAX_VALUE).setFlags(
OP_FLAGS_PULLED).build();
- appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
+ appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR, ops::complete);
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
List<AppOpEntry> opsList =
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 9476e9260c73..c38d649ada9b 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -77,6 +77,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
@@ -122,6 +123,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
PackageManagerInternal mPmInternal;
/** File storing persisted {@link #mGrantedUriPermissions}. */
+ @GuardedBy("mLock")
private final AtomicFile mGrantFile;
/** XML constants used in {@link #mGrantFile} */
@@ -142,6 +144,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
* This optimized lookup structure maps from {@link UriPermission#targetUid}
* to {@link UriPermission#uri} to {@link UriPermission}.
*/
+ @GuardedBy("mLock")
private final SparseArray<ArrayMap<GrantUri, UriPermission>>
mGrantedUriPermissions = new SparseArray<>();
@@ -206,39 +209,44 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
}
+ @Override
+ public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
+ Uri uri, final int modeFlags, int sourceUserId, int targetUserId) {
+ grantUriPermissionFromOwnerUnlocked(token, fromUid, targetPkg, uri, modeFlags, sourceUserId,
+ targetUserId);
+ }
+
/**
* @param uri This uri must NOT contain an embedded userId.
* @param sourceUserId The userId in which the uri is to be resolved.
* @param targetUserId The userId of the app that receives the grant.
*/
- @Override
- public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
- final int modeFlags, int sourceUserId, int targetUserId) {
+ private void grantUriPermissionFromOwnerUnlocked(IBinder token, int fromUid, String targetPkg,
+ Uri uri, final int modeFlags, int sourceUserId, int targetUserId) {
targetUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
"grantUriPermissionFromOwner", null);
- synchronized(mLock) {
- UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
- if (owner == null) {
- throw new IllegalArgumentException("Unknown owner: " + token);
- }
- if (fromUid != Binder.getCallingUid()) {
- if (Binder.getCallingUid() != myUid()) {
- // Only system code can grant URI permissions on behalf
- // of other users.
- throw new SecurityException("nice try");
- }
- }
- if (targetPkg == null) {
- throw new IllegalArgumentException("null target");
- }
- if (uri == null) {
- throw new IllegalArgumentException("null uri");
- }
- grantUriPermission(fromUid, targetPkg, new GrantUri(sourceUserId, uri, modeFlags),
- modeFlags, owner, targetUserId);
+ UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+ if (owner == null) {
+ throw new IllegalArgumentException("Unknown owner: " + token);
}
+ if (fromUid != Binder.getCallingUid()) {
+ if (Binder.getCallingUid() != myUid()) {
+ // Only system code can grant URI permissions on behalf
+ // of other users.
+ throw new SecurityException("nice try");
+ }
+ }
+ if (targetPkg == null) {
+ throw new IllegalArgumentException("null target");
+ }
+ if (uri == null) {
+ throw new IllegalArgumentException("null uri");
+ }
+
+ grantUriPermissionUnlocked(fromUid, targetPkg, new GrantUri(sourceUserId, uri, modeFlags),
+ modeFlags, owner, targetUserId);
}
@Override
@@ -362,7 +370,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
persistChanged |= prefixPerm.takePersistableModes(modeFlags);
}
- persistChanged |= maybePrunePersistedUriGrants(uid);
+ persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
if (persistChanged) {
schedulePersistUriGrants();
@@ -374,8 +382,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
public void clearGrantedUriPermissions(String packageName, int userId) {
mAmInternal.enforceCallingPermission(
CLEAR_APP_GRANTED_URI_PERMISSIONS, "clearGrantedUriPermissions");
- synchronized(mLock) {
- removeUriPermissionsForPackage(packageName, userId, true, true);
+ synchronized (mLock) {
+ removeUriPermissionsForPackageLocked(packageName, userId, true, true);
}
}
@@ -416,11 +424,11 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
if (exactPerm != null) {
persistChanged |= exactPerm.releasePersistableModes(modeFlags);
- removeUriPermissionIfNeeded(exactPerm);
+ removeUriPermissionIfNeededLocked(exactPerm);
}
if (prefixPerm != null) {
persistChanged |= prefixPerm.releasePersistableModes(modeFlags);
- removeUriPermissionIfNeeded(prefixPerm);
+ removeUriPermissionIfNeededLocked(prefixPerm);
}
if (persistChanged) {
@@ -441,8 +449,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
* @param targetOnly When {@code true}, only remove permissions where the app is the target,
* not source.
*/
- void removeUriPermissionsForPackage(
- String packageName, int userHandle, boolean persistable, boolean targetOnly) {
+ @GuardedBy("mLock")
+ private void removeUriPermissionsForPackageLocked(String packageName, int userHandle,
+ boolean persistable, boolean targetOnly) {
if (userHandle == UserHandle.USER_ALL && packageName == null) {
throw new IllegalArgumentException("Must narrow by either package or user");
}
@@ -494,7 +503,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
/** Returns if the ContentProvider has granted a uri to callingUid */
- boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) {
+ @GuardedBy("mLock")
+ private boolean checkAuthorityGrantsLocked(int callingUid, ProviderInfo cpi, int userId,
+ boolean checkUser) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
if (perms != null) {
for (int i = perms.size() - 1; i >= 0; i--) {
@@ -530,7 +541,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
*
* @return if any mutations occured that require persisting.
*/
- private boolean maybePrunePersistedUriGrants(int uid) {
+ @GuardedBy("mLock")
+ private boolean maybePrunePersistedUriGrantsLocked(int uid) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
if (perms == null) return false;
if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
@@ -552,14 +564,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
if (DEBUG) Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime);
perm.releasePersistableModes(~0);
- removeUriPermissionIfNeeded(perm);
+ removeUriPermissionIfNeededLocked(perm);
}
return true;
}
/** Like checkGrantUriPermission, but takes an Intent. */
- NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
+ private NeededUriGrants checkGrantUriPermissionFromIntentUnlocked(int callingUid,
String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
if (DEBUG) Slog.v(TAG,
"Checking URI perm to data=" + (intent != null ? intent.getData() : null)
@@ -598,7 +610,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
if (data != null) {
GrantUri grantUri = GrantUri.resolve(contentUserHint, data, mode);
- targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, mode, targetUid);
+ targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, mode,
+ targetUid);
if (targetUid > 0) {
if (needed == null) {
needed = new NeededUriGrants(targetPkg, targetUid, mode);
@@ -611,7 +624,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
Uri uri = clip.getItemAt(i).getUri();
if (uri != null) {
GrantUri grantUri = GrantUri.resolve(contentUserHint, uri, mode);
- targetUid = checkGrantUriPermission(callingUid, targetPkg,
+ targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg,
grantUri, mode, targetUid);
if (targetUid > 0) {
if (needed == null) {
@@ -622,7 +635,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
} else {
Intent clipIntent = clip.getItemAt(i).getIntent();
if (clipIntent != null) {
- NeededUriGrants newNeeded = checkGrantUriPermissionFromIntent(
+ NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentUnlocked(
callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
if (newNeeded != null) {
needed = newNeeded;
@@ -635,7 +648,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return needed;
}
- void readGrantedUriPermissions() {
+ @GuardedBy("mLock")
+ private void readGrantedUriPermissionsLocked() {
if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()");
final long now = System.currentTimeMillis();
@@ -681,7 +695,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
if (targetUid != -1) {
final GrantUri grantUri = new GrantUri(sourceUserId, uri,
prefix ? Intent.FLAG_GRANT_PREFIX_URI_PERMISSION : 0);
- final UriPermission perm = findOrCreateUriPermission(
+ final UriPermission perm = findOrCreateUriPermissionLocked(
sourcePkg, targetPkg, targetUid, grantUri);
perm.initPersistedModes(modeFlags, createdTime);
}
@@ -703,7 +717,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
}
- private UriPermission findOrCreateUriPermission(String sourcePkg,
+ @GuardedBy("mLock")
+ private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
String targetPkg, int targetUid, GrantUri grantUri) {
ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
if (targetUris == null) {
@@ -740,15 +755,18 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return;
}
- final UriPermission perm = findOrCreateUriPermission(
- pi.packageName, targetPkg, targetUid, grantUri);
+ final UriPermission perm;
+ synchronized (mLock) {
+ perm = findOrCreateUriPermissionLocked(pi.packageName, targetPkg, targetUid, grantUri);
+ }
perm.grantModes(modeFlags, owner);
mPmInternal.grantImplicitAccess(UserHandle.getUserId(targetUid), null,
UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/);
}
/** Like grantUriPermissionUnchecked, but takes an Intent. */
- void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed, UriPermissionOwner owner) {
+ private void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed,
+ UriPermissionOwner owner) {
if (needed == null) {
return;
}
@@ -759,7 +777,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
}
- void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+ private void grantUriPermissionUnlocked(int callingUid, String targetPkg, GrantUri grantUri,
final int modeFlags, UriPermissionOwner owner, int targetUserId) {
if (targetPkg == null) {
throw new NullPointerException("targetPkg");
@@ -767,7 +785,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
int targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
targetUserId);
- targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, modeFlags, targetUid);
+ targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, modeFlags,
+ targetUid);
if (targetUid < 0) {
return;
}
@@ -775,7 +794,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
grantUriPermissionUnchecked(targetUid, targetPkg, grantUri, modeFlags, owner);
}
- void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri,
+ private void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri,
final int modeFlags) {
if (DEBUG) Slog.v(TAG, "Revoking all granted permissions to " + grantUri);
@@ -788,8 +807,19 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return;
}
+ final boolean callerHoldsPermissions = checkHoldingPermissionsUnlocked(pi, grantUri,
+ callingUid, modeFlags);
+ synchronized (mLock) {
+ revokeUriPermissionLocked(targetPackage, callingUid, grantUri, modeFlags,
+ callerHoldsPermissions);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri,
+ final int modeFlags, final boolean callerHoldsPermissions) {
// Does the caller have this permission on the URI?
- if (!checkHoldingPermissions(pi, grantUri, callingUid, modeFlags)) {
+ if (!callerHoldsPermissions) {
// If they don't have direct access to the URI, then revoke any
// ownerless URI permissions that have been granted to them.
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
@@ -861,7 +891,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
* the given {@link ProviderInfo}. Final permission checking is always done
* in {@link ContentProvider}.
*/
- private boolean checkHoldingPermissions(
+ private boolean checkHoldingPermissionsUnlocked(
ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
if (DEBUG) Slog.v(TAG, "checkHoldingPermissions: uri=" + grantUri + " uid=" + uid);
if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
@@ -870,11 +900,17 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return false;
}
}
- return checkHoldingPermissionsInternal(pi, grantUri, uid, modeFlags, true);
+ return checkHoldingPermissionsInternalUnlocked(pi, grantUri, uid, modeFlags, true);
}
- private boolean checkHoldingPermissionsInternal(ProviderInfo pi,
+ private boolean checkHoldingPermissionsInternalUnlocked(ProviderInfo pi,
GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
+ // We must never hold our local mLock in this method, since we may need
+ // to call into ActivityManager for dynamic permission checks
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException("Must never hold local mLock");
+ }
+
if (pi.applicationInfo.uid == uid) {
return true;
} else if (!pi.exported) {
@@ -968,7 +1004,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return readMet && writeMet && forceMet;
}
- private void removeUriPermissionIfNeeded(UriPermission perm) {
+ @GuardedBy("mLock")
+ private void removeUriPermissionIfNeededLocked(UriPermission perm) {
if (perm.modeFlags != 0) {
return;
}
@@ -985,6 +1022,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
}
+ @GuardedBy("mLock")
private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
if (targetUris != null) {
@@ -1020,7 +1058,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
* If you already know the uid of the target, you can supply it in
* lastTargetUid else set that to -1.
*/
- int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+ private int checkGrantUriPermissionUnlocked(int callingUid, String targetPkg, GrantUri grantUri,
int modeFlags, int lastTargetUid) {
if (!Intent.isAccessUriMode(modeFlags)) {
return -1;
@@ -1076,7 +1114,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
boolean targetHoldsPermission = false;
if (targetUid >= 0) {
// First... does the target actually need this permission?
- if (checkHoldingPermissions(pi, grantUri, targetUid, modeFlags)) {
+ if (checkHoldingPermissionsUnlocked(pi, grantUri, targetUid, modeFlags)) {
// No need to grant the target this permission.
if (DEBUG) Slog.v(TAG,
"Target " + targetPkg + " already has full permission to " + grantUri);
@@ -1144,7 +1182,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
*/
boolean specialCrossUserGrant = targetUid >= 0
&& UserHandle.getUserId(targetUid) != grantUri.sourceUserId
- && checkHoldingPermissionsInternal(pi, grantUri, callingUid,
+ && checkHoldingPermissionsInternalUnlocked(pi, grantUri, callingUid,
modeFlags, false /*without considering the uid permissions*/);
// Second... is the provider allowing granting of URI permissions?
@@ -1179,9 +1217,13 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
// Third... does the caller itself have permission to access this uri?
- if (!checkHoldingPermissions(pi, grantUri, callingUid, modeFlags)) {
+ if (!checkHoldingPermissionsUnlocked(pi, grantUri, callingUid, modeFlags)) {
// Require they hold a strong enough Uri permission
- if (!checkUriPermission(grantUri, callingUid, modeFlags)) {
+ final boolean res;
+ synchronized (mLock) {
+ res = checkUriPermissionLocked(grantUri, callingUid, modeFlags);
+ }
+ if (!res) {
if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) {
throw new SecurityException(
"UID " + callingUid + " does not have permission to " + grantUri
@@ -1200,13 +1242,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
/**
* @param userId The userId in which the uri is to be resolved.
*/
- int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags,
- int userId) {
- return checkGrantUriPermission(callingUid, targetPkg,
+ private int checkGrantUriPermissionUnlocked(int callingUid, String targetPkg, Uri uri,
+ int modeFlags, int userId) {
+ return checkGrantUriPermissionUnlocked(callingUid, targetPkg,
new GrantUri(userId, uri, modeFlags), modeFlags, -1);
}
- boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags) {
+ @GuardedBy("mLock")
+ private boolean checkUriPermissionLocked(GrantUri grantUri, int uid, final int modeFlags) {
final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
: UriPermission.STRENGTH_OWNED;
@@ -1238,7 +1281,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return false;
}
- private void writeGrantedUriPermissions() {
+ @GuardedBy("mLock")
+ private void writeGrantedUriPermissionsLocked() {
if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
final long startTime = SystemClock.uptimeMillis();
@@ -1299,34 +1343,35 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
public void handleMessage(Message msg) {
switch (msg.what) {
case PERSIST_URI_GRANTS_MSG: {
- writeGrantedUriPermissions();
+ synchronized (mLock) {
+ writeGrantedUriPermissionsLocked();
+ }
break;
}
}
}
}
- final class LocalService implements UriGrantsManagerInternal {
+ private final class LocalService implements UriGrantsManagerInternal {
@Override
public void removeUriPermissionIfNeeded(UriPermission perm) {
synchronized (mLock) {
- UriGrantsManagerService.this.removeUriPermissionIfNeeded(perm);
+ UriGrantsManagerService.this.removeUriPermissionIfNeededLocked(perm);
}
}
@Override
public void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri,
int modeFlags) {
- synchronized (mLock) {
- UriGrantsManagerService.this.revokeUriPermission(
- targetPackage, callingUid, grantUri, modeFlags);
- }
+ UriGrantsManagerService.this.revokeUriPermission(
+ targetPackage, callingUid, grantUri, modeFlags);
}
@Override
public boolean checkUriPermission(GrantUri grantUri, int uid, int modeFlags) {
synchronized (mLock) {
- return UriGrantsManagerService.this.checkUriPermission(grantUri, uid, modeFlags);
+ return UriGrantsManagerService.this.checkUriPermissionLocked(grantUri, uid,
+ modeFlags);
}
}
@@ -1334,83 +1379,73 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags,
int userId) {
enforceNotIsolatedCaller("checkGrantUriPermission");
- synchronized (mLock) {
- return UriGrantsManagerService.this.checkGrantUriPermission(
- callingUid, targetPkg, uri, modeFlags, userId);
- }
+ return UriGrantsManagerService.this.checkGrantUriPermissionUnlocked(
+ callingUid, targetPkg, uri, modeFlags, userId);
}
@Override
public NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid,
String targetPkg, int targetUserId) {
- synchronized (mLock) {
- final int mode = (intent != null) ? intent.getFlags() : 0;
- return UriGrantsManagerService.this.checkGrantUriPermissionFromIntent(
- callingUid, targetPkg, intent, mode, null, targetUserId);
- }
+ final int mode = (intent != null) ? intent.getFlags() : 0;
+ return UriGrantsManagerService.this.checkGrantUriPermissionFromIntentUnlocked(
+ callingUid, targetPkg, intent, mode, null, targetUserId);
}
@Override
public void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed,
UriPermissionOwner owner) {
- synchronized (mLock) {
- UriGrantsManagerService.this.grantUriPermissionUncheckedFromIntent(needed, owner);
- }
+ UriGrantsManagerService.this.grantUriPermissionUncheckedFromIntent(needed, owner);
}
@Override
public void onSystemReady() {
synchronized (mLock) {
- UriGrantsManagerService.this.readGrantedUriPermissions();
+ UriGrantsManagerService.this.readGrantedUriPermissionsLocked();
}
}
@Override
public IBinder newUriPermissionOwner(String name) {
enforceNotIsolatedCaller("newUriPermissionOwner");
- synchronized(mLock) {
- UriPermissionOwner owner = new UriPermissionOwner(this, name);
- return owner.getExternalToken();
- }
+ UriPermissionOwner owner = new UriPermissionOwner(this, name);
+ return owner.getExternalToken();
}
@Override
public void removeUriPermissionsForPackage(String packageName, int userHandle,
boolean persistable, boolean targetOnly) {
- synchronized(mLock) {
- UriGrantsManagerService.this.removeUriPermissionsForPackage(
+ synchronized (mLock) {
+ UriGrantsManagerService.this.removeUriPermissionsForPackageLocked(
packageName, userHandle, persistable, targetOnly);
}
}
@Override
public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
- synchronized(mLock) {
- final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
- if (owner == null) {
- throw new IllegalArgumentException("Unknown owner: " + token);
- }
+ final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+ if (owner == null) {
+ throw new IllegalArgumentException("Unknown owner: " + token);
+ }
- if (uri == null) {
- owner.removeUriPermissions(mode);
- } else {
- owner.removeUriPermission(new GrantUri(userId, uri, mode), mode);
- }
+ if (uri == null) {
+ owner.removeUriPermissions(mode);
+ } else {
+ owner.removeUriPermission(new GrantUri(userId, uri, mode), mode);
}
}
@Override
public boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId,
boolean checkUser) {
- synchronized(mLock) {
- return UriGrantsManagerService.this.checkAuthorityGrants(
+ synchronized (mLock) {
+ return UriGrantsManagerService.this.checkAuthorityGrantsLocked(
callingUid, cpi, userId, checkUser);
}
}
@Override
public void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
- synchronized(mLock) {
+ synchronized (mLock) {
boolean needSep = false;
boolean printedAnything = false;
if (mGrantedUriPermissions.size() > 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 7f03778ab1c7..0d5621dc0e4f 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
@@ -269,9 +268,6 @@ class ActivityStack extends Task {
private final AnimatingActivityRegistry mAnimatingActivityRegistry =
new AnimatingActivityRegistry();
- /** Stores the override windowing-mode from before a transient mode change (eg. split) */
- private int mRestoreOverrideWindowingMode = WINDOWING_MODE_UNDEFINED;
-
private boolean mTopActivityOccludesKeyguard;
private ActivityRecord mTopDismissingKeyguardActivity;
@@ -662,19 +658,6 @@ class ActivityStack extends Task {
}
/**
- * A transient windowing mode is one which activities enter into temporarily. Examples of this
- * are Split window modes and pip. Non-transient modes are modes that displays can adopt.
- *
- * @param windowingMode the windowingMode to test for transient-ness.
- * @return {@code true} if the windowing mode is transient, {@code false} otherwise.
- */
- private static boolean isTransientWindowingMode(int windowingMode) {
- return windowingMode == WINDOWING_MODE_PINNED
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
-
- /**
* Specialization of {@link #setWindowingMode(int)} for this subclass.
*
* @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
@@ -698,11 +681,6 @@ class ActivityStack extends Task {
final int currentOverrideMode = getRequestedOverrideWindowingMode();
final Task topTask = getTopMostTask();
int windowingMode = preferredWindowingMode;
- if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
- && isTransientWindowingMode(currentMode)) {
- // Leaving a transient mode. Interpret UNDEFINED as "restore"
- windowingMode = mRestoreOverrideWindowingMode;
- }
// Need to make sure windowing mode is supported. If we in the process of creating the stack
// no need to resolve the windowing mode again as it is already resolved to the right mode.
@@ -712,29 +690,16 @@ class ActivityStack extends Task {
windowingMode = WINDOWING_MODE_UNDEFINED;
}
}
- if (taskDisplayArea.getRootSplitScreenPrimaryTask() == this
- && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- // Resolution to split-screen secondary for the primary split-screen stack means
- // we want to leave split-screen mode.
- windowingMode = mRestoreOverrideWindowingMode;
- }
final boolean alreadyInSplitScreenMode = taskDisplayArea.isSplitScreenModeActivated();
- // Take any required action due to us not supporting the preferred windowing mode.
- if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ if (creating && alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
&& isActivityTypeStandardOrUndefined()) {
- final boolean preferredSplitScreen =
- preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- if (preferredSplitScreen || creating) {
- // Looks like we can't launch in split screen mode or the stack we are launching
- // doesn't support split-screen mode, go ahead an dismiss split-screen and display a
- // warning toast about it.
- mAtmService.getTaskChangeNotificationController()
- .notifyActivityDismissingDockedStack();
- taskDisplayArea.onSplitScreenModeDismissed(this);
- }
+ // If the stack is being created explicitly in fullscreen mode, dismiss split-screen
+ // and display a warning toast about it.
+ mAtmService.getTaskChangeNotificationController()
+ .notifyActivityDismissingDockedStack();
+ taskDisplayArea.onSplitScreenModeDismissed(this);
}
if (currentMode == windowingMode) {
@@ -797,9 +762,6 @@ class ActivityStack extends Task {
+ " while there is already one isn't currently supported");
//return;
}
- if (isTransientWindowingMode(windowingMode) && !isTransientWindowingMode(currentMode)) {
- mRestoreOverrideWindowingMode = currentOverrideMode;
- }
mTmpRect2.setEmpty();
if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 59181a64f423..a5b94b327699 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4776,6 +4776,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to.
*/
void reparentDisplayContent(WindowState win, SurfaceControl sc) {
+ if (mParentWindow != null) {
+ mParentWindow.removeEmbeddedDisplayContent(this);
+ }
mParentWindow = win;
mParentWindow.addEmbeddedDisplayContent(this);
mParentSurfaceControl = sc;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 31897057e076..48609e17ba40 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3164,6 +3164,11 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
+ public SurfaceControl.Builder makeAnimationLeash() {
+ return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId);
+ }
+
+ @Override
public SurfaceControl getAnimationLeashParent() {
if (WindowManagerService.sHierarchicalAnimations) {
return super.getAnimationLeashParent();
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index df0fa9cc3272..6e9428ee6976 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -61,6 +61,7 @@ class TaskChangeNotificationController {
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;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -183,6 +184,10 @@ class TaskChangeNotificationController {
l.onTaskRequestedOrientationChanged(m.arg1, m.arg2);
};
+ private final TaskStackConsumer mNotifyOnActivityRotation = (l, m) -> {
+ l.onActivityRotation();
+ };
+
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -277,6 +282,9 @@ class TaskChangeNotificationController {
case NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG:
forAllRemoteListeners(mNotifyTaskRequestedOrientationChanged, msg);
break;
+ case NOTIFY_ACTIVITY_ROTATED_MSG:
+ forAllRemoteListeners(mNotifyOnActivityRotation, msg);
+ break;
}
if (msg.obj instanceof SomeArgs) {
((SomeArgs) msg.obj).recycle();
@@ -574,4 +582,11 @@ class TaskChangeNotificationController {
forAllLocalListeners(mNotifyTaskRequestedOrientationChanged, msg);
msg.sendToTarget();
}
+
+ /** @see android.app.ITaskStackListener#onActivityRotation() */
+ void notifyOnActivityRotation() {
+ final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_ROTATED_MSG);
+ forAllLocalListeners(mNotifyOnActivityRotation, msg);
+ msg.sendToTarget();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index d1cb2105246a..aee5a1d7838b 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -106,6 +106,8 @@ class WallpaperController {
private static final int WALLPAPER_DRAW_TIMEOUT = 2;
private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+ private boolean mShouldUpdateZoom;
+
/**
* Temporary storage for taking a screenshot of the wallpaper.
* @see #screenshotWallpaperLocked()
@@ -400,6 +402,7 @@ class WallpaperController {
void setWallpaperZoomOut(WindowState window, float zoom) {
if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) {
window.mWallpaperZoomOut = zoom;
+ mShouldUpdateZoom = true;
updateWallpaperOffsetLocked(window, false);
}
}
@@ -623,9 +626,7 @@ class WallpaperController {
mLastWallpaperX = mWallpaperTarget.mWallpaperX;
mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
}
- if (mWallpaperTarget.mWallpaperZoomOut >= 0) {
- mLastWallpaperZoomOut = mWallpaperTarget.mWallpaperZoomOut;
- }
+ computeLastWallpaperZoomOut();
if (mWallpaperTarget.mWallpaperY >= 0) {
mLastWallpaperY = mWallpaperTarget.mWallpaperY;
mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
@@ -804,8 +805,11 @@ class WallpaperController {
* we'll have conflicts and break the "depth system" mental model.
*/
private void computeLastWallpaperZoomOut() {
- mLastWallpaperZoomOut = 0;
- mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true);
+ if (mShouldUpdateZoom) {
+ mLastWallpaperZoomOut = 0;
+ mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true);
+ mShouldUpdateZoom = false;
+ }
}
private float zoomOutToScale(float zoom) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f34510e2104a..8934e8f5c2e0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3848,6 +3848,11 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean rotationChanged = displayContent.updateRotationUnchecked();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ if (rotationChanged) {
+ mAtmService.getTaskChangeNotificationController()
+ .notifyOnActivityRotation();
+ }
+
if (!rotationChanged || forceRelayout) {
displayContent.setLayoutNeeded();
layoutNeeded = true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 045089082fd8..36232e13fcf1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5790,10 +5790,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// be invoked and we need to invoke it ourself.
if (mLocalSyncId >= 0) {
mBLASTSyncEngine.setReady(mLocalSyncId);
- } else {
- mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction);
+ return mWinAnimator.finishDrawingLocked(null);
}
+ mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction);
mUsingBLASTSyncTransaction = false;
mWaitingSyncId = 0;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
index 71e79b331cae..56727e81bda5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
@@ -16,34 +16,23 @@
package com.android.server.location;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
+import android.app.ActivityManagerInternal;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.UserInfo;
-import android.os.Handler;
-import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.LocalServices;
import com.android.server.location.UserInfoHelper.UserListener;
import org.junit.After;
@@ -51,16 +40,18 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.quality.Strictness;
-
-import java.util.ArrayList;
-import java.util.List;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class UserInfoHelperTest {
+ private static class TestUserInfoHelper extends UserInfoHelper {
+ TestUserInfoHelper(Context context) {
+ super(context);
+ }
+ }
+
private static final int USER1_ID = 1;
private static final int USER1_MANAGED_ID = 11;
private static final int[] USER1_PROFILES = new int[]{USER1_ID, USER1_MANAGED_ID};
@@ -70,69 +61,30 @@ public class UserInfoHelperTest {
@Mock private Context mContext;
@Mock private UserManager mUserManager;
+ @Mock private ActivityManagerInternal mActivityManagerInternal;
- private StaticMockitoSession mMockingSession;
- private List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>();
-
- private UserInfoHelper mHelper;
+ private TestUserInfoHelper mHelper;
@Before
public void setUp() {
- mMockingSession = mockitoSession()
- .initMocks(this)
- .spyStatic(ActivityManager.class)
- .strictness(Strictness.WARN)
- .startMocking();
+ initMocks(this);
+ LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal);
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
- doAnswer(invocation -> {
- mBroadcastReceivers.add(invocation.getArgument(0));
- return null;
- }).when(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), any(
- UserHandle.class), any(IntentFilter.class), isNull(), any(Handler.class));
- doReturn(USER1_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER1_ID);
- doReturn(USER2_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER2_ID);
- doReturn(new UserInfo(USER1_ID, "", 0)).when(mUserManager).getProfileParent(
- USER1_MANAGED_ID);
- doReturn(new UserInfo(USER2_ID, "", 0)).when(mUserManager).getProfileParent(
- USER2_MANAGED_ID);
-
- doReturn(USER1_ID).when(ActivityManager::getCurrentUser);
-
- mHelper = new UserInfoHelper(mContext);
+
+ doReturn(USER1_PROFILES).when(mUserManager).getEnabledProfileIds(USER1_ID);
+ doReturn(USER2_PROFILES).when(mUserManager).getEnabledProfileIds(USER2_ID);
+ doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER1_ID);
+ doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER1_MANAGED_ID);
+ doReturn(USER1_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds();
+
+ mHelper = new TestUserInfoHelper(mContext);
mHelper.onSystemReady();
}
@After
public void tearDown() {
- if (mMockingSession != null) {
- mMockingSession.finishMocking();
- }
- }
-
- private void switchUser(int userId) {
- doReturn(userId).when(ActivityManager::getCurrentUser);
- Intent intent = new Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE,
- userId);
- for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
- broadcastReceiver.onReceive(mContext, intent);
- }
- }
-
- private void startUser(int userId) {
- Intent intent = new Intent(Intent.ACTION_USER_STARTED).putExtra(Intent.EXTRA_USER_HANDLE,
- userId);
- for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
- broadcastReceiver.onReceive(mContext, intent);
- }
- }
-
- private void stopUser(int userId) {
- Intent intent = new Intent(Intent.ACTION_USER_STOPPED).putExtra(Intent.EXTRA_USER_HANDLE,
- userId);
- for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
- broadcastReceiver.onReceive(mContext, intent);
- }
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
}
@Test
@@ -140,16 +92,21 @@ public class UserInfoHelperTest {
UserListener listener = mock(UserListener.class);
mHelper.addListener(listener);
- switchUser(USER1_ID);
- verify(listener, never()).onUserChanged(anyInt(), anyInt());
-
- switchUser(USER2_ID);
- verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED);
- verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED);
-
- switchUser(USER1_ID);
- verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED);
- verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED);
+ mHelper.dispatchOnCurrentUserChanged(USER1_ID, USER2_ID);
+ verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED);
+ verify(listener, times(1)).onUserChanged(USER1_MANAGED_ID,
+ UserListener.CURRENT_USER_CHANGED);
+ verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED);
+ verify(listener, times(1)).onUserChanged(USER2_MANAGED_ID,
+ UserListener.CURRENT_USER_CHANGED);
+
+ mHelper.dispatchOnCurrentUserChanged(USER2_ID, USER1_ID);
+ verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED);
+ verify(listener, times(2)).onUserChanged(USER2_MANAGED_ID,
+ UserListener.CURRENT_USER_CHANGED);
+ verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED);
+ verify(listener, times(2)).onUserChanged(USER1_MANAGED_ID,
+ UserListener.CURRENT_USER_CHANGED);
}
@Test
@@ -157,11 +114,11 @@ public class UserInfoHelperTest {
UserListener listener = mock(UserListener.class);
mHelper.addListener(listener);
- startUser(USER1_ID);
+ mHelper.dispatchOnUserStarted(USER1_ID);
verify(listener).onUserChanged(USER1_ID, UserListener.USER_STARTED);
- startUser(USER2_ID);
- verify(listener).onUserChanged(USER2_ID, UserListener.USER_STARTED);
+ mHelper.dispatchOnUserStarted(USER1_MANAGED_ID);
+ verify(listener).onUserChanged(USER1_MANAGED_ID, UserListener.USER_STARTED);
}
@Test
@@ -169,24 +126,22 @@ public class UserInfoHelperTest {
UserListener listener = mock(UserListener.class);
mHelper.addListener(listener);
- stopUser(USER1_ID);
- verify(listener).onUserChanged(USER1_ID, UserListener.USER_STOPPED);
-
- stopUser(USER2_ID);
+ mHelper.dispatchOnUserStopped(USER2_ID);
verify(listener).onUserChanged(USER2_ID, UserListener.USER_STOPPED);
+
+ mHelper.dispatchOnUserStopped(USER2_MANAGED_ID);
+ verify(listener).onUserChanged(USER2_MANAGED_ID, UserListener.USER_STOPPED);
}
@Test
public void testCurrentUserIds() {
assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES);
- switchUser(USER2_ID);
+ doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_ID);
+ doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_MANAGED_ID);
+ doReturn(USER2_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds();
assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER2_PROFILES);
-
- switchUser(USER1_ID);
-
- assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES);
}
@Test
@@ -196,7 +151,11 @@ public class UserInfoHelperTest {
assertThat(mHelper.isCurrentUserId(USER2_ID)).isFalse();
assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isFalse();
- switchUser(USER2_ID);
+ doReturn(false).when(mActivityManagerInternal).isCurrentProfile(USER1_ID);
+ doReturn(false).when(mActivityManagerInternal).isCurrentProfile(USER1_MANAGED_ID);
+ doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_ID);
+ doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_MANAGED_ID);
+ doReturn(USER2_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds();
assertThat(mHelper.isCurrentUserId(USER1_ID)).isFalse();
assertThat(mHelper.isCurrentUserId(USER2_ID)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 7af7a23b1ef6..c34b8e19a41d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -33,6 +33,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.Looper;
@@ -261,4 +262,89 @@ public class HdmiControlServiceTest {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isTrue();
}
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_enabled() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isTrue();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_disabled() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isFalse();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isTrue();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_honorsUnregistration() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlService.removeHdmiControlVolumeControlStatusChangeListener(callback);
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isFalse();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate_multiple() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback1 = new VolumeControlFeatureCallback();
+ VolumeControlFeatureCallback callback2 = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback1);
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback2);
+
+
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback1.mCallbackReceived).isTrue();
+ assertThat(callback2.mCallbackReceived).isTrue();
+ assertThat(callback1.mVolumeControlEnabled).isTrue();
+ assertThat(callback2.mVolumeControlEnabled).isTrue();
+ }
+
+ private static class VolumeControlFeatureCallback extends
+ IHdmiCecVolumeControlFeatureListener.Stub {
+ boolean mCallbackReceived = false;
+ boolean mVolumeControlEnabled = false;
+
+ @Override
+ public void onHdmiCecVolumeControlFeature(boolean enabled) throws RemoteException {
+ this.mCallbackReceived = true;
+ this.mVolumeControlEnabled = enabled;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
index 5412bb5106ff..74b4d122cbc0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
@@ -18,8 +18,8 @@ package com.android.server.pm.parsing
import android.content.pm.PackageManager
import android.platform.test.annotations.Presubmit
+import androidx.test.filters.LargeTest
import com.google.common.truth.Expect
-import com.google.common.truth.Truth.assertWithMessage
import org.junit.Rule
import org.junit.Test
@@ -52,6 +52,7 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
}
}
+ @LargeTest
@Test
fun packageInfoEquality() {
val flags = PackageManager.GET_ACTIVITIES or
@@ -65,7 +66,9 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
PackageManager.GET_SERVICES or
PackageManager.GET_SHARED_LIBRARY_FILES or
PackageManager.GET_SIGNATURES or
- PackageManager.GET_SIGNING_CERTIFICATES
+ PackageManager.GET_SIGNING_CERTIFICATES or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
val oldPackageInfo = oldPackages.asSequence().map { oldPackageInfo(it, flags) }
val newPackageInfo = newPackages.asSequence().map { newPackageInfo(it, flags) }
@@ -77,11 +80,79 @@ class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
} else {
"$firstName | $secondName"
}
- expect.withMessage("${it.first?.applicationInfo?.sourceDir} $packageName")
- .that(it.first?.dumpToString())
- .isEqualTo(it.second?.dumpToString())
+
+ // Main components are asserted independently to separate the failures. Otherwise the
+ // comparison would include every component in one massive string.
+
+ val prefix = "${it.first?.applicationInfo?.sourceDir} $packageName"
+
+ expect.withMessage("$prefix PackageInfo")
+ .that(it.second?.dumpToString())
+ .isEqualTo(it.first?.dumpToString())
+
+ expect.withMessage("$prefix ApplicationInfo")
+ .that(it.second?.applicationInfo?.dumpToString())
+ .isEqualTo(it.first?.applicationInfo?.dumpToString())
+
+ val firstActivityNames = it.first?.activities?.map { it.name } ?: emptyList()
+ val secondActivityNames = it.second?.activities?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix activities")
+ .that(secondActivityNames)
+ .containsExactlyElementsIn(firstActivityNames)
+ .inOrder()
+
+ if (!it.first?.activities.isNullOrEmpty() && !it.second?.activities.isNullOrEmpty()) {
+ it.first?.activities?.zip(it.second?.activities!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
+
+ val firstReceiverNames = it.first?.receivers?.map { it.name } ?: emptyList()
+ val secondReceiverNames = it.second?.receivers?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix receivers")
+ .that(secondReceiverNames)
+ .containsExactlyElementsIn(firstReceiverNames)
+ .inOrder()
+
+ if (!it.first?.receivers.isNullOrEmpty() && !it.second?.receivers.isNullOrEmpty()) {
+ it.first?.receivers?.zip(it.second?.receivers!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
+
+ val firstProviderNames = it.first?.providers?.map { it.name } ?: emptyList()
+ val secondProviderNames = it.second?.providers?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix providers")
+ .that(secondProviderNames)
+ .containsExactlyElementsIn(firstProviderNames)
+ .inOrder()
+
+ if (!it.first?.providers.isNullOrEmpty() && !it.second?.providers.isNullOrEmpty()) {
+ it.first?.providers?.zip(it.second?.providers!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
+
+ val firstServiceNames = it.first?.services?.map { it.name } ?: emptyList()
+ val secondServiceNames = it.second?.services?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix services")
+ .that(secondServiceNames)
+ .containsExactlyElementsIn(firstServiceNames)
+ .inOrder()
+
+ if (!it.first?.services.isNullOrEmpty() && !it.second?.services.isNullOrEmpty()) {
+ it.first?.services?.zip(it.second?.services!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
}
}
}
-
-
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 0f028f05d514..420ff19aab74 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -19,6 +19,7 @@ package com.android.server.pm.parsing
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
+import android.content.pm.ComponentInfo
import android.content.pm.ConfigurationInfo
import android.content.pm.FeatureInfo
import android.content.pm.InstrumentationInfo
@@ -27,6 +28,8 @@ import android.content.pm.PackageParser
import android.content.pm.PackageUserState
import android.content.pm.PermissionInfo
import android.content.pm.ProviderInfo
+import android.content.pm.ServiceInfo
+import android.os.Bundle
import android.os.Debug
import android.os.Environment
import android.util.SparseArray
@@ -38,8 +41,10 @@ import com.android.server.pm.pkg.PackageStateUnserialized
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.BeforeClass
-import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock
import java.io.File
@@ -47,7 +52,7 @@ open class AndroidPackageParsingTestBase {
companion object {
- private const val VERIFY_ALL_APKS = false
+ private const val VERIFY_ALL_APKS = true
/** For auditing memory usage differences */
private const val DUMP_HPROF_TO_EXTERNAL = false
@@ -81,10 +86,14 @@ open class AndroidPackageParsingTestBase {
.filter { file -> file.name.endsWith(".apk") }
.toList()
}
+ .distinct()
private val dummyUserState = mock(PackageUserState::class.java).apply {
installed = true
- Mockito.`when`(isAvailable(anyInt())).thenReturn(true)
+ whenever(isAvailable(anyInt())) { true }
+ whenever(isMatch(any<ComponentInfo>(), anyInt())) { true }
+ whenever(isMatch(anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
+ anyString(), anyInt())) { true }
}
lateinit var oldPackages: List<PackageParser.Package>
@@ -145,6 +154,7 @@ open class AndroidPackageParsingTestBase {
private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> {
this.pkg = aPkg
whenever(pkgState) { PackageStateUnserialized() }
+ whenever(readUserState(anyInt())) { dummyUserState }
}
}
@@ -156,19 +166,10 @@ open class AndroidPackageParsingTestBase {
// The following methods prepend "this." because @hide APIs can cause an IDE to auto-import
// the R.attr constant instead of referencing the field in an attempt to fix the error.
- /**
- * Known exclusions:
- * - [ApplicationInfo.credentialProtectedDataDir]
- * - [ApplicationInfo.dataDir]
- * - [ApplicationInfo.deviceProtectedDataDir]
- * - [ApplicationInfo.processName]
- * - [ApplicationInfo.publicSourceDir]
- * - [ApplicationInfo.scanPublicSourceDir]
- * - [ApplicationInfo.scanSourceDir]
- * - [ApplicationInfo.sourceDir]
- * These attributes used to be assigned post-package-parsing as part of another component,
- * but are now adjusted directly inside [PackageImpl].
- */
+ // It's difficult to comment out a line in a triple quoted string, so this is used instead
+ // to ignore specific fields. A comment is required to explain why a field was ignored.
+ private fun Any?.ignored(comment: String): String = "IGNORED"
+
protected fun ApplicationInfo.dumpToString() = """
appComponentFactory=${this.appComponentFactory}
backupAgentName=${this.backupAgentName}
@@ -179,22 +180,31 @@ open class AndroidPackageParsingTestBase {
compatibleWidthLimitDp=${this.compatibleWidthLimitDp}
compileSdkVersion=${this.compileSdkVersion}
compileSdkVersionCodename=${this.compileSdkVersionCodename}
+ credentialProtectedDataDir=${this.credentialProtectedDataDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
+ crossProfile=${this.crossProfile.ignored("Added in R")}
+ dataDir=${this.dataDir.ignored("Deferred pre-R, but assigned immediately in R")}
descriptionRes=${this.descriptionRes}
+ deviceProtectedDataDir=${this.deviceProtectedDataDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
enabled=${this.enabled}
enabledSetting=${this.enabledSetting}
flags=${Integer.toBinaryString(this.flags)}
fullBackupContent=${this.fullBackupContent}
+ gwpAsanMode=${this.gwpAsanMode.ignored("Added in R")}
hiddenUntilInstalled=${this.hiddenUntilInstalled}
icon=${this.icon}
iconRes=${this.iconRes}
installLocation=${this.installLocation}
+ labelRes=${this.labelRes}
largestWidthLimitDp=${this.largestWidthLimitDp}
logo=${this.logo}
longVersionCode=${this.longVersionCode}
+ ${"".ignored("mHiddenApiPolicy is a private field")}
manageSpaceActivityName=${this.manageSpaceActivityName}
- maxAspectRatio.compareTo(that.maxAspectRatio)=${this.maxAspectRatio}
- metaData=${this.metaData}
- minAspectRatio.compareTo(that.minAspectRatio)=${this.minAspectRatio}
+ maxAspectRatio=${this.maxAspectRatio}
+ metaData=${this.metaData.dumpToString()}
+ minAspectRatio=${this.minAspectRatio}
minSdkVersion=${this.minSdkVersion}
name=${this.name}
nativeLibraryDir=${this.nativeLibraryDir}
@@ -206,18 +216,27 @@ open class AndroidPackageParsingTestBase {
permission=${this.permission}
primaryCpuAbi=${this.primaryCpuAbi}
privateFlags=${Integer.toBinaryString(this.privateFlags)}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
+ publicSourceDir=${this.publicSourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
requiresSmallestWidthDp=${this.requiresSmallestWidthDp}
resourceDirs=${this.resourceDirs?.contentToString()}
roundIconRes=${this.roundIconRes}
- secondaryCpuAbi=${this.secondaryCpuAbi}
- secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
+ scanPublicSourceDir=${this.scanPublicSourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
+ scanSourceDir=${this.scanSourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
seInfo=${this.seInfo}
seInfoUser=${this.seInfoUser}
+ secondaryCpuAbi=${this.secondaryCpuAbi}
+ secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
sharedLibraryFiles=${this.sharedLibraryFiles?.contentToString()}
sharedLibraryInfos=${this.sharedLibraryInfos}
showUserIcon=${this.showUserIcon}
+ sourceDir=${this.sourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
splitClassLoaderNames=${this.splitClassLoaderNames?.contentToString()}
- splitDependencies=${this.splitDependencies}
+ splitDependencies=${this.splitDependencies.dumpToString()}
splitNames=${this.splitNames?.contentToString()}
splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
splitSourceDirs=${this.splitSourceDirs?.contentToString()}
@@ -226,8 +245,8 @@ open class AndroidPackageParsingTestBase {
targetSdkVersion=${this.targetSdkVersion}
taskAffinity=${this.taskAffinity}
theme=${this.theme}
- uid=${this.uid}
uiOptions=${this.uiOptions}
+ uid=${this.uid}
versionCode=${this.versionCode}
volumeUuid=${this.volumeUuid}
zygotePreloadName=${this.zygotePreloadName}
@@ -241,19 +260,27 @@ open class AndroidPackageParsingTestBase {
""".trimIndent()
protected fun InstrumentationInfo.dumpToString() = """
+ banner=${this.banner}
credentialProtectedDataDir=${this.credentialProtectedDataDir}
dataDir=${this.dataDir}
deviceProtectedDataDir=${this.deviceProtectedDataDir}
functionalTest=${this.functionalTest}
handleProfiling=${this.handleProfiling}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ metaData=${this.metaData}
+ name=${this.name}
nativeLibraryDir=${this.nativeLibraryDir}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
primaryCpuAbi=${this.primaryCpuAbi}
publicSourceDir=${this.publicSourceDir}
secondaryCpuAbi=${this.secondaryCpuAbi}
secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
+ showUserIcon=${this.showUserIcon}
sourceDir=${this.sourceDir}
- splitDependencies=${this.splitDependencies.sequence()
- .map { it.first to it.second?.contentToString() }.joinToString()}
+ splitDependencies=${this.splitDependencies.dumpToString()}
splitNames=${this.splitNames?.contentToString()}
splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
splitSourceDirs=${this.splitSourceDirs?.contentToString()}
@@ -262,25 +289,40 @@ open class AndroidPackageParsingTestBase {
""".trimIndent()
protected fun ActivityInfo.dumpToString() = """
+ banner=${this.banner}
colorMode=${this.colorMode}
configChanges=${this.configChanges}
+ descriptionRes=${this.descriptionRes}
+ directBootAware=${this.directBootAware}
documentLaunchMode=${this.documentLaunchMode}
+ enabled=${this.enabled}
+ exported=${this.exported}
flags=${Integer.toBinaryString(this.flags)}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
launchMode=${this.launchMode}
launchToken=${this.launchToken}
lockTaskLaunchMode=${this.lockTaskLaunchMode}
+ logo=${this.logo}
maxAspectRatio=${this.maxAspectRatio}
maxRecents=${this.maxRecents}
+ metaData=${this.metaData.dumpToString()}
minAspectRatio=${this.minAspectRatio}
+ name=${this.name}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
parentActivityName=${this.parentActivityName}
permission=${this.permission}
- persistableMode=${this.persistableMode}
- privateFlags=${Integer.toBinaryString(this.privateFlags)}
+ persistableMode=${this.persistableMode.ignored("Could be dropped pre-R, fixed in R")}
+ privateFlags=${this.privateFlags}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
requestedVrComponent=${this.requestedVrComponent}
resizeMode=${this.resizeMode}
rotationAnimation=${this.rotationAnimation}
screenOrientation=${this.screenOrientation}
+ showUserIcon=${this.showUserIcon}
softInputMode=${this.softInputMode}
+ splitName=${this.splitName}
targetActivity=${this.targetActivity}
taskAffinity=${this.taskAffinity}
theme=${this.theme}
@@ -300,30 +342,77 @@ open class AndroidPackageParsingTestBase {
protected fun PermissionInfo.dumpToString() = """
backgroundPermission=${this.backgroundPermission}
+ banner=${this.banner}
descriptionRes=${this.descriptionRes}
flags=${Integer.toBinaryString(this.flags)}
group=${this.group}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ metaData=${this.metaData.dumpToString()}
+ name=${this.name}
nonLocalizedDescription=${this.nonLocalizedDescription}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
protectionLevel=${this.protectionLevel}
requestRes=${this.requestRes}
+ showUserIcon=${this.showUserIcon}
""".trimIndent()
protected fun ProviderInfo.dumpToString() = """
+ applicationInfo=${this.applicationInfo.ignored("Already checked")}
authority=${this.authority}
+ banner=${this.banner}
+ descriptionRes=${this.descriptionRes}
+ directBootAware=${this.directBootAware}
+ enabled=${this.enabled}
+ exported=${this.exported}
flags=${Integer.toBinaryString(this.flags)}
forceUriPermissions=${this.forceUriPermissions}
grantUriPermissions=${this.grantUriPermissions}
+ icon=${this.icon}
initOrder=${this.initOrder}
isSyncable=${this.isSyncable}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ metaData=${this.metaData.dumpToString()}
multiprocess=${this.multiprocess}
+ name=${this.name}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
pathPermissions=${this.pathPermissions?.joinToString {
"readPermission=${it.readPermission}\nwritePermission=${it.writePermission}"
}}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
readPermission=${this.readPermission}
+ showUserIcon=${this.showUserIcon}
+ splitName=${this.splitName}
uriPermissionPatterns=${this.uriPermissionPatterns?.contentToString()}
writePermission=${this.writePermission}
""".trimIndent()
+ protected fun ServiceInfo.dumpToString() = """
+ applicationInfo=${this.applicationInfo.ignored("Already checked")}
+ banner=${this.banner}
+ descriptionRes=${this.descriptionRes}
+ directBootAware=${this.directBootAware}
+ enabled=${this.enabled}
+ exported=${this.exported}
+ flags=${Integer.toBinaryString(this.flags)}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ mForegroundServiceType"${this.mForegroundServiceType}
+ metaData=${this.metaData.dumpToString()}
+ name=${this.name}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
+ permission=${this.permission}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
+ showUserIcon=${this.showUserIcon}
+ splitName=${this.splitName}
+ """.trimIndent()
+
protected fun ConfigurationInfo.dumpToString() = """
reqGlEsVersion=${this.reqGlEsVersion}
reqInputFeatures=${this.reqInputFeatures}
@@ -333,8 +422,10 @@ open class AndroidPackageParsingTestBase {
""".trimIndent()
protected fun PackageInfo.dumpToString() = """
- activities=${this.activities?.joinToString { it.dumpToString() }}
- applicationInfo=${this.applicationInfo.dumpToString()}
+ activities=${this.activities?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
+ applicationInfo=${this.applicationInfo.dumpToString()
+ .ignored("Checked separately in test")}
baseRevisionCode=${this.baseRevisionCode}
compileSdkVersion=${this.compileSdkVersion}
compileSdkVersionCodename=${this.compileSdkVersionCodename}
@@ -356,15 +447,18 @@ open class AndroidPackageParsingTestBase {
overlayTarget=${this.overlayTarget}
packageName=${this.packageName}
permissions=${this.permissions?.joinToString { it.dumpToString() }}
- providers=${this.providers?.joinToString { it.dumpToString() }}
- receivers=${this.receivers?.joinToString { it.dumpToString() }}
+ providers=${this.providers?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
+ receivers=${this.receivers?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }}
requestedPermissions=${this.requestedPermissions?.contentToString()}
requestedPermissionsFlags=${this.requestedPermissionsFlags?.contentToString()}
requiredAccountType=${this.requiredAccountType}
requiredForAllUsers=${this.requiredForAllUsers}
restrictedAccountType=${this.restrictedAccountType}
- services=${this.services?.contentToString()}
+ services=${this.services?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
sharedUserId=${this.sharedUserId}
sharedUserLabel=${this.sharedUserLabel}
signatures=${this.signatures?.joinToString { it.toCharsString() }}
@@ -378,11 +472,17 @@ open class AndroidPackageParsingTestBase {
versionName=${this.versionName}
""".trimIndent()
- @Suppress("unused")
- private fun <T> SparseArray<T>.sequence(): Sequence<Pair<Int, T>> {
- var index = 0
- return generateSequence {
- index++.takeIf { it < size() }?.let { keyAt(it) to valueAt(index) }
+ private fun Bundle?.dumpToString() = this?.keySet()?.associateWith { get(it) }?.toString()
+
+ private fun <T> SparseArray<T>?.dumpToString(): String {
+ if (this == null) {
+ return "EMPTY"
+ }
+
+ val list = mutableListOf<Pair<Int, T>>()
+ for (index in (0 until size())) {
+ list += keyAt(index) to valueAt(index)
}
+ return list.toString()
}
}
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index e86399e1a631..62b6a65cc6cb 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -60,14 +60,12 @@ import java.util.Set;
public class UriGrantsManagerServiceTest {
private UriGrantsMockContext mContext;
- private UriGrantsManagerService mService;
- private UriGrantsManagerInternal mLocalService;
+ private UriGrantsManagerInternal mService;
@Before
public void setUp() throws Exception {
mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
- mService = UriGrantsManagerService.createForTest(mContext.getFilesDir());
- mLocalService = mService.getLocalService();
+ mService = UriGrantsManagerService.createForTest(mContext.getFilesDir()).getLocalService();
}
/**
@@ -80,8 +78,7 @@ public class UriGrantsManagerServiceTest {
final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null,
- USER_PRIMARY);
+ intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY);
assertEquals(PKG_SOCIAL, needed.targetPkg);
assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
@@ -98,8 +95,7 @@ public class UriGrantsManagerServiceTest {
final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null,
- USER_SECONDARY);
+ intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_SECONDARY);
assertEquals(PKG_SOCIAL, needed.targetPkg);
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
@@ -113,8 +109,7 @@ public class UriGrantsManagerServiceTest {
public void testNeeded_public() {
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PUBLIC).addFlags(FLAG_READ);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_PUBLIC, PKG_SOCIAL, intent, intent.getFlags(), null,
- USER_PRIMARY);
+ intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_PRIMARY);
assertNull(needed);
}
@@ -128,7 +123,7 @@ public class UriGrantsManagerServiceTest {
final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PUBLIC, FLAG_READ);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_PUBLIC, PKG_SOCIAL, intent, intent.getFlags(), null, USER_SECONDARY);
+ intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_SECONDARY);
assertEquals(PKG_SOCIAL, needed.targetPkg);
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
@@ -143,7 +138,7 @@ public class UriGrantsManagerServiceTest {
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PRIVATE).addFlags(FLAG_READ);
try {
mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_PRIVATE, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
+ intent, UID_PRIMARY_PRIVATE, PKG_SOCIAL, USER_PRIMARY);
fail();
} catch (SecurityException expected) {
}
@@ -158,7 +153,7 @@ public class UriGrantsManagerServiceTest {
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_FORCE)
.addFlags(FLAG_READ);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_FORCE, PKG_FORCE, intent, intent.getFlags(), null, USER_PRIMARY);
+ intent, UID_PRIMARY_FORCE, PKG_FORCE, USER_PRIMARY);
assertEquals(asSet(new GrantUri(USER_PRIMARY, URI_FORCE, 0)), needed.uris);
}
@@ -172,15 +167,15 @@ public class UriGrantsManagerServiceTest {
{
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
.addFlags(FLAG_READ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- assertNull(mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
- intent, intent.getFlags(), null, USER_PRIMARY));
+ assertNull(mService.checkGrantUriPermissionFromIntent(
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY));
}
{
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
.addFlags(FLAG_READ | FLAG_PREFIX);
try {
- mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
- intent, intent.getFlags(), null, USER_PRIMARY);
+ mService.checkGrantUriPermissionFromIntent(
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY);
fail();
} catch (SecurityException expected) {
}
@@ -189,8 +184,8 @@ public class UriGrantsManagerServiceTest {
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
.addFlags(FLAG_READ | FLAG_PERSISTABLE);
try {
- mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
- intent, intent.getFlags(), null, USER_PRIMARY);
+ mService.checkGrantUriPermissionFromIntent(
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY);
fail();
} catch (SecurityException expected) {
}
@@ -209,8 +204,7 @@ public class UriGrantsManagerServiceTest {
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
.addFlags(FLAG_READ);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null,
- USER_SECONDARY);
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_SECONDARY);
assertEquals(FLAG_READ, needed.flags);
}
{
@@ -218,8 +212,7 @@ public class UriGrantsManagerServiceTest {
.addFlags(FLAG_READ | FLAG_PREFIX);
try {
mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null,
- USER_SECONDARY);
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_SECONDARY);
fail();
} catch (SecurityException expected) {
}
@@ -229,8 +222,7 @@ public class UriGrantsManagerServiceTest {
.addFlags(FLAG_READ | FLAG_PERSISTABLE);
try {
mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null,
- USER_SECONDARY);
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_SECONDARY);
fail();
} catch (SecurityException expected) {
}
@@ -248,21 +240,21 @@ public class UriGrantsManagerServiceTest {
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
.addFlags(FLAG_READ);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY);
assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, 0)), needed.uris);
}
{
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
.addFlags(FLAG_READ | FLAG_PREFIX);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY);
assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, FLAG_PREFIX)), needed.uris);
}
{
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
.addFlags(FLAG_READ | FLAG_PERSISTABLE);
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY);
+ intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY);
assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, 0)), needed.uris);
}
}
@@ -284,8 +276,8 @@ public class UriGrantsManagerServiceTest {
// When granting towards primary, persistable can't be honored so
// the entire grant fails
try {
- mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_CAMERA, PKG_SOCIAL, intent,
- intent.getFlags(), null, USER_PRIMARY);
+ mService.checkGrantUriPermissionFromIntent(
+ intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY);
fail();
} catch (SecurityException expected) {
}
@@ -294,8 +286,8 @@ public class UriGrantsManagerServiceTest {
// When granting towards secondary, persistable can't be honored so
// the entire grant fails
try {
- mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_CAMERA, PKG_SOCIAL, intent,
- intent.getFlags(), null, USER_SECONDARY);
+ mService.checkGrantUriPermissionFromIntent(
+ intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_SECONDARY);
fail();
} catch (SecurityException expected) {
}
@@ -310,18 +302,16 @@ public class UriGrantsManagerServiceTest {
public void testGrant_overlap() {
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PHOTO_1).addFlags(FLAG_READ);
- final UriPermissionOwner activity = new UriPermissionOwner(mLocalService, "activity");
- final UriPermissionOwner service = new UriPermissionOwner(mLocalService, "service");
+ final UriPermissionOwner activity = new UriPermissionOwner(mService, "activity");
+ final UriPermissionOwner service = new UriPermissionOwner(mService, "service");
final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ);
// Grant read via activity and write via service
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
- activity);
+ intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), activity);
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
- service);
+ intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), service);
// Verify that everything is good with the world
assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ));
@@ -338,7 +328,7 @@ public class UriGrantsManagerServiceTest {
@Test
public void testCheckAuthorityGrants() {
final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PHOTO_1).addFlags(FLAG_READ);
- final UriPermissionOwner owner = new UriPermissionOwner(mLocalService, "primary");
+ final UriPermissionOwner owner = new UriPermissionOwner(mService, "primary");
final ProviderInfo cameraInfo = mContext.mPmInternal.resolveContentProvider(
PKG_CAMERA, 0, USER_PRIMARY);
@@ -355,8 +345,7 @@ public class UriGrantsManagerServiceTest {
// Granting primary camera to primary social
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
- UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
- owner);
+ intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), owner);
assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
cameraInfo, USER_PRIMARY, true));
assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
@@ -368,8 +357,7 @@ public class UriGrantsManagerServiceTest {
// Granting secondary camera to primary social
mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent(
- UID_SECONDARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY),
- owner);
+ intent, UID_SECONDARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), owner);
assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
cameraInfo, USER_PRIMARY, true));
assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 752707e5a5dc..d366efe0d979 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -594,6 +594,7 @@ public final class SmsCbMessage implements Parcelable {
SmsCbEtwsInfo etwsInfo = getEtwsWarningInfo();
if (etwsInfo != null) {
cv.put(CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
+ cv.put(CellBroadcasts.ETWS_IS_PRIMARY, etwsInfo.isPrimary());
}
SmsCbCmasInfo cmasInfo = getCmasWarningInfo();
@@ -667,9 +668,12 @@ public final class SmsCbMessage implements Parcelable {
SmsCbEtwsInfo etwsInfo;
int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE);
- if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
+ int etwsIsPrimaryColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_IS_PRIMARY);
+ if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)
+ && etwsIsPrimaryColumn != -1 && !cursor.isNull(etwsIsPrimaryColumn)) {
int warningType = cursor.getInt(etwsWarningTypeColumn);
- etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null);
+ boolean isPrimary = cursor.getInt(etwsIsPrimaryColumn) != 0;
+ etwsInfo = new SmsCbEtwsInfo(warningType, false, false, isPrimary, null);
} else {
etwsInfo = null;
}
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 83b35616286a..9c5b7b66f2a3 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -135,7 +135,6 @@ java_sdk_library {
permitted_packages: [
"android.hardware.wifi",
"android.net.wifi",
- "android.x.net.wifi",
// Created by jarjar rules.
"com.android.wifi.x",
],
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 70c5e72e4e0c..b841921355e9 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -632,7 +632,6 @@ public class WifiInfo implements Parcelable {
/**
* @hide
- * TODO: makes real freq boundaries
*/
public boolean is24GHz() {
return ScanResult.is24GHz(mFrequency);
@@ -640,7 +639,6 @@ public class WifiInfo implements Parcelable {
/**
* @hide
- * TODO: makes real freq boundaries
*/
@UnsupportedAppUsage
public boolean is5GHz() {
@@ -648,6 +646,13 @@ public class WifiInfo implements Parcelable {
}
/**
+ * @hide
+ */
+ public boolean is6GHz() {
+ return ScanResult.is6GHz(mFrequency);
+ }
+
+ /**
* Record the MAC address of the WLAN interface
* @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
* @hide