From b0ae9ba7c8b511fafa21f4bf294d7380f09ed2e7 Mon Sep 17 00:00:00 2001 From: Andrey Yepin Date: Wed, 31 Jul 2024 15:21:58 -0700 Subject: [PATCH 001/652] Documentation update Bug: 352496527 Test: N/A Flag: DOCS_ONLY Change-Id: I86e1c8b5c7da1725bfe4113ac682837bda3a51b7 --- core/java/android/content/Intent.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 111e6a8e93ef..297f172802f6 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -6150,7 +6150,8 @@ public class Intent implements Parcelable, Cloneable { * {@link #EXTRA_CHOOSER_MODIFY_SHARE_ACTION}, * {@link #EXTRA_METADATA_TEXT}, * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}, - * {@link #EXTRA_CHOOSER_RESULT_INTENT_SENDER}. + * {@link #EXTRA_CHOOSER_RESULT_INTENT_SENDER}, + * {@link #EXTRA_EXCLUDE_COMPONENTS}. *

*/ @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING) -- GitLab From 03028555d279a156575dd7f8147f84bb6ce544db Mon Sep 17 00:00:00 2001 From: Vlad Popa Date: Tue, 24 Sep 2024 18:06:14 -0700 Subject: [PATCH 002/652] Add some improvements in abs vol sync with audio server Also improve logging in AudioService Flag: EXEMPT bugfix Test: manual Bug: 350103953 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:efb3a1fc184f27242cbb09d0c96cb7c673a89f58) Merged-In: Ic3a9f2a9d7aea6cb82892d9943d19dc9ec8fd5c5 Change-Id: Ic3a9f2a9d7aea6cb82892d9943d19dc9ec8fd5c5 --- .../android/server/audio/AudioService.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f64a16359028..e1909d91a77d 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1583,8 +1583,11 @@ public class AudioService extends IAudioService.Stub synchronized (mCachedAbsVolDrivingStreamsLock) { mCachedAbsVolDrivingStreams.forEach((dev, stream) -> { - mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", /*enabled=*/true, - stream); + boolean enabled = true; + if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) { + enabled = mAvrcpAbsVolSupported; + } + mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream); }); } } @@ -4848,7 +4851,7 @@ public class AudioService extends IAudioService.Stub if (absDev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) { enabled = mAvrcpAbsVolSupported; } - if (stream != streamType) { + if (stream != streamType || !enabled) { mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev, /*address=*/"", enabled, streamType); } @@ -10354,10 +10357,10 @@ public class AudioService extends IAudioService.Stub } /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) { - mAvrcpAbsVolSupported = support; - if (absVolumeIndexFix()) { - int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP; - synchronized (mCachedAbsVolDrivingStreamsLock) { + synchronized (mCachedAbsVolDrivingStreamsLock) { + mAvrcpAbsVolSupported = support; + if (absVolumeIndexFix()) { + int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP; mCachedAbsVolDrivingStreams.compute(a2dpDev, (dev, stream) -> { if (!mAvrcpAbsVolSupported) { mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/ @@ -12420,6 +12423,12 @@ public class AudioService extends IAudioService.Stub pw.println("\nLoudness alignment:"); mLoudnessCodecHelper.dump(pw); + pw.println("\nAbsolute voume devices:"); + synchronized (mCachedAbsVolDrivingStreamsLock) { + mCachedAbsVolDrivingStreams.forEach((dev, stream) -> pw.println( + "Device type: 0x" + Integer.toHexString(dev) + ", driving stream " + stream)); + } + mAudioSystem.dump(pw); } -- GitLab From 94bbc6a937ea3a1f96b1c2e06572437f9e8dc852 Mon Sep 17 00:00:00 2001 From: Sherry Zhou Date: Wed, 2 Oct 2024 20:51:15 +0000 Subject: [PATCH 003/652] Fix clock position is too high in lockscreen preview in tablet portrait mode Bug: 369937183 Flag: com.android.systemui.migrate_clocks_to_blueprint Test: manual test, attach screenshots in comment#6 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:efa861ac8951da4c6c82d849c64b29c06df68299) Merged-In: I4455798e3de7bb4b597a232926041311ce382b47 Change-Id: I4455798e3de7bb4b597a232926041311ce382b47 --- .../ui/binder/KeyguardPreviewClockViewBinder.kt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index 8386628f4c83..57cb10ff9367 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -121,7 +121,10 @@ object KeyguardPreviewClockViewBinder { private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) { constraints.apply { constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT) - constrainHeight(R.id.lockscreen_clock_view_large, ConstraintSet.MATCH_CONSTRAINT) + // The following two lines make lockscreen_clock_view_large is constrained to available + // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT + constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT) + constrainMaxHeight(R.id.lockscreen_clock_view_large, 0) val largeClockTopMargin = SystemBarUtils.getStatusBarHeight(context) + context.resources.getDimensionPixelSize( @@ -138,7 +141,7 @@ object KeyguardPreviewClockViewBinder { R.id.lockscreen_clock_view_large, ConstraintSet.END, PARENT_ID, - ConstraintSet.END + ConstraintSet.END, ) // In preview, we'll show UDFPS icon for UDFPS devices @@ -160,14 +163,14 @@ object KeyguardPreviewClockViewBinder { BOTTOM, PARENT_ID, BOTTOM, - clockBottomMargin + clockBottomMargin, ) } constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT) constrainHeight( R.id.lockscreen_clock_view, - context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height) + context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height), ) connect( R.id.lockscreen_clock_view, @@ -175,7 +178,7 @@ object KeyguardPreviewClockViewBinder { PARENT_ID, START, context.resources.getDimensionPixelSize(customizationR.dimen.clock_padding_start) + - context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal) + context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal), ) val smallClockTopMargin = context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + @@ -188,7 +191,7 @@ object KeyguardPreviewClockViewBinder { context: Context, rootView: ConstraintLayout, previewClock: ClockController, - viewModel: KeyguardPreviewClockViewModel + viewModel: KeyguardPreviewClockViewModel, ) { val cs = ConstraintSet().apply { clone(rootView) } applyClockDefaultConstraints(context, cs) -- GitLab From ade2c89f3d3a43c8636b96d270289eb80e0e09a0 Mon Sep 17 00:00:00 2001 From: Yining Liu Date: Tue, 1 Oct 2024 21:59:51 +0000 Subject: [PATCH 004/652] Fix java crash by inconsistent single-line view and view model Fix the java crash when a conversation notification entry has null MessagingStyle. Fix: 368441586 Flag: com.android.systemui.notification_async_hybrid_view_inflation Test: SingleLineViewBinderTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:8702b2246c3282abe105d2e60ca4f74d57d11e09) Merged-In: If47e3f0ccfcc5e7079189288a6f3820691abd3e8 Change-Id: If47e3f0ccfcc5e7079189288a6f3820691abd3e8 --- .../row/NotificationContentInflater.java | 5 +- .../row/ui/viewbinder/SingleLineViewBinder.kt | 12 ++-- .../row/SingleLineViewBinderTest.kt | 65 +++++++++++++++---- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 8c80fd400360..36e3e92e4063 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -203,13 +203,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder messagingStyle = mConversationProcessor .processNotification(entry, builder, mLogger); } - result.mInflatedSingleLineViewModel = SingleLineViewInflater + SingleLineViewModel viewModel = SingleLineViewInflater .inflateSingleLineViewModel( entry.getSbn().getNotification(), messagingStyle, builder, row.getContext() ); + // If the messagingStyle is null, we want to inflate the normal view + isConversation = viewModel.isConversation(); + result.mInflatedSingleLineViewModel = viewModel; result.mInflatedSingleLineView = SingleLineViewInflater.inflatePrivateSingleLineView( isConversation, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt index 3b0f1ee22be3..a17197c1f8ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt @@ -24,14 +24,14 @@ import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineVi object SingleLineViewBinder { @JvmStatic fun bind(viewModel: SingleLineViewModel?, view: HybridNotificationView?) { - if (viewModel?.isConversation() == true && view is HybridConversationNotificationView) { + if (view is HybridConversationNotificationView) { if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return - viewModel.conversationData?.avatar?.let { view.setAvatar(it) } + viewModel?.conversationData?.avatar?.let { view.setAvatar(it) } view.setText( - viewModel.titleText, - viewModel.contentText, - viewModel.conversationData?.conversationSenderName + viewModel?.titleText, + viewModel?.contentText, + viewModel?.conversationData?.conversationSenderName, ) } else { // bind the title and content text views @@ -39,7 +39,7 @@ object SingleLineViewBinder { bind( /* title = */ viewModel?.titleText, /* text = */ viewModel?.contentText, - /* contentView = */ null + /* contentView = */ null, ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt index 6b3fb5b4a2eb..503fa789cb80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt @@ -29,12 +29,13 @@ import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.in import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePublicSingleLineView import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder -import com.android.systemui.util.mockito.mock import kotlin.test.assertEquals import kotlin.test.assertNotNull +import kotlin.test.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) @@ -69,7 +70,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, entry = row.entry, context = context, - logger = mock() + logger = mock(), ) val publicView = @@ -78,7 +79,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, entry = row.entry, context = context, - logger = mock() + logger = mock(), ) assertNotNull(publicView) @@ -114,7 +115,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { .addMessage( "How about lunch?", System.currentTimeMillis(), - Person.Builder().setName("user2").build() + Person.Builder().setName("user2").build(), ) .setGroupConversation(true) notificationBuilder.setStyle(style).setShortcutId(SHORTCUT_ID) @@ -127,7 +128,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, entry = row.entry, context = context, - logger = mock() + logger = mock(), ) as HybridConversationNotificationView @@ -137,7 +138,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, entry = row.entry, context = context, - logger = mock() + logger = mock(), ) as HybridConversationNotificationView assertNotNull(publicView) @@ -150,10 +151,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { systemUiContext = context, ) // WHEN: binds the view - SingleLineViewBinder.bind( - viewModel, - view, - ) + SingleLineViewBinder.bind(viewModel, view) // THEN: the single-line conversation view should be bound with view model's corresponding // fields @@ -161,10 +159,55 @@ class SingleLineViewBinderTest : SysuiTestCase() { assertEquals(viewModel.contentText, view.textView.text) assertEquals( viewModel.conversationData?.conversationSenderName, - view.conversationSenderNameView.text + view.conversationSenderNameView.text, ) } + @Test + @EnableFlags(AsyncHybridViewInflation.FLAG_NAME) + fun bindConversationSingleLineView_nonConversationViewModel() { + // GIVEN: a ConversationSingleLineView, and a nonConversationViewModel + val style = Notification.BigTextStyle().bigText(CONTENT_TEXT) + notificationBuilder.setStyle(style) + val notification = notificationBuilder.build() + val row: ExpandableNotificationRow = helper.createRow(notification) + + val view = + inflatePrivateSingleLineView( + isConversation = true, + reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, + entry = row.entry, + context = context, + logger = mock(), + ) + + val publicView = + inflatePublicSingleLineView( + isConversation = true, + reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, + entry = row.entry, + context = context, + logger = mock(), + ) + assertNotNull(publicView) + + val viewModel = + SingleLineViewInflater.inflateSingleLineViewModel( + notification = notification, + messagingStyle = null, + builder = notificationBuilder, + systemUiContext = context, + ) + // WHEN: binds the view with the view model + SingleLineViewBinder.bind(viewModel, view) + + // THEN: the single-line view should be bound with view model's corresponding + // fields as a normal non-conversation single-line view + assertEquals(viewModel.titleText, view?.titleView?.text) + assertEquals(viewModel.contentText, view?.textView?.text) + assertNull(viewModel.conversationData) + } + private companion object { const val CHANNEL_ID = "CHANNEL_ID" const val CONTENT_TITLE = "A Cool New Feature" -- GitLab From 276128e3c9c49ae4790607f5867f04462b5948e3 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Tue, 30 Jul 2024 15:32:43 -0700 Subject: [PATCH 005/652] Separate finishTransaction for leftover transition When enter PiP from split-screen, the fade-in animation would be picked. The alpha will be reset to 1 as part of the finishTransaction and from the MixedTransitionHelper, when we dispatch the leftovers with the same finishTransaction, there is no guarantee that when finishTransaction is actually executed, which leads to a race condition. Fixing this by passing over a fresh finishTransaction to the leftovers in MixedTransitionHelper, similar to what we do for startTransaction. This CL covers the enter PiP from split-screen in gesture nav mode. Flag: EXEMPT bugfix Video: http://recall/-/aaaaaabFQoRHlzixHdtY/fcSpmPtl7xQUMCkJj0PFUY Video: http://recall/-/aaaaaabFQoRHlzixHdtY/cxkqthnSiPrK1t5FsbEBQD Test: Enter PiP from split-screen, make sure no flicker Test: Enter PiP with non-auto-enter API, make sure no regression there Test: atest --iteration 5 WMShellFlickerTestsPip1 -- --test-arg \ com.android.tradefed.testtype.AndroidJUnitTest:instrumentation-arg:filter-tests:="com.android.wm.shell.flicker.pip.FromSplitScreenEnterPipOnUserLeaveHintTest#visibleLayersShownMoreThanOneConsecutiveEntry[ROTATION_0_GESTURAL_NAV]" Test: atest --iteration 5 WMShellFlickerTestsPip1 -- --test-arg \ com.android.tradefed.testtype.AndroidJUnitTest:instrumentation-arg:filter-tests:="com.android.wm.shell.flicker.pip.FromSplitScreenAutoEnterPipOnGoToHomeTest#visibleLayersShownMoreThanOneConsecutiveEntry[ROTATION_0_GESTURAL_NAV]" Bug: 359667269 Bug: 312446524 Bug: 289943985 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ff188280c713b7ee0bb28a4cb829df85424f212e) Merged-In: I2f1c2bf9d002639c0e144d2743de9331dcf590c0 Change-Id: I2f1c2bf9d002639c0e144d2743de9331dcf590c0 --- .../android/wm/shell/transition/MixedTransitionHelper.java | 5 ++++- .../pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java index 30d7245436be..e61929fef312 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java @@ -141,10 +141,13 @@ public class MixedTransitionHelper { pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA); pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction, finishCB); + // make a new finishTransaction because pip's startEnterAnimation "consumes" it so + // we need a separate one to send over to launcher. + SurfaceControl.Transaction otherFinishT = new SurfaceControl.Transaction(); // Dispatch the rest of the transition normally. This will most-likely be taken by // recents or default handler. mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse, - otherStartT, finishTransaction, finishCB, mixedHandler); + otherStartT, otherFinishT, finishCB, mixedHandler); } else { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just " + "forward animation to Pip-Handler."); diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt index d03d7799d675..d1bf6acf785d 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt @@ -182,12 +182,6 @@ class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : } } - /** {@inheritDoc} */ - @FlakyTest(bugId = 312446524) - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - /** {@inheritDoc} */ @Test @FlakyTest(bugId = 336510055) -- GitLab From 4ae06726eb3259db1a0667f507f492f96a7aae10 Mon Sep 17 00:00:00 2001 From: Yabin Huang Date: Wed, 18 Sep 2024 18:06:59 +0000 Subject: [PATCH 006/652] Modify PhoneWindowManager to avoid interfering with current user's experience - There are several code locations that assume the operations are for the current user. - However, these operations could be executed in reponse to requests for visible background users in MUMD environment. - We should handle them to avoid interfering with current user's experience. Bug: 358267540 Test: atest WmTests:PhoneWindowManagerTests Flag: EXEMPT bugfix (cherry picked from https://partner-android-review.googlesource.com/q/commit:bd7104e0e1abe78201a4adcf6ce3bbc1abc0b93f) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:0a332b80a88ec64789afbad7bbc84371b9ecbefb) Merged-In: I21cff2407937d9de87108529a74d0e9e3f4ca4e8 Change-Id: I21cff2407937d9de87108529a74d0e9e3f4ca4e8 --- .../server/policy/PhoneWindowManager.java | 81 ++++++++++++++++++- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 63491e8434bf..ca6051874d78 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -37,6 +37,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.O; import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID; +import static android.os.UserManager.isVisibleBackgroundUsersEnabled; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -184,6 +185,7 @@ import android.service.dreams.IDreamManager; import android.service.vr.IPersistentVrStateCallbacks; import android.speech.RecognizerIntent; import android.telecom.TelecomManager; +import android.util.ArraySet; import android.util.Log; import android.util.MathUtils; import android.util.MutableBoolean; @@ -256,6 +258,7 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -718,6 +721,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Timeout for showing the keyguard after the screen is on, in case no "ready" is received. private int mKeyguardDrawnTimeout = 1000; + private final boolean mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled(); + + // Key codes that should be ignored for visible background users in MUMD environment. + private static final Set KEY_CODES_IGNORED_FOR_VISIBLE_BACKGROUND_USERS = + new ArraySet<>(Arrays.asList( + KeyEvent.KEYCODE_POWER, + KeyEvent.KEYCODE_SLEEP, + KeyEvent.KEYCODE_WAKEUP, + KeyEvent.KEYCODE_CALL, + KeyEvent.KEYCODE_ENDCALL, + KeyEvent.KEYCODE_ASSIST, + KeyEvent.KEYCODE_VOICE_ASSIST, + KeyEvent.KEYCODE_MUTE, + KeyEvent.KEYCODE_VOLUME_MUTE + )); + private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5; @@ -811,7 +830,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { event.recycle(); break; case MSG_HANDLE_ALL_APPS: - launchAllAppsAction(); + KeyEvent keyEvent = (KeyEvent) msg.obj; + if (isKeyEventForCurrentUser(keyEvent.getDisplayId(), keyEvent.getKeyCode(), + "launchAllAppsViaA11y")) { + launchAllAppsAction(); + } break; case MSG_RINGER_TOGGLE_CHORD: handleRingerChordGesture(); @@ -2082,7 +2105,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { switch (mLongPressOnHomeBehavior) { case LONG_PRESS_HOME_ALL_APPS: notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS); - launchAllAppsAction(); + if (isKeyEventForCurrentUser(event.getDisplayId(), event.getKeyCode(), + "launchAllAppsViaA11y")) { + launchAllAppsAction(); + } break; case LONG_PRESS_HOME_ASSIST: notifyKeyGestureCompleted(event, @@ -3763,7 +3789,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK); } else if (mPendingMetaAction) { if (!canceled) { - launchAllAppsAction(); + if (isKeyEventForCurrentUser(event.getDisplayId(), event.getKeyCode(), + "launchAllAppsViaA11y")) { + launchAllAppsAction(); + } notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS); } @@ -4030,7 +4059,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS: case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS: - if (complete) { + if (complete && isKeyEventForCurrentUser(event.getDisplayId(), + event.getKeycodes()[0], "launchAllAppsViaA11y")) { launchAllAppsAction(); } return true; @@ -4859,6 +4889,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 || event.isWakeKey(); + // There are key events that perform the operation as the current user, + // and these should be ignored for visible background users. + if (mVisibleBackgroundUsersEnabled + && KEY_CODES_IGNORED_FOR_VISIBLE_BACKGROUND_USERS.contains(keyCode) + && !isKeyEventForCurrentUser(event.getDisplayId(), keyCode, null)) { + return 0; + } + if (!mSystemBooted) { // If we have not yet booted, don't let key events do anything. // Exception: Wake and power key events are forwarded to PowerManager to allow it to @@ -5866,6 +5904,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void wakeUpFromWakeKey(KeyEvent event) { + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), "wakeUpFromWakeKey")) { + return; + } wakeUpFromWakeKey( event.getEventTime(), event.getKeyCode(), @@ -6445,6 +6487,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display. @Override public void setAllowLockscreenWhenOn(int displayId, boolean allow) { + // We should ignore this operation for visible background users + // until lockscreen supports multi-display. + if (mVisibleBackgroundUsersEnabled + && mUserManagerInternal.getUserAssignedToDisplay(displayId) != mCurrentUserId) { + return; + } if (allow) { mAllowLockscreenWhenOnDisplays.add(displayId); } else { @@ -7253,4 +7301,29 @@ public class PhoneWindowManager implements WindowManagerPolicy { } return DEFAULT_DISPLAY; } + + /** + * This method is intended to prevent key events for visible background users + * from interfering with the current user's experience in MUMD environment. + * + * @param displayId the displayId of the key event. + * @param keyCode the key code of the event. + * + * @return false if the key event is for a visible background user. + */ + private boolean isKeyEventForCurrentUser(int displayId, int keyCode, @Nullable String purpose) { + if (!mVisibleBackgroundUsersEnabled) { + return true; + } + int assignedUser = mUserManagerInternal.getUserAssignedToDisplay(displayId); + if (assignedUser == mCurrentUserId) { + return true; + } + if (DEBUG_INPUT) { + Slog.w(TAG, "Cannot handle " + KeyEvent.keyCodeToString(keyCode) + + (purpose != null ? " to " + purpose : "") + + " for visible background user(u" + assignedUser + ")"); + } + return false; + } } -- GitLab From 35aa9f5f4767f44fc20ae950d73ee5fe6514ff5f Mon Sep 17 00:00:00 2001 From: Atneya Nair Date: Wed, 25 Sep 2024 17:36:15 -0700 Subject: [PATCH 007/652] appop: Finish all when last in chain fail When starting an op for an attribution chain, if a later attr in the chain fails to start, we should finish the already started ops to avoid a split in the op state in the chain. Test: Manual with mic indicator and recording Test: CtsMediaAudioRecordPermissionTests Test: CtsSensorPrivacyTestCases Fixes: 363915467 Bug: 293603271 Flag: EXEMPT security (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c0bca399b2f809ca9efebb5e9ed492e0c32fa95c) Merged-In: I92060d44e666fa6725411de5d714ac0d380f42ae Change-Id: I92060d44e666fa6725411de5d714ac0d380f42ae --- .../android/server/pm/permission/PermissionManagerService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 df9f7fb3d6e5..5fc3e332b95c 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1015,8 +1015,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { permission, attributionSource, message, forDataDelivery, startDataDelivery, fromDatasource, attributedOp); // Finish any started op if some step in the attribution chain failed. - if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED - && result != PermissionChecker.PERMISSION_SOFT_DENIED) { + if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) { if (attributedOp == AppOpsManager.OP_NONE) { finishDataDelivery(AppOpsManager.permissionToOpCode(permission), attributionSource.asState(), fromDatasource); -- GitLab From e5b081962ab2ab2582b5de27dbf176e1e27d117d Mon Sep 17 00:00:00 2001 From: Josh Tsuji Date: Thu, 3 Oct 2024 18:23:08 -0400 Subject: [PATCH 008/652] Return to OCCLUDED from canceled swipe if needed. An edge case of a canceled screen off, followed by a partial swipe up to PRIMARY_BOUNCER, followed by a slightly late call to setOccluded=true resulted in the lockscreen overlapping the occluding app until a screen off. Tests in a follow-up CL (really!) because there are no existing tests for the dragging transition and it's a fairly large change to add support for that. Bug: 370177430 Test: manual/shortly Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:40aa6d94b61f04c1dc822783447d8e5847dc0ff5) Merged-In: I6af8b473756e43ebccecbccdd6c97d8d082ef912 Change-Id: I6af8b473756e43ebccecbccdd6c97d8d082ef912 --- .../interactor/FromLockscreenTransitionInteractor.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 1e9541e1923e..6d1d9cbd9aae 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -189,6 +189,7 @@ constructor( internalTransitionInteractor.currentTransitionInfoInternal, keyguardInteractor.statusBarState, keyguardInteractor.isKeyguardDismissible, + keyguardInteractor.isKeyguardOccluded, ) .collect { ( @@ -196,7 +197,8 @@ constructor( startedStep, currentTransitionInfo, statusBarState, - isKeyguardUnlocked) -> + isKeyguardUnlocked, + isKeyguardOccluded) -> val id = transitionId if (id != null) { if (startedStep.to == KeyguardState.PRIMARY_BOUNCER) { @@ -236,9 +238,13 @@ constructor( if (nextState == TransitionState.CANCELED) { transitionRepository.startTransition( TransitionInfo( - ownerName = name, + ownerName = + "$name " + + "(on behalf of FromPrimaryBouncerInteractor)", from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.LOCKSCREEN, + to = + if (isKeyguardOccluded) KeyguardState.OCCLUDED + else KeyguardState.LOCKSCREEN, modeOnCanceled = TransitionModeOnCanceled.REVERSE, animator = getDefaultAnimatorForTransitionsToState( -- GitLab From 9d704623ae4020782c070a8a17d7497f128d1d9e Mon Sep 17 00:00:00 2001 From: Sandeep Bandaru Date: Fri, 4 Oct 2024 00:09:38 +0000 Subject: [PATCH 009/652] Extend service permission list only accessible from SystemUid. As noted in b/185746653 - for isolated_compute_app, we do not want even the holding app for the isolated-process to be able to bind to it. This was implemented specifically for HOTWORD usecase previously and missed for few other usecases. Similar to hotword service, we are extending the same permission check to wearablesensingservice and ondeviceintelligence service which also run as isolated_compute_app and require this enforcement in framework. Bug: 369871251 Flag: EXEMPT bugfix Test: added CTS in topic (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:31ce2795a8fa161920ac4e153fb62a37cb20cf2c) Merged-In: I6bbe1a48de15243ace803e08c2ab7550c3612eb1 Change-Id: I6bbe1a48de15243ace803e08c2ab7550c3612eb1 --- services/core/java/com/android/server/am/ActiveServices.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 36665240c16b..f9197e3c5c42 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -5077,7 +5077,10 @@ public final class ActiveServices { + " requires " + r.permission); return new ServiceLookupResult(r.permission); } else if ((Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals(r.permission) - || Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE.equals(r.permission)) + || Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE.equals(r.permission) + || Manifest.permission.BIND_WEARABLE_SENSING_SERVICE.equals(r.permission) + || Manifest.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE.equals( + r.permission)) && callingUid != Process.SYSTEM_UID) { // Hotword detection and visual query detection must run in its own sandbox, and we // don't even trust its enclosing application to bind to it - only the system. -- GitLab From 7d723f582b46ca5b296042dbab798584514b0d2f Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Tue, 8 Oct 2024 12:27:30 +0000 Subject: [PATCH 010/652] Face auth bypass layout issues The notification shade height isn't stable on swipe up. Unsure what regressed this but add a check for bypass only that ensures a stable height as the user is swiping up. This matches other checks in the same file. Along with this, the AOD icons in bypass mode would also adjust while swiping. The existing code already accounts for bypass so remove the extra checks. Test: atest KeyguardRootViewModelTest Flag: EXEMPT bugfix Fixes: 370656534 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:01b33a743507465fda1f5e004c683579be3a8af2) Merged-In: I38b8f33bd714143891c27ff67e7b6c0c08226a51 Change-Id: I38b8f33bd714143891c27ff67e7b6c0c08226a51 --- .../ui/viewmodel/KeyguardRootViewModelTest.kt | 26 ++++++------------- .../ui/viewmodel/KeyguardRootViewModel.kt | 4 --- .../NotificationPanelViewController.java | 5 ++++ 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 5d606c67a4d7..4b1ed3b2354a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -188,17 +188,6 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() assertThat(isVisible?.isAnimating).isFalse() } - @Test - fun iconContainer_isVisible_bypassEnabled() = - testScope.runTest { - val isVisible by collectLastValue(underTest.isNotifIconContainerVisible) - runCurrent() - deviceEntryRepository.setBypassEnabled(true) - runCurrent() - - assertThat(isVisible?.value).isTrue() - } - @Test fun iconContainer_isNotVisible_pulseExpanding_notBypassing() = testScope.runTest { @@ -288,22 +277,23 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() } @Test - fun iconContainer_isNotVisible_bypassDisabled_onLockscreen() = + fun iconContainer_isNotVisible_notifsFullyHiddenThenVisible_bypassEnabled() = testScope.runTest { val isVisible by collectLastValue(underTest.isNotifIconContainerVisible) runCurrent() - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, - testScope, - ) notificationsKeyguardInteractor.setPulseExpanding(false) - deviceEntryRepository.setBypassEnabled(false) + deviceEntryRepository.setBypassEnabled(true) whenever(dozeParameters.alwaysOn).thenReturn(true) whenever(dozeParameters.displayNeedsBlanking).thenReturn(false) notificationsKeyguardInteractor.setNotificationsFullyHidden(true) runCurrent() + assertThat(isVisible?.value).isTrue() + assertThat(isVisible?.isAnimating).isTrue() + + notificationsKeyguardInteractor.setNotificationsFullyHidden(false) + runCurrent() + assertThat(isVisible?.value).isFalse() assertThat(isVisible?.isAnimating).isTrue() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 0b023d9bfead..630fb99bc70a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -341,12 +341,8 @@ constructor( when { // If there are no notification icons to show, then it can be hidden !hasAodIcons -> false - // If we're bypassing, then we're visible - isBypassEnabled -> true // If we are pulsing (and not bypassing), then we are hidden isPulseExpanding -> false - // Besides bypass above, they should not be visible on lockscreen - isOnLockscreen -> false // If notifs are fully gone, then we're visible areNotifsFullyHidden -> true // Otherwise, we're hidden diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 0c05dbde6117..e197f03b5575 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -2710,6 +2710,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private int calculatePanelHeightShade() { + // Bypass should always occupy the full height + if (mBarState == KEYGUARD && mKeyguardBypassController.getBypassEnabled()) { + return mNotificationStackScrollLayoutController.getHeight(); + } + int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin(); int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin; -- GitLab From 9e87948a44c1a27d0ec7fcedb40105da2913bd7b Mon Sep 17 00:00:00 2001 From: Calvin Huang Date: Tue, 8 Oct 2024 09:53:32 -0700 Subject: [PATCH 011/652] Update PAUSE usage state when activity move from RESUMED to STOPPING In some multitasking scenarios, an activity can be scheduled to move directly from the RESUMED state to the STOPPED state, bypassing the PAUSED state. But the activity will cycled through to PAUSED state anyway. Need to update the PAUSED usage state in this case. Test: atest com.google.android.gts.playstore.UsageStatsHostTest Test: atest ActivityRecordTests Bug: 368228446 Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:292ae8c060b23750422af0f822c0ca297addbfb7) Merged-In: Ib66e9627d4c7ed857df9d6ae614e2001ad2d97b6 Change-Id: Ib66e9627d4c7ed857df9d6ae614e2001ad2d97b6 --- .../core/java/com/android/server/wm/ActivityRecord.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 12d733fc8c1a..8d0639b1e73b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5863,6 +5863,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } + final State prevState = mState; mState = state; if (getTaskFragment() != null) { @@ -5903,6 +5904,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.updateBatteryStats(this, false); mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED); break; + case STOPPING: + // It is possible that an Activity is scheduled to be STOPPED directly from RESUMED + // state. Updating the PAUSED usage state in that case, since the Activity will be + // STOPPED while cycled through the PAUSED state. + if (prevState == RESUMED) { + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED); + } + break; case STOPPED: mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED); if (mDisplayContent != null) { -- GitLab From 85cdb918a065cc029942cc16c61cb088b30ef1ca Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Tue, 8 Oct 2024 20:13:14 +0000 Subject: [PATCH 012/652] Process adb shell input keyevent 82 through dismiss Previously, it would trigger a shade collapse, and eventually get to keyguard to dismiss. Sending the request to keyguard follows the path of every other dismiss request. Bug: 371893850 Test: atest CtsWindowManagerDeviceKeyguard:android.server.wm.keyguard.KeyguardLockedTests Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:55017463035c83ebb968f7abc0ca900ac3601533) Merged-In: I2c9a3ead9540ab473815e06d474d42978ff59cf6 Change-Id: I2c9a3ead9540ab473815e06d474d42978ff59cf6 --- .../interactor/KeyguardKeyEventInteractorTest.kt | 13 ++++++++----- .../domain/interactor/KeyguardKeyEventInteractor.kt | 11 ++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt index 13f30f560cdf..945e44afa455 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt @@ -46,6 +46,7 @@ import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.isNull @ExperimentalCoroutinesApi @SmallTest @@ -96,7 +97,7 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { .sendVolumeKeyEvent( eq(actionDownVolumeDownKeyEvent), eq(AudioManager.USE_DEFAULT_STREAM_TYPE), - eq(true) + eq(true), ) assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isTrue() @@ -104,7 +105,7 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { .sendVolumeKeyEvent( eq(actionDownVolumeUpKeyEvent), eq(AudioManager.USE_DEFAULT_STREAM_TYPE), - eq(true) + eq(true), ) } @@ -117,7 +118,7 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { .sendVolumeKeyEvent( eq(actionDownVolumeDownKeyEvent), eq(AudioManager.USE_DEFAULT_STREAM_TYPE), - eq(true) + eq(true), ) assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isFalse() @@ -125,7 +126,7 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { .sendVolumeKeyEvent( eq(actionDownVolumeUpKeyEvent), eq(AudioManager.USE_DEFAULT_STREAM_TYPE), - eq(true) + eq(true), ) } @@ -135,7 +136,9 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) - verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU) + val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU) + assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() + verify(statusBarKeyguardViewManager).dismissWithAction(any(), isNull(), eq(false)) } @Test diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt index 65b42e657e75..fcf486b5696b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler.Companion.handleAction import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper +import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.ShadeController @@ -105,7 +106,15 @@ constructor( (statusBarStateController.state != StatusBarState.SHADE) && statusBarKeyguardViewManager.shouldDismissOnMenuPressed() if (shouldUnlockOnMenuPressed) { - shadeController.animateCollapseShadeForced() + statusBarKeyguardViewManager.dismissWithAction( + object : OnDismissAction { + override fun onDismiss(): Boolean { + return false + } + }, + null, + false, + ) return true } return false -- GitLab From 7d4d2294d1bcdab1cc2086e4d3e367a4ba090bd4 Mon Sep 17 00:00:00 2001 From: "seokgyun.hong" Date: Fri, 19 Jul 2024 10:26:21 +0900 Subject: [PATCH 013/652] Prevent calls to StatusBarManagerInternal from visible background users Visible background users have access to UI on assigned displays on devices that have config_multiuserVisibleBackgroundUsers enabled. The main use case is Automotive's multi-display Whole Cabin experience where passengers (modeled as visible background users) can interact with the display in front of them concurrently with the driver (modeled as the the current user) interacting with driver's display. - Calls to StatusBarManagerInternal trigger callbacks to the registered IStatusBar for the current user. - However, StatusBarManagerInternal APIs can be called from not only the current user but also visible background users. - We should prevent this to ensure that visible background users do not interfere with the current user's experience. Bug: 332222893 Flag: EXEMPT bugfix Test: atest MagnificationConnectionManagerTest atest WmTests:PhoneWindowManagerTests (cherry picked from https://partner-android-review.googlesource.com/q/commit:62538ec79f939853bd4ea75e81f4f46204fce72d) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3bf69d6d2a6a0852c149d37304472d82a2f9f961) Merged-In: Icdc6515e69e8044cf972a9e14e2fdd7f7a4b6958 Change-Id: Icdc6515e69e8044cf972a9e14e2fdd7f7a4b6958 --- .../AccessibilityManagerService.java | 6 ++++ .../MagnificationConnectionManager.java | 10 ++++++ .../server/policy/PhoneWindowManager.java | 36 +++++++++++++++++-- .../server/search/SearchManagerService.java | 12 +++++++ .../MagnificationConnectionManagerTest.java | 7 ++++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7580b697b516..49f15e46894d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3653,6 +3653,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } + // Magnification connection should not be requested for visible background users. + // (b/332222893) + if (mUmi.isVisibleBackgroundFullUser(userState.mUserId)) { + return; + } + final boolean shortcutEnabled = (userState.isShortcutMagnificationEnabledLocked() || userState.isMagnificationSingleFingerTripleTapEnabledLocked() || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java index 19e3e690924e..fe06406e580a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java @@ -19,6 +19,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION; import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION_CALLBACK; import static android.os.Build.HW_TIMEOUT_MULTIPLIER; +import static android.os.UserHandle.getCallingUserId; import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID; @@ -54,6 +55,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; +import com.android.server.pm.UserManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -209,6 +211,7 @@ public class MagnificationConnectionManager implements private final Callback mCallback; private final AccessibilityTraceManager mTrace; private final MagnificationScaleProvider mScaleProvider; + private final UserManagerInternal mUserManagerInternal; public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback, AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) { @@ -217,6 +220,7 @@ public class MagnificationConnectionManager implements mCallback = callback; mTrace = trace; mScaleProvider = scaleProvider; + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } /** @@ -280,12 +284,18 @@ public class MagnificationConnectionManager implements * Requests {@link IMagnificationConnection} through * {@link StatusBarManagerInternal#requestMagnificationConnection(boolean)} and * destroys all window magnifications if necessary. + * NOTE: Currently, this is not allowed to call from visible background users.(b/332222893) * * @param connect {@code true} if needs connection, otherwise set the connection to null and * destroy all window magnifications. * @return {@code true} if {@link IMagnificationConnection} state is going to change. */ public boolean requestConnection(boolean connect) { + final int callingUserId = getCallingUserId(); + if (mUserManagerInternal.isVisibleBackgroundFullUser(callingUserId)) { + throw new SecurityException("Visible background user(u" + callingUserId + + " is not permitted to request magnification connection."); + } if (DBG) { Slog.d(TAG, "requestConnection :" + connect); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index ca6051874d78..e0c4ebeb9f89 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -734,7 +734,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { KeyEvent.KEYCODE_ASSIST, KeyEvent.KEYCODE_VOICE_ASSIST, KeyEvent.KEYCODE_MUTE, - KeyEvent.KEYCODE_VOLUME_MUTE + KeyEvent.KEYCODE_VOLUME_MUTE, + KeyEvent.KEYCODE_RECENT_APPS, + KeyEvent.KEYCODE_APP_SWITCH, + KeyEvent.KEYCODE_NOTIFICATION )); private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; @@ -2077,12 +2080,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { } switch (mDoubleTapOnHomeBehavior) { case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), "toggleRecentApps")) { + break; + } notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH); mHomeConsumed = true; toggleRecentApps(); break; case DOUBLE_TAP_HOME_PIP_MENU: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), + "showPictureInPictureMenu")) { + break; + } mHomeConsumed = true; showPictureInPictureMenuInternal(); break; @@ -2111,12 +2123,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } break; case LONG_PRESS_HOME_ASSIST: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), "launchAssistAction")) { + break; + } notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT); launchAssistAction(null, event.getDeviceId(), event.getEventTime(), AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); break; case LONG_PRESS_HOME_NOTIFICATION_PANEL: + if (!isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), "toggleNotificationPanel")) { + break; + } notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL); toggleNotificationPanel(); @@ -3477,7 +3497,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (isUserSetupComplete() && !keyguardOn) { if (mModifierShortcutManager.interceptKey(event)) { - dismissKeyboardShortcutsMenu(); + if (isKeyEventForCurrentUser( + event.getDisplayId(), event.getKeyCode(), + "dismissKeyboardShortcutsMenu")) { + dismissKeyboardShortcutsMenu(); + } mPendingMetaAction = false; mPendingCapsLockToggle = false; return true; @@ -4733,7 +4757,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // no keyguard stuff to worry about, just launch home! - if (mRecentsVisible) { + // If Recents is visible and the action is not from visible background users, + // hide Recents and notify it to launch Home. + if (mRecentsVisible + && (!mVisibleBackgroundUsersEnabled || displayId == DEFAULT_DISPLAY)) { try { ActivityManager.getService().stopAppSwitches(); } catch (RemoteException e) {} @@ -5477,6 +5504,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { * Notify the StatusBar that a system key was pressed. */ private void sendSystemKeyToStatusBar(KeyEvent key) { + if (!isKeyEventForCurrentUser(key.getDisplayId(), key.getKeyCode(), "handleSystemKey")) { + return; + } IStatusBarService statusBar = getStatusBarService(); if (statusBar != null) { try { diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index 9b39fa1e177c..a49a9fdf4cca 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -46,6 +46,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemService.TargetUser; +import com.android.server.pm.UserManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.FileDescriptor; @@ -89,6 +90,8 @@ public class SearchManagerService extends ISearchManager.Stub { @GuardedBy("mSearchables") private final SparseArray mSearchables = new SparseArray<>(); + private final UserManagerInternal mUserManagerInternal; + /** * Initializes the Search Manager service in the provided system context. * Only one instance of this object should be created! @@ -101,6 +104,7 @@ public class SearchManagerService extends ISearchManager.Stub { mMyPackageMonitor.register(context, null, UserHandle.ALL, true); new GlobalSearchProviderObserver(context.getContentResolver()); mHandler = BackgroundThread.getHandler(); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } private Searchables getSearchables(int userId) { @@ -336,6 +340,14 @@ public class SearchManagerService extends ISearchManager.Stub { @Override public void launchAssist(int userHandle, Bundle args) { + // Currently, visible background users are not allowed to launch assist.(b/332222893) + // TODO(b/368715893): Consider indirect calls from system service when checking the + // calling user. + final int callingUserId = UserHandle.getCallingUserId(); + if (mUserManagerInternal.isVisibleBackgroundFullUser(callingUserId)) { + throw new SecurityException("Visible background user(u" + callingUserId + + ") is not permitted to launch assist."); + } StatusBarManagerInternal statusBarManager = LocalServices.getService(StatusBarManagerInternal.class); if (statusBarManager != null) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java index 87fe6cf8f283..6d27dddfc357 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java @@ -62,6 +62,7 @@ import androidx.test.filters.FlakyTest; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; +import com.android.server.pm.UserManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.Before; @@ -92,12 +93,16 @@ public class MagnificationConnectionManagerTest { private MagnificationConnectionManager.Callback mMockCallback; private MockContentResolver mResolver; private MagnificationConnectionManager mMagnificationConnectionManager; + @Mock + private UserManagerInternal mMockUserManagerInternal; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + LocalServices.removeServiceForTest(UserManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); + LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal); mResolver = new MockContentResolver(); mMockConnection = new MockMagnificationConnection(); mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(), @@ -110,6 +115,8 @@ public class MagnificationConnectionManagerTest { Settings.Secure.putFloatForUser(mResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f, CURRENT_USER_ID); + + when(mMockUserManagerInternal.isVisibleBackgroundFullUser(anyInt())).thenReturn(false); } private void stubSetConnection(boolean needDelay) { -- GitLab From d869c280d408ee185e889bd948a714b5f860cbfb Mon Sep 17 00:00:00 2001 From: William Xiao Date: Wed, 9 Oct 2024 16:04:21 -0700 Subject: [PATCH 014/652] Call dream overlay callback onWakeUp in dream overlay reset The dream overlay may not always receive onWakeUp, which can cause SysUI state to incorrect and not recognize that the user dream has exited. This can happen when unlocking from the dream when the wake redirect is active. Always attempt to send onWakeUp when the overlay state is reset so that SysUI state will be correct. Bug: 370610704 Fixed: 370610704 Test: atest DreamOverlayCallbackControllerTest DreamOverlayServiceTest Flag: android.service.dreams.dream_wake_redirect (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:899bdd1c81e3e17f0f81b101123a852086c03ddb) Merged-In: I23716e7c884e41758a093461ec29f487f500bcbb Change-Id: I23716e7c884e41758a093461ec29f487f500bcbb --- .../dreams/DreamOverlayCallbackControllerTest.kt | 14 ++++++++++++++ .../systemui/dreams/DreamOverlayServiceTest.kt | 3 +++ .../dreams/DreamOverlayCallbackController.kt | 6 ++++-- .../systemui/dreams/DreamOverlayService.java | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt index d9dcfdc7becb..9c6fd4bf71b4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt @@ -56,6 +56,7 @@ class DreamOverlayCallbackControllerTest : SysuiTestCase() { // Adding twice should not invoke twice reset(callback) + underTest.onStartDream() underTest.addCallback(callback) underTest.onWakeUp() verify(callback, times(1)).onWakeUp() @@ -67,6 +68,19 @@ class DreamOverlayCallbackControllerTest : SysuiTestCase() { verify(callback, never()).onWakeUp() } + @Test + fun onWakeUp_multipleCalls() { + underTest.onStartDream() + assertThat(underTest.isDreaming).isEqualTo(true) + + underTest.addCallback(callback) + underTest.onWakeUp() + underTest.onWakeUp() + underTest.onWakeUp() + verify(callback, times(1)).onWakeUp() + assertThat(underTest.isDreaming).isEqualTo(false) + } + @Test fun onStartDreamInvokesCallback() { underTest.addCallback(callback) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt index 7dd717470153..30b0aef90489 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -712,6 +712,9 @@ class DreamOverlayServiceTest : SysuiTestCase() { // Verify DreamOverlayContainerViewController is destroyed. verify(mDreamOverlayContainerViewController).destroy() + + // DreamOverlay callback receives onWakeUp. + verify(mDreamOverlayCallbackController).onWakeUp() } @Test diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt index d5ff8f21abb2..2b617525fbcc 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt @@ -39,8 +39,10 @@ class DreamOverlayCallbackController @Inject constructor() : } fun onWakeUp() { - isDreaming = false - callbacks.forEach { it.onWakeUp() } + if (isDreaming) { + isDreaming = false + callbacks.forEach { it.onWakeUp() } + } } fun onStartDream() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 113e0011f5bd..0b465e9ce6db 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -296,6 +296,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mStateController.setLowLightActive(false); mStateController.setEntryAnimationsFinished(false); + mDreamOverlayCallbackController.onWakeUp(); + if (mDreamOverlayContainerViewController != null) { mDreamOverlayContainerViewController.destroy(); mDreamOverlayContainerViewController = null; -- GitLab From a9bd641e90b4503a7251ca776f7330f7b5b94e65 Mon Sep 17 00:00:00 2001 From: Ibrahim Yilmaz Date: Wed, 9 Oct 2024 19:30:52 +0000 Subject: [PATCH 015/652] Revert "Reset HUN clipping after cancellation of disappearing animation" This reverts commit ba2a922fb9cba2c5008006e73972fd7c1c199721. Reason for revert: b/369608449 Only run without interruptions can clear the clipping rect. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a03615b3e067dac90f6e1bad4aa3ce8bece26233) Merged-In: Ide457a83d2eca1ddafa78e6f10d89d62da56f4d4 Change-Id: Ide457a83d2eca1ddafa78e6f10d89d62da56f4d4 --- .../notification/row/ActivatableNotificationView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 7b6a2cb62b14..560028cb5640 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -444,9 +444,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (onFinishedRunnable != null) { onFinishedRunnable.run(); } + if (mRunWithoutInterruptions) { + enableAppearDrawing(false); + } // We need to reset the View state, even if the animation was cancelled - enableAppearDrawing(false); onAppearAnimationFinished(isAppearing); if (mRunWithoutInterruptions) { -- GitLab From 3aac99cc775c3b9bcedb7f08dddc7ea0c8ba7060 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 18 Sep 2024 15:51:26 +0000 Subject: [PATCH 016/652] AudioService: synchronize audio mode and focus for Telecom make sure that an audio focus abandon by Telecom is delayed until the corresponding audio mode reset sequence has been completed Bug: 339030944 Bug: 346573435 Flag: EXEMPT bug fix Test: repro steps in bug (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:15f9e56e1f898bd34400db8231f2cda71ca4cc91) Merged-In: If7249e8276c76df710e0fa363f0aad91c511c14f Change-Id: If7249e8276c76df710e0fa363f0aad91c511c14f --- .../server/audio/AudioDeviceBroker.java | 13 +- .../android/server/audio/AudioService.java | 130 ++++++++++++++---- 2 files changed, 111 insertions(+), 32 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 0fd22c583192..2bbe08d22020 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1321,9 +1321,9 @@ public class AudioDeviceBroker { sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info); } - /*package*/ void postSetModeOwner(int mode, int pid, int uid) { - sendLMsgNoDelay(MSG_I_SET_MODE_OWNER, SENDMSG_REPLACE, - new AudioModeInfo(mode, pid, uid)); + /*package*/ void postSetModeOwner(int mode, int pid, int uid, boolean signal) { + sendILMsgNoDelay(MSG_IL_SET_MODE_OWNER, SENDMSG_REPLACE, + signal ? 1 : 0, new AudioModeInfo(mode, pid, uid)); } /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) { @@ -2025,7 +2025,7 @@ public class AudioDeviceBroker { mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); } break; - case MSG_I_SET_MODE_OWNER: + case MSG_IL_SET_MODE_OWNER: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { int btScoRequesterUid = bluetoothScoRequestOwnerUid(); @@ -2036,6 +2036,9 @@ public class AudioDeviceBroker { } } } + if (msg.arg1 == 1 /*signal*/) { + mAudioService.decrementAudioModeResetCount(); + } break; case MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT: @@ -2224,7 +2227,7 @@ public class AudioDeviceBroker { private static final int MSG_REPORT_NEW_ROUTES = 13; private static final int MSG_II_SET_HEARING_AID_VOLUME = 14; private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15; - private static final int MSG_I_SET_MODE_OWNER = 16; + private static final int MSG_IL_SET_MODE_OWNER = 16; private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22; private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e1909d91a77d..c37d47149ef5 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1918,7 +1918,7 @@ public class AudioService extends IAudioService.Stub // Restore call state synchronized (mDeviceBroker.mSetModeLock) { onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(), - mContext.getPackageName(), true /*force*/); + mContext.getPackageName(), true /*force*/, false /*signal*/); } final int forSys; synchronized (mSettingsLock) { @@ -4746,14 +4746,47 @@ public class AudioService extends IAudioService.Stub } } if (updateAudioMode) { - sendMsg(mAudioHandler, - MSG_UPDATE_AUDIO_MODE, - existingMsgPolicy, - AudioSystem.MODE_CURRENT, - android.os.Process.myPid(), - mContext.getPackageName(), - delay); + postUpdateAudioMode(existingMsgPolicy, AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), mContext.getPackageName(), + false /*signal*/, delay); + } + } + } + + static class UpdateAudioModeInfo { + UpdateAudioModeInfo(int mode, int pid, String packageName, boolean signal) { + mMode = mode; + mPid = pid; + mPackageName = packageName; + mSignal = signal; + } + private final int mMode; + private final int mPid; + private final String mPackageName; + private final boolean mSignal; + + int getMode() { + return mMode; + } + int getPid() { + return mPid; + } + String getPackageName() { + return mPackageName; + } + boolean getSignal() { + return mSignal; + } + } + + void postUpdateAudioMode(int msgPolicy, int mode, int pid, String packageName, + boolean signal, int delay) { + synchronized (mAudioModeResetLock) { + if (signal) { + mAudioModeResetCount++; } + sendMsg(mAudioHandler, MSG_UPDATE_AUDIO_MODE, msgPolicy, 0, 0, + new UpdateAudioModeInfo(mode, pid, packageName, signal), delay); } } @@ -6155,13 +6188,9 @@ public class AudioService extends IAudioService.Stub } else { SetModeDeathHandler h = mSetModeDeathHandlers.get(index); mSetModeDeathHandlers.remove(index); - sendMsg(mAudioHandler, - MSG_UPDATE_AUDIO_MODE, - SENDMSG_QUEUE, - AudioSystem.MODE_CURRENT, - android.os.Process.myPid(), - mContext.getPackageName(), - 0); + postUpdateAudioMode(SENDMSG_QUEUE, AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), mContext.getPackageName(), + false /*signal*/, 0); } } } @@ -6407,19 +6436,14 @@ public class AudioService extends IAudioService.Stub } } - sendMsg(mAudioHandler, - MSG_UPDATE_AUDIO_MODE, - SENDMSG_REPLACE, - mode, - pid, - callingPackage, - 0); + postUpdateAudioMode(SENDMSG_REPLACE, mode, pid, callingPackage, + hasModifyPhoneStatePermission && mode == AudioSystem.MODE_NORMAL, 0); } } @GuardedBy("mDeviceBroker.mSetModeLock") void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage, - boolean force) { + boolean force, boolean signal) { if (requestedMode == AudioSystem.MODE_CURRENT) { requestedMode = getMode(); } @@ -6434,7 +6458,7 @@ public class AudioService extends IAudioService.Stub } if (DEBUG_MODE) { Log.v(TAG, "onUpdateAudioMode() new mode: " + mode + ", current mode: " - + mMode.get() + " requested mode: " + requestedMode); + + mMode.get() + " requested mode: " + requestedMode + " signal: " + signal); } if (mode != mMode.get() || force) { int status = AudioSystem.SUCCESS; @@ -6480,7 +6504,7 @@ public class AudioService extends IAudioService.Stub // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO // connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwner(mode, pid, uid); + mDeviceBroker.postSetModeOwner(mode, pid, uid, signal); } else { Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode); } @@ -10162,7 +10186,7 @@ public class AudioService extends IAudioService.Stub h.setRecordingActive(isRecordingActiveForUid(h.getUid())); if (wasActive != h.isActive()) { onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(), - mContext.getPackageName(), false /*force*/); + mContext.getPackageName(), false /*force*/, false /*signal*/); } } break; @@ -10192,7 +10216,9 @@ public class AudioService extends IAudioService.Stub case MSG_UPDATE_AUDIO_MODE: synchronized (mDeviceBroker.mSetModeLock) { - onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj, false /*force*/); + UpdateAudioModeInfo info = (UpdateAudioModeInfo) msg.obj; + onUpdateAudioMode(info.getMode(), info.getPid(), info.getPackageName(), + false /*force*/, info.getSignal()); } break; @@ -10895,9 +10921,59 @@ public class AudioService extends IAudioService.Stub return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } mmi.record(); + //delay abandon focus requests from Telecom if an audio mode reset from Telecom + // is still being processed + final boolean abandonFromTelecom = (mContext.checkCallingOrSelfPermission( + MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) + && ((aa != null && aa.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION) + || AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)); + if (abandonFromTelecom) { + synchronized (mAudioModeResetLock) { + final long start = java.lang.System.currentTimeMillis(); + long elapsed = 0; + while (mAudioModeResetCount > 0) { + if (DEBUG_MODE) { + Log.i(TAG, "Abandon focus from Telecom, waiting for mode change"); + } + try { + mAudioModeResetLock.wait( + AUDIO_MODE_RESET_TIMEOUT_MS - elapsed); + } catch (InterruptedException e) { + Log.w(TAG, "Interrupted while waiting for audio mode reset"); + } + elapsed = java.lang.System.currentTimeMillis() - start; + if (elapsed >= AUDIO_MODE_RESET_TIMEOUT_MS) { + Log.e(TAG, "Timeout waiting for audio mode reset"); + break; + } + } + if (DEBUG_MODE && elapsed != 0) { + Log.i(TAG, "Abandon focus from Telecom done waiting"); + } + } + } return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName); } + /** synchronization between setMode(NORMAL) and abandonAudioFocus() frmo Telecom */ + private static final long AUDIO_MODE_RESET_TIMEOUT_MS = 3000; + + private final Object mAudioModeResetLock = new Object(); + + @GuardedBy("mAudioModeResetLock") + private int mAudioModeResetCount = 0; + + void decrementAudioModeResetCount() { + synchronized (mAudioModeResetLock) { + if (mAudioModeResetCount > 0) { + mAudioModeResetCount--; + } else { + Log.w(TAG, "mAudioModeResetCount already 0"); + } + mAudioModeResetLock.notify(); + } + } + /** see {@link AudioManager#abandonAudioFocusForTest(AudioFocusRequest, String)} */ public int abandonAudioFocusForTest(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa, String callingPackageName) { -- GitLab From 6e7c3ccd6a2787af9dbc5c36323522f12343fb88 Mon Sep 17 00:00:00 2001 From: Chandru S Date: Thu, 10 Oct 2024 02:27:29 +0000 Subject: [PATCH 017/652] Fix persisting SFPS indicator issue. KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never reset. This is caused by usages of show()/forceShow() that only read (but not collect) the canShowAlternateBouncer flow to set the alternate bouncer visible state, if there is a race condition between when canShowAlternateBouncer flow changes to false and when the read happens, alternateBouncerVisible state will be set to an incorrect value and not reset on time. Confirmed with additional logs that this is what is happening. Flag: EXEMPT bugfix Fixes: 360737693 Test: verified manually 1. Enable screensaver while charging (on a large screen device with side fingerprint sensor) 2. Go to screensaver 3. Try authenticating with un-enrolled finger 5 times 4. Bouncer should be shown 5. SFPS indicator should not be visible on bouncer 6. unlock the device with PIN/pattern/password 7. SFPS indicator should not be visible after device is entered (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:070817baf2efe665c186e19775c9beda05199a72) Merged-In: I3bad4783b4283820bcf6473fd87da8cc8173c07f Change-Id: I3bad4783b4283820bcf6473fd87da8cc8173c07f --- .../interactor/AlternateBouncerInteractor.kt | 6 ++++++ .../phone/StatusBarKeyguardViewManager.java | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index 373671d01395..0949ea4d7797 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.domain.interactor +import android.util.Log import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository @@ -46,6 +47,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn /** Encapsulates business logic for interacting with the lock-screen alternate bouncer. */ @@ -137,6 +139,8 @@ constructor( flowOf(false) } } + .distinctUntilChanged() + .onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") } .stateIn( scope = scope, started = WhileSubscribed(), @@ -234,5 +238,7 @@ constructor( companion object { private const val MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS = 200L + + private const val TAG = "AlternateBouncerInteractor" } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 1ea26e5727ac..6fde603af4c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -528,6 +528,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb this::consumeKeyguardAuthenticatedBiometricsHandled ); } else { + // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( mAlternateBouncerInteractor.getCanShowAlternateBouncer(), this::consumeCanShowAlternateBouncer @@ -576,8 +577,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } private void consumeCanShowAlternateBouncer(boolean canShow) { - // do nothing, we only are registering for the flow to ensure that there's at least - // one subscriber that will update AlternateBouncerInteractor.canShowAlternateBouncer.value + // Hack: this is required to fix issues where + // KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never + // reset. This is caused by usages of show()/forceShow() that only read this flow to set the + // alternate bouncer visible state, if there is a race condition between when that flow + // changes to false and when the read happens, the flow will be set to an incorrect value + // and not reset on time. + if (!canShow) { + Log.d(TAG, "canShowAlternateBouncer turned false, maybe try hiding the alternate " + + "bouncer if it is already visible"); + mAlternateBouncerInteractor.maybeHide(); + } } /** Register a callback, to be invoked by the Predictive Back system. */ -- GitLab From 2355d893234e7a0887385be58df1ec84f8da2d8c Mon Sep 17 00:00:00 2001 From: Yunfan Chen Date: Thu, 10 Oct 2024 15:46:36 +0900 Subject: [PATCH 018/652] Partially revert the decor bounds calculation Previous change uses the intersection to calculate the non-decor frame to make sure the insets area is not deducted if the window is not even overlap with the insets. This causes a zero result when a split screen task is launched by another fullscreen task with an intent. The initial bounds of that window is out of the display and hence the overlap is zero. Without changing the split screen launch implementation, we have no better clue of the case. Revert the calculation back to the previous strategy - deduct the insets from the window regardless of the position relationship. Added todo for long term solution. The shell should report to the WM about the difference between the current window bounds and the final bounds to let the insets system report a stable bounds to the app. Bug: 364883053 Test: See reproduce steps in the bug Test: WindowPolicyTests Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:140e0710744d0426295d46863c233da6c1f6e8a6) Merged-In: I599a000f609c6a9964d6d0bc1bce14b0b9c979e6 Change-Id: I599a000f609c6a9964d6d0bc1bce14b0b9c979e6 --- .../android/server/wm/ConfigurationContainer.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 670a61dca5c8..05dcbb7f9af4 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; @@ -268,7 +269,16 @@ public abstract class ConfigurationContainer { } final DisplayPolicy.DecorInsets.Info decor = displayContent.getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh); - outAppBounds.intersectUnchecked(decor.mOverrideNonDecorFrame); + if (!outAppBounds.intersect(decor.mOverrideNonDecorFrame)) { + // TODO (b/364883053): When a split screen is requested from an app intent for a new + // task, the bounds is not the final bounds, and this is also not a bounds change + // event handled correctly with the offset. Revert back to legacy method for this + // case. + if (inOutConfig.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_MULTI_WINDOW) { + outAppBounds.inset(decor.mOverrideNonDecorInsets); + } + } if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) { outAppBounds.offset(-task.mOffsetXForInsets, -task.mOffsetYForInsets); } -- GitLab From fb0aea8712289a763b09bf8c9d5b00539312e673 Mon Sep 17 00:00:00 2001 From: Ibrahim Yilmaz Date: Thu, 10 Oct 2024 21:41:11 +0000 Subject: [PATCH 019/652] [SingleLineView] Fallback to mUser when senderPerson is null Bug: 371843309 Test: Send group notification for conversations. Last message sender in one of these conversations should be null. Check SingleLineView and see Messaging#mUser name comes instead of null Flag: com.android.systemui.notification_async_hybrid_view_inflation (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:29b080800b12dd5b406a1799150bd39d46d75618) Merged-In: I011378b8f0f0a29557b651fb85273a8cc91db6b8 Change-Id: I011378b8f0f0a29557b651fb85273a8cc91db6b8 --- .../row/SingleLineViewInflater.kt | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt index d67947d1fda8..4e26ae85100c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt @@ -90,7 +90,7 @@ internal object SingleLineViewInflater { notification = notification, isGroupConversation = isGroupConversation, builder = builder, - systemUiContext = systemUiContext + systemUiContext = systemUiContext, ) val conversationData = @@ -98,7 +98,7 @@ internal object SingleLineViewInflater { // We don't show the sender's name for one-to-one conversation conversationSenderName = if (isGroupConversation) conversationTextData?.senderName else null, - avatar = conversationAvatar + avatar = conversationAvatar, ) return SingleLineViewModel( @@ -111,7 +111,7 @@ internal object SingleLineViewInflater { @JvmStatic fun inflateRedactedSingleLineViewModel( context: Context, - isConversation: Boolean = false + isConversation: Boolean = false, ): SingleLineViewModel { val conversationData = if (isConversation) { @@ -122,7 +122,7 @@ internal object SingleLineViewInflater { com.android.systemui.res.R.drawable .ic_redacted_notification_single_line_icon ) - ) + ), ) } else { null @@ -134,7 +134,7 @@ internal object SingleLineViewInflater { context.getString( com.android.systemui.res.R.string.redacted_notification_single_line_text ), - conversationData + conversationData, ) } @@ -159,11 +159,13 @@ internal object SingleLineViewInflater { } // load the sender's name to display - val name = lastMessage.senderPerson?.name + // null senderPerson means the current user. + val name = lastMessage.senderPerson?.name ?: user.name + val senderName = systemUiContext.resources.getString( R.string.conversation_single_line_name_display, - if (Flags.cleanUpSpansAndNewLines()) name?.toString() else name + if (Flags.cleanUpSpansAndNewLines()) name?.toString() else name, ) // We need to find back-up values for those texts if they are needed and empty @@ -333,7 +335,7 @@ internal object SingleLineViewInflater { sender.icon ?: builder.getDefaultAvatar( name = sender.name, - uniqueNames = uniqueNames + uniqueNames = uniqueNames, ) lastKey = senderKey } else { @@ -341,7 +343,7 @@ internal object SingleLineViewInflater { sender.icon ?: builder.getDefaultAvatar( name = sender.name, - uniqueNames = uniqueNames + uniqueNames = uniqueNames, ) break } @@ -424,7 +426,7 @@ internal object SingleLineViewInflater { private fun Notification.Builder.getDefaultAvatar( name: CharSequence?, - uniqueNames: PeopleHelper.NameToPrefixMap? = null + uniqueNames: PeopleHelper.NameToPrefixMap? = null, ): Icon { val layoutColor = getSmallIconColor(/* isHeader= */ false) if (!name.isNullOrEmpty()) { @@ -432,7 +434,7 @@ internal object SingleLineViewInflater { return peopleHelper.createAvatarSymbol( /* name = */ name, /* symbol = */ symbol, - /* layoutColor = */ layoutColor + /* layoutColor = */ layoutColor, ) } // If name is null, create default avatar with background color -- GitLab From 50655f736d609a5b9614205e4560d07684a432d1 Mon Sep 17 00:00:00 2001 From: Chandru S Date: Fri, 4 Oct 2024 03:19:34 +0000 Subject: [PATCH 020/652] Fixes the issue of UDFPS icon background being white when the device is in DOZE_PULSING state Test: manually 1. Disable dark theme and AOD 2. Enroll fingerprint on a udfps device 3. Go to lockscreen, keep the phone on a table. 4. Press power button to force Lockscreen -> Dozing transition 5. move the phone around on the table to switch from dozing to doze_pulsing 6. Fingerprint icon background should not be visible now (black) Fixes: 366100470 Flag: EXEMPT bugfix (cherry picked from commit 331bbabf05c5701b1783a14b382d57bfbb54dc38) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ac2dacc7feece11ebc82af4081411c24078ac372) Merged-In: I6e962ff0b3bd1184e453b5b1ea247e5efb6f4cec Change-Id: I6e962ff0b3bd1184e453b5b1ea247e5efb6f4cec --- .../DeviceEntryBackgroundViewModelTest.kt | 90 +++++++++++++++++++ .../DeviceEntryBackgroundViewModel.kt | 2 + .../LockscreenToDozingTransitionViewModel.kt | 8 +- .../DeviceEntryBackgroundViewModelKosmos.kt | 52 +++++++++++ 4 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt create mode 100644 packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt new file mode 100644 index 000000000000..22677b2f8e3e --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 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.keyguard.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING +import com.android.systemui.keyguard.shared.model.TransitionState.STARTED +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@SmallTest +@RunWith(AndroidJUnit4::class) +class DeviceEntryBackgroundViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val underTest: DeviceEntryBackgroundViewModel by lazy { + kosmos.deviceEntryBackgroundViewModel + } + + @Test + fun lockscreenToDozingTransitionChangesBackgroundViewAlphaToZero() = + testScope.runTest { + kosmos.fingerprintPropertyRepository.supportsUdfps() + val alpha by collectLastValue(underTest.alpha) + + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + listOf(dozingToLockscreen(0f, STARTED), dozingToLockscreen(0.1f)), + testScope, + ) + runCurrent() + assertThat(alpha).isEqualTo(1.0f) + + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + listOf(lockscreenToDozing(0f, STARTED)), + testScope, + ) + runCurrent() + + assertThat(alpha).isEqualTo(0.0f) + } + + private fun lockscreenToDozing(value: Float, state: TransitionState = RUNNING): TransitionStep { + return TransitionStep( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + value = value, + transitionState = state, + ownerName = "DeviceEntryBackgroundViewModelTest", + ) + } + + private fun dozingToLockscreen(value: Float, state: TransitionState = RUNNING): TransitionStep { + return TransitionStep( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + value = value, + transitionState = state, + ownerName = "DeviceEntryBackgroundViewModelTest", + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt index a021de446911..546b32073aca 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt @@ -59,6 +59,7 @@ constructor( primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel, primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel, primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel, + lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel, ) { val color: Flow = deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground -> @@ -103,6 +104,7 @@ constructor( primaryBouncerToLockscreenTransitionViewModel .deviceEntryBackgroundViewAlpha, occludedToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha, + lockscreenToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha, ) .merge() .onStart { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt index d3eefca67037..7abf35de5dae 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt @@ -55,16 +55,16 @@ constructor( onCancel = { 1f }, ) + val deviceEntryBackgroundViewAlpha: Flow = + transitionAnimation.immediatelyTransitionTo(0f) + override val deviceEntryParentViewAlpha: Flow = deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { isUdfpsEnrolledAndEnabled -> if (isUdfpsEnrolledAndEnabled) { transitionAnimation.immediatelyTransitionTo(1f) } else { - transitionAnimation.sharedFlow( - duration = 250.milliseconds, - onStep = { 1f - it }, - ) + transitionAnimation.sharedFlow(duration = 250.milliseconds, onStep = { 1f - it }) } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt new file mode 100644 index 000000000000..09836ed46817 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 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.keyguard.ui.viewmodel + +import android.content.applicationContext +import com.android.systemui.common.ui.domain.interactor.configurationInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import kotlinx.coroutines.ExperimentalCoroutinesApi + +@ExperimentalCoroutinesApi +val Kosmos.deviceEntryBackgroundViewModel by Fixture { + DeviceEntryBackgroundViewModel( + context = applicationContext, + deviceEntryIconViewModel = deviceEntryIconViewModel, + configurationInteractor = configurationInteractor, + keyguardTransitionInteractor = keyguardTransitionInteractor, + alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel, + alternateBouncerToDozingTransitionViewModel = alternateBouncerToDozingTransitionViewModel, + aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, + dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel, + dreamingToAodTransitionViewModel = dreamingToAodTransitionViewModel, + dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel, + goneToAodTransitionViewModel = goneToAodTransitionViewModel, + goneToDozingTransitionViewModel = goneToDozingTransitionViewModel, + goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel, + lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel, + occludedToAodTransitionViewModel = occludedToAodTransitionViewModel, + occludedToDozingTransitionViewModel = occludedToDozingTransitionViewModel, + occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel, + primaryBouncerToAodTransitionViewModel = primaryBouncerToAodTransitionViewModel, + primaryBouncerToDozingTransitionViewModel = primaryBouncerToDozingTransitionViewModel, + primaryBouncerToLockscreenTransitionViewModel = + primaryBouncerToLockscreenTransitionViewModel, + lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel, + ) +} -- GitLab From 69f77e1883047bcfa3b3ad958ee3f077cba8b2d1 Mon Sep 17 00:00:00 2001 From: "hyunjae.choi" Date: Mon, 14 Oct 2024 19:07:43 +0000 Subject: [PATCH 021/652] Fix the enforcing method to pass current user for visible background users To verify if the calling user is the visible background user, we use the UserManagerService#enforceCurrentUserIfVisibleBackgroundEnabled method. This method requires the current user to be passed as a parameter. Bug: 373274137 Flag: EXEMPT bugfix Test: atest FrameworksUiServicesTests:com.android.server.UiModeManagerServiceTest Test: atest CtsAppTestCases:UiModeManagerTest Test: atest --user-type secondary_user CtsAppTestCases:UiModeManagerTest Test: atest --user-type secondary_user_on_secondary_display CtsAppTestCases:UiModeManagerTest (cherry picked from https://partner-android-review.googlesource.com/q/commit:87566613c12b7f44bd9465c6226d7b542ea1e3a2) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ec8e8394ba6de60b6466147717d036afdfbefedd) Merged-In: I3d24a483e3f563778d1e49971a35dd74895c79d1 Change-Id: I3d24a483e3f563778d1e49971a35dd74895c79d1 --- .../android/server/UiModeManagerService.java | 33 +++++++++++-------- .../server/UiModeManagerServiceTest.java | 3 ++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index f32031dec43c..f2069d011958 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -427,6 +427,11 @@ final class UiModeManagerService extends SystemService { mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression; } + @VisibleForTesting + void setCurrentUser(int currentUserId) { + mCurrentUser = currentUserId; + } + @Override public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { mCurrentUser = to.getUserIdentifier(); @@ -848,9 +853,9 @@ final class UiModeManagerService extends SystemService { throw new IllegalArgumentException("Unknown mode: " + mode); } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -913,7 +918,7 @@ final class UiModeManagerService extends SystemService { @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) { setAttentionModeThemeOverlay_enforcePermission(); - enforceCurrentUserIfVisibleBackgroundEnabled(UserHandle.getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) { @@ -1004,16 +1009,16 @@ final class UiModeManagerService extends SystemService { return false; } final int user = Binder.getCallingUserHandle().getIdentifier(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); - if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) { Slog.e(TAG, "Target user is not current user," + " INTERACT_ACROSS_USERS permission is required"); return false; - } + + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + // Store the last requested bedtime night mode state so that we don't need to notify // anyone if the user decides to switch to the night mode to bedtime. if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { @@ -1062,9 +1067,10 @@ final class UiModeManagerService extends SystemService { Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission"); return; } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1092,9 +1098,10 @@ final class UiModeManagerService extends SystemService { Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission"); return; } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1115,7 +1122,7 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mProjectionHolders == null) { @@ -1161,7 +1168,7 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); return releaseProjectionUnchecked(projectionType, callingPackage); } @@ -1203,7 +1210,7 @@ final class UiModeManagerService extends SystemService { return; } - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mProjectionListeners == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index c247c08c8010..3b0cb4ad8779 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -68,6 +68,7 @@ import static org.testng.Assert.assertThrows; import android.Manifest; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.Flags; import android.app.IOnProjectionStateChangedListener; @@ -247,6 +248,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { mInjector = spy(new TestInjector()); mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true, mTwilightManager, mInjector); + // Initialize the current user. + mUiManagerService.setCurrentUser(ActivityManager.getCurrentUser()); try { mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); } catch (SecurityException e) {/* ignore for permission denial */} -- GitLab From 0c45be989379c5c3869976118283601297fb784b Mon Sep 17 00:00:00 2001 From: Tiger Date: Wed, 16 Oct 2024 17:25:52 +0800 Subject: [PATCH 022/652] Update mPosition when creating a new InsetsSourceControl This should be part of commit: ac6cde62e08f5fe42cabc048ed985537d80d1d6b that InsetsSourceProvider.mPosition should always be the same as InsetsSourceControl.mSurfacePosition except during mHasPendingPosition. Fix: 370738846 Flag: EXEMPT bugfix Test: Click on the IME switch button and see if the IME position is correct. Test: atest InsetsSourceProviderTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b9474405880eb483cfda7c13111f985bf7d0247d) Merged-In: I5de9a80cbbb601bbdf39009f962fb1175751db28 Change-Id: I5de9a80cbbb601bbdf39009f962fb1175751db28 --- .../server/wm/InsetsSourceProvider.java | 3 ++- .../server/wm/InsetsSourceProviderTest.java | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 57b879277326..d3cae4c7b940 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -400,7 +400,7 @@ class InsetsSourceProvider { } final Point position = getWindowFrameSurfacePosition(); if (!mPosition.equals(position)) { - mPosition.set(position.x, position.y); + mPosition.set(position); if (windowState != null && windowState.getWindowFrames().didFrameSizeChange() && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) { windowState.applyWithNextDraw(mSetControlPositionConsumer); @@ -543,6 +543,7 @@ class InsetsSourceProvider { } boolean initiallyVisible = mClientVisible; final Point surfacePosition = getWindowFrameSurfacePosition(); + mPosition.set(surfacePosition); mAdapter = new ControlAdapter(surfacePosition); if (mSource.getType() == WindowInsets.Type.ime()) { if (android.view.inputmethod.Flags.refactorInsetsController()) { diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index e8d089c61362..f48ba65217e2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; @@ -259,6 +260,27 @@ public class InsetsSourceProviderTest extends WindowTestsBase { assertEquals(Insets.of(0, 0, 0, 100), insets); } + @Test + public void testUpdateInsetsControlPosition() { + final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); + + final WindowState ime1 = createWindow(null, TYPE_INPUT_METHOD, "ime1"); + ime1.getFrame().set(new Rect(0, 0, 0, 0)); + mImeProvider.setWindowContainer(ime1, null, null); + mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */); + ime1.getFrame().set(new Rect(0, 400, 500, 500)); + mImeProvider.updateInsetsControlPosition(ime1); + assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition()); + + final WindowState ime2 = createWindow(null, TYPE_INPUT_METHOD, "ime2"); + ime2.getFrame().set(new Rect(0, 0, 0, 0)); + mImeProvider.setWindowContainer(ime2, null, null); + mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */); + ime2.getFrame().set(new Rect(0, 400, 500, 500)); + mImeProvider.updateInsetsControlPosition(ime2); + assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition()); + } + @Test public void testSetRequestedVisibleTypes() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); -- GitLab From 810d2b8d39d08261f647a7b71c2b3113c2ba6a26 Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Mon, 14 Oct 2024 18:31:46 +0000 Subject: [PATCH 023/652] Fix tapping HUNs with weather clock This reverts another CL which attempted to fix touching the unlock icon when the notification shelf was next to it. By restoring the ordering in XML, this restores the proper touch hierarachy. Fixes: 370270324 Flag: EXEMPT bugfix (cherry picked from commit 0b558a935f18be75b71f06b81d8a807c3fca4c3d) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2261f88f1ade916d18582edc925e013ed66cce35) Merged-In: I906da1cf5e5106ada355232941399efe7f1d3b14 Change-Id: I906da1cf5e5106ada355232941399efe7f1d3b14 --- .../res/layout/super_notification_shade.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 22d34eb7b115..fbb07bed4b50 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -58,22 +58,22 @@ android:layout_height="match_parent" android:visibility="invisible" /> - - + - - + -- GitLab From d77da0e4f762703bd61a296d7ef4ac3777d78eb0 Mon Sep 17 00:00:00 2001 From: Geoffrey Boullanger Date: Wed, 16 Oct 2024 14:42:52 +0000 Subject: [PATCH 024/652] Revert "Migrate to best practise to read network state" This reverts commit f9f96b6fcb031edcfe7e294fbab071b6b0ce88c5. Reason for revert: breaking NTP calls when the device's date/time is too old Bug: b/372188013 (cherry picked from https://android-review.googlesource.com/q/commit:3ae8bf1de1e772922a0670a24d0b40fafa5307cf) Merged-In: I3f04991e7c9fb8f029da47ce895b1db65365bf9e Change-Id: I3f04991e7c9fb8f029da47ce895b1db65365bf9e --- core/java/android/util/NtpTrustedTime.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 9f54d9fca24b..3adbd686cd2c 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.Network; -import android.net.NetworkCapabilities; +import android.net.NetworkInfo; import android.net.SntpClient; import android.os.Build; import android.os.SystemClock; @@ -687,16 +687,8 @@ public abstract class NtpTrustedTime implements TrustedTime { if (connectivityManager == null) { return false; } - final NetworkCapabilities networkCapabilities = - connectivityManager.getNetworkCapabilities(network); - if (networkCapabilities == null) { - if (LOGD) Log.d(TAG, "getNetwork: failed to get network capabilities"); - return false; - } - final boolean isConnectedToInternet = networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_INTERNET) - && networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_VALIDATED); + final NetworkInfo ni = connectivityManager.getNetworkInfo(network); + // This connectivity check is to avoid performing a DNS lookup for the time server on a // unconnected network. There are races to obtain time in Android when connectivity // changes, which means that forceRefresh() can be called by various components before @@ -706,8 +698,8 @@ public abstract class NtpTrustedTime implements TrustedTime { // A side effect of check is that tests that run a fake NTP server on the device itself // will only be able to use it if the active network is connected, even though loopback // addresses are actually reachable. - if (!isConnectedToInternet) { - if (LOGD) Log.d(TAG, "getNetwork: no internet connectivity"); + if (ni == null || !ni.isConnected()) { + if (LOGD) Log.d(TAG, "getNetwork: no connectivity"); return false; } return true; -- GitLab From 389aab722a4ca5ec4f03131d189f32588344ad10 Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Wed, 18 Sep 2024 16:34:11 +0100 Subject: [PATCH 025/652] Block uninstall if DMRH in a managed user Previous change introduced a bug - on a personal device with a work profile the user could uninstall updates from the personal side, thus downgrading DMRH in the work profile, breaking it. With this change if a package is DMRH on any managed user, uninstall will be blocked Bug: 360807442 Test: btest a.d.c.DevicePolicyManagementRoleHolderTest Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3d5d962a6156fa822c0038e85315e7823a68ff59) Merged-In: I9fd8bed4a8203f3dbfa8dc4ea1388e7c8d136d9a Change-Id: I9fd8bed4a8203f3dbfa8dc4ea1388e7c8d136d9a --- .../server/pm/PackageManagerService.java | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ff9c3e5f467b..611e0d86202a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3389,18 +3389,31 @@ public class PackageManagerService implements PackageSender, TestUtilityService return true; } // Does it contain a device admin for any user? - int[] users; + int[] allUsers = mUserManager.getUserIds(); + int[] targetUsers; if (userId == UserHandle.USER_ALL) { - users = mUserManager.getUserIds(); + targetUsers = allUsers; } else { - users = new int[]{userId}; + targetUsers = new int[]{userId}; } - for (int i = 0; i < users.length; ++i) { - if (dpm.packageHasActiveAdmins(packageName, users[i])) { + + for (int i = 0; i < targetUsers.length; ++i) { + if (dpm.packageHasActiveAdmins(packageName, targetUsers[i])) { return true; } - if (isDeviceManagementRoleHolder(packageName, users[i]) - && dpmi.isUserOrganizationManaged(users[i])) { + } + + // If a package is DMRH on a managed user, it should also be treated as an admin on + // that user. If that package is also a system package, it should also be protected + // on other users otherwise "uninstall updates" on an unmanaged user may break + // management on other users because apk version is shared between all users. + var packageState = snapshotComputer().getPackageStateInternal(packageName); + if (packageState == null) { + return false; + } + for (int user : packageState.isSystem() ? allUsers : targetUsers) { + if (isDeviceManagementRoleHolder(packageName, user) + && dpmi.isUserOrganizationManaged(user)) { return true; } } -- GitLab From d0114a2027bc9b775001b5e417d90ac0997d59f9 Mon Sep 17 00:00:00 2001 From: Yabin Huang Date: Thu, 17 Oct 2024 15:29:03 -0700 Subject: [PATCH 026/652] Specify the display ID to mirror when creating virtual display In VirtualDisplayConfig, the default value of mDisplayIdToMirror is DEFAULT_DISPLAY. In order to create virtual display for visible background users (running on secondary displays), this CL passed the correct display ID when creating virtual display. Bug: 355529421 Test: atest CtsWindowManagerDeviceIme Test: atest --user-type secondary_user CtsWindowManagerDeviceIme Test: atest --user-type secondary_user_on_secondary_display CtsWindowManagerDeviceIme Flag: EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:036933d9608c43e483514e1c1003aaa6531664f1) Merged-In: Ibdd39e598560c08bcaf6fa2846191b82bae1a8ab Change-Id: Ibdd39e598560c08bcaf6fa2846191b82bae1a8ab --- .../android/hardware/display/DisplayManager.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 97f6899ff141..b0ea92d140a5 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -18,6 +18,7 @@ package android.hardware.display; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.HdrCapabilities.HdrType; +import static android.view.Display.INVALID_DISPLAY; import android.Manifest; import android.annotation.FlaggedApi; @@ -47,6 +48,7 @@ import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserManager; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -96,6 +98,8 @@ public final class DisplayManager { @GuardedBy("mLock") private final WeakDisplayCache mDisplayCache = new WeakDisplayCache(); + private int mDisplayIdToMirror = INVALID_DISPLAY; + /** * Broadcast receiver that indicates when the Wifi display status changes. *

@@ -1086,6 +1090,7 @@ public final class DisplayManager { if (surface != null) { builder.setSurface(surface); } + builder.setDisplayIdToMirror(getDisplayIdToMirror()); return createVirtualDisplay(builder.build(), handler, callback); } @@ -1163,6 +1168,7 @@ public final class DisplayManager { if (surface != null) { builder.setSurface(surface); } + builder.setDisplayIdToMirror(getDisplayIdToMirror()); return createVirtualDisplay(projection, builder.build(), callback, handler); } @@ -1708,6 +1714,16 @@ public final class DisplayManager { return mGlobal.getDefaultDozeBrightness(displayId); } + private int getDisplayIdToMirror() { + if (mDisplayIdToMirror == INVALID_DISPLAY) { + final UserManager userManager = mContext.getSystemService(UserManager.class); + mDisplayIdToMirror = userManager.isVisibleBackgroundUsersSupported() + ? userManager.getMainDisplayIdAssignedToUser() + : DEFAULT_DISPLAY; + } + return mDisplayIdToMirror; + } + /** * Listens for changes in available display devices. */ -- GitLab From 543a9e8dffbbd712635729439b9eb52467deccd2 Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Wed, 11 Sep 2024 08:25:20 +0000 Subject: [PATCH 027/652] Keep wallpaper in prepare back transition. Revert part of ag/29115929, it would be safe as long as the wallpaper surface won't merged into next transition. Bug: 365500016 Flag: com.android.window.flags.migrate_predictive_back_transition Test: atest BackAnimationControllerTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a3d5df4aaac6d687cde0a31f0e22324a6ac6f9db) Merged-In: I51232a95a585c1b931a68f456d9f208a6c50cb71 Change-Id: I51232a95a585c1b931a68f456d9f208a6c50cb71 --- .../com/android/wm/shell/back/BackAnimationController.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 3e5adf395cdd..5836085e0ddc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -1501,10 +1501,6 @@ public class BackAnimationController implements RemoteCallable= 0; --i) { final TransitionInfo.Change c = info.getChanges().get(i); - if (c.hasFlags(FLAG_IS_WALLPAPER)) { - st.setAlpha(c.getLeash(), 1.0f); - continue; - } if (TransitionUtil.isOpeningMode(c.getMode())) { final Point offset = c.getEndRelOffset(); st.setPosition(c.getLeash(), offset.x, offset.y); -- GitLab From 621c7532c08f3e7535de7ad7f2ccf82e73cd2198 Mon Sep 17 00:00:00 2001 From: Ashley Ingram Date: Thu, 17 Oct 2024 08:49:21 +0000 Subject: [PATCH 028/652] Make Wearable settings readable Settings were not hidden on Wear pre-T, so some apps have dependencies on these settings. When they run on U, they crash (because the settings are now hidden). We do not have an alternative API, so there's no other approach apps can take in subsequent API versions. Change the Settings to be Readable. We can deprecate these when official APIs are added to Wear. Bug: 372375270 Flag: EXEMPT: unflaggable (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:e0722e9156795c6c97ce232f2e52c686b1bef202) Merged-In: Ib599b0390980677aa1155366dad7c6f9b584224c Change-Id: Ib599b0390980677aa1155366dad7c6f9b584224c --- core/java/android/provider/Settings.java | 114 +++++++++++------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b8a8be159d12..ef8b3d738154 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -19338,14 +19338,14 @@ public final class Settings { * If hotword detection should be enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String HOTWORD_DETECTION_ENABLED = "hotword_detection_enabled"; /** * Whether Smart Replies are enabled within Wear. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SMART_REPLIES_ENABLED = "smart_replies_enabled"; /** @@ -19359,7 +19359,7 @@ public final class Settings { * If FLP should obtain location data from the paired device. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String OBTAIN_PAIRED_DEVICE_LOCATION = "obtain_paired_device_location"; @@ -19367,7 +19367,7 @@ public final class Settings { * The play store availability on companion phone. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String PHONE_PLAY_STORE_AVAILABILITY = "phone_play_store_availability"; @@ -19383,7 +19383,7 @@ public final class Settings { * Whether the bug report is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BUG_REPORT = "bug_report"; // Possible bug report states @@ -19396,14 +19396,14 @@ public final class Settings { * The enabled/disabled state of the SmartIlluminate. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SMART_ILLUMINATE_ENABLED = "smart_illuminate_enabled"; /** * Whether automatic time is enabled on the watch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_AUTO_TIME = "clockwork_auto_time"; // Possible clockwork auto time states @@ -19421,7 +19421,7 @@ public final class Settings { * Whether automatic time zone is enabled on the watch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_AUTO_TIME_ZONE = "clockwork_auto_time_zone"; // Possible clockwork auto time zone states @@ -19438,14 +19438,14 @@ public final class Settings { * Whether 24 hour time format is enabled on the watch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_24HR_TIME = "clockwork_24hr_time"; /** * Whether the auto wifi toggle setting is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AUTO_WIFI = "auto_wifi"; // Possible force wifi on states @@ -19465,7 +19465,7 @@ public final class Settings { * wifi requirement until this time). The time is in millis since epoch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS = "alt_bypass_wifi_requirement_time_millis"; @@ -19473,7 +19473,7 @@ public final class Settings { * Whether the setup was skipped. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SETUP_SKIPPED = "setup_skipped"; // Possible setup_skipped states @@ -19488,7 +19488,7 @@ public final class Settings { * The last requested call forwarding action. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String LAST_CALL_FORWARD_ACTION = "last_call_forward_action"; // Possible call forwarding actions @@ -19501,31 +19501,31 @@ public final class Settings { // Stem button settings. /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_1_TYPE = "STEM_1_TYPE"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_1_DATA = "STEM_1_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_1_DEFAULT_DATA = "STEM_1_DEFAULT_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_2_TYPE = "STEM_2_TYPE"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_2_DATA = "STEM_2_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_2_DEFAULT_DATA = "STEM_2_DEFAULT_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_3_TYPE = "STEM_3_TYPE"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_3_DATA = "STEM_3_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_3_DEFAULT_DATA = "STEM_3_DEFAULT_DATA"; // Stem types @@ -19540,14 +19540,14 @@ public final class Settings { * If the device should be muted when off body. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String MUTE_WHEN_OFF_BODY_ENABLED = "obtain_mute_when_off_body"; /** * Wear OS version string. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WEAR_OS_VERSION_STRING = "wear_os_version_string"; /** @@ -19560,28 +19560,28 @@ public final class Settings { * The android wear system version. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String ANDROID_WEAR_VERSION = "android_wear_version"; /** * The wear system capabiltiies. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SYSTEM_CAPABILITIES = "system_capabilities"; /** * The android wear system edition. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SYSTEM_EDITION = "android_wear_system_edition"; /** * The Wear platform MR number. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number"; /** @@ -19595,42 +19595,42 @@ public final class Settings { * Whether ambient is currently enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_ENABLED = "ambient_enabled"; /** * Whether ambient tilt to wake is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_TILT_TO_WAKE = "ambient_tilt_to_wake"; /** * Whether ambient low bit mode is enabled by developer options. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_LOW_BIT_ENABLED_DEV = "ambient_low_bit_enabled_dev"; /** * Whether ambient touch to wake is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_TOUCH_TO_WAKE = "ambient_touch_to_wake"; /** * Whether ambient tilt to bright is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_TILT_TO_BRIGHT = "ambient_tilt_to_bright"; /** * Whether touch and hold to edit WF is enabled * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String GESTURE_TOUCH_AND_HOLD_WATCH_FACE_ENABLED = "gesture_touch_and_hold_watchface_enabled"; @@ -19644,7 +19644,7 @@ public final class Settings { * Whether bedtime mode is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BEDTIME_MODE = "bedtime_mode"; /** @@ -19656,35 +19656,35 @@ public final class Settings { * Whether the current watchface is decomposable. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String DECOMPOSABLE_WATCHFACE = "current_watchface_decomposable"; /** * Whether to force ambient when docked. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_FORCE_WHEN_DOCKED = "ambient_force_when_docked"; /** * Whether the ambient low bit mode is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_LOW_BIT_ENABLED = "ambient_low_bit_enabled"; /** * The timeout duration in minutes of ambient mode when plugged in. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_PLUGGED_TIMEOUT_MIN = "ambient_plugged_timeout_min"; /** * What OS does paired device has. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String PAIRED_DEVICE_OS_TYPE = "paired_device_os_type"; // Possible values of PAIRED_DEVICE_OS_TYPE @@ -19719,7 +19719,7 @@ public final class Settings { * The user's last setting for hfp client. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String USER_HFP_CLIENT_SETTING = "user_hfp_client_setting"; // Possible hfp client user setting values @@ -19744,7 +19744,7 @@ public final class Settings { * The companion App name. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String COMPANION_APP_NAME = "wear_companion_app_name"; /** @@ -19752,21 +19752,21 @@ public final class Settings { * wear. 1 for supporting, 0 for not supporting. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String ENABLE_ALL_LANGUAGES = "enable_all_languages"; /** * The Locale (as language tag) the user chose at startup. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SETUP_LOCALE = "setup_locale"; /** * The version of oem setup present. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String OEM_SETUP_VERSION = "oem_setup_version"; /** @@ -19812,7 +19812,7 @@ public final class Settings { * -{@link BATTERY_SAVER_MODE_CUSTOM} * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BATTERY_SAVER_MODE = "battery_saver_mode"; /** @@ -19869,7 +19869,7 @@ public final class Settings { * If burn in protection is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BURN_IN_PROTECTION_ENABLED = "burn_in_protection"; /** @@ -19888,7 +19888,7 @@ public final class Settings { * RIGHT_WRIST_ROTATION_0 = "2", RIGHT_WRIST_ROTATION_180 = "3" * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WRIST_ORIENTATION_MODE = "wear_wrist_orientation_mode"; /** @@ -19927,7 +19927,7 @@ public final class Settings { * * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_SYSUI_PACKAGE = "clockwork_sysui_package"; /** @@ -19957,7 +19957,7 @@ public final class Settings { * Whether the device has Wet Mode/ Touch Lock Mode enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WET_MODE_ON = "wet_mode_on"; /** @@ -19976,7 +19976,7 @@ public final class Settings { * Whether charging sounds are enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CHARGING_SOUNDS_ENABLED = "wear_charging_sounds_enabled"; /** @@ -19985,7 +19985,7 @@ public final class Settings { * * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String DYNAMIC_COLOR_THEME_ENABLED = "dynamic_color_theme_enabled"; /** @@ -20077,7 +20077,7 @@ public final class Settings { * The key to indicate the data migration status on device upgrade in Wear Services. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String UPGRADE_DATA_MIGRATION_STATUS = "upgrade_data_migration_status"; @@ -20134,20 +20134,20 @@ public final class Settings { * The custom foreground color. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CUSTOM_COLOR_FOREGROUND = "custom_foreground_color"; /** * The custom background color. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CUSTOM_COLOR_BACKGROUND = "custom_background_color"; /** The status of the phone switching process. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String PHONE_SWITCHING_STATUS = "phone_switching_status"; /** @@ -20318,7 +20318,7 @@ public final class Settings { * Controls the launcher ui mode on wearable devices. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WEAR_LAUNCHER_UI_MODE = "wear_launcher_ui_mode"; /** Whether Wear Power Anomaly Service is enabled. -- GitLab From 5af7ec8fbeb52666d70873b62adbddb696716578 Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Thu, 26 Sep 2024 19:45:46 +0000 Subject: [PATCH 029/652] Revert "Sanitize Bundle from AbstractAccountAuthenticator." This reverts commit 09d381088952db904dbd0cc1c1c7362c033cf216. Reason for revert: b/342661425: fix filters obfuscated_gaia_id extra used for Cross profile account setup. A new version will be created. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2dc861d4ae98f4e18c82d3ad8f3b478dd2ddb7bb) Merged-In: I4da2ad09473f0155ebf11a45a48562b59ee5de37 Change-Id: I4da2ad09473f0155ebf11a45a48562b59ee5de37 --- .../accounts/AccountManagerService.java | 88 +------------------ .../accounts/AccountManagerServiceTest.java | 62 ++++--------- 2 files changed, 20 insertions(+), 130 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 88edb121c0c8..3499a3a5edde 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1773,8 +1773,7 @@ public class AccountManagerService // Create a Session for the target user and pass in the bundle completeCloningAccount(response, result, account, toAccounts, userFrom); } else { - // Bundle format is not defined. - super.onResultSkipSanitization(result); + super.onResult(result); } } }.bind(); @@ -1861,8 +1860,7 @@ public class AccountManagerService // account to avoid retries? // TODO: what we do with the visibility? - // Bundle format is not defined. - super.onResultSkipSanitization(result); + super.onResult(result); } @Override @@ -2108,7 +2106,6 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); - result = sanitizeBundle(result); IAccountManagerResponse response = getResponseAndClose(); if (response != null) { try { @@ -2462,7 +2459,6 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); - result = sanitizeBundle(result); if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) && !result.containsKey(AccountManager.KEY_INTENT)) { final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); @@ -2977,7 +2973,6 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); - result = sanitizeBundle(result); if (result != null) { String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); Bundle bundle = new Bundle(); @@ -3155,7 +3150,6 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); - result = sanitizeBundle(result); if (result != null) { if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { Intent intent = newGrantCredentialsPermissionIntent( @@ -3627,12 +3621,6 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); - Bundle sessionBundle = null; - if (result != null) { - // Session bundle will be removed from result. - sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); - } - result = sanitizeBundle(result); mNumResults++; Intent intent = null; if (result != null) { @@ -3694,6 +3682,7 @@ public class AccountManagerService // bundle contains data necessary for finishing the session // later. The session bundle will be encrypted here and // decrypted later when trying to finish the session. + Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); if (sessionBundle != null) { String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE); if (TextUtils.isEmpty(accountType) @@ -4081,7 +4070,6 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); - result = sanitizeBundle(result); IAccountManagerResponse response = getResponseAndClose(); if (response == null) { return; @@ -4395,7 +4383,6 @@ public class AccountManagerService @Override public void onResult(Bundle result) { Bundle.setDefusable(result, true); - result = sanitizeBundle(result); mNumResults++; if (result == null) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); @@ -4952,68 +4939,6 @@ public class AccountManagerService callback, resultReceiver); } - - // All keys for Strings passed from AbstractAccountAuthenticator using Bundle. - private static final String[] sStringBundleKeys = new String[] { - AccountManager.KEY_ACCOUNT_NAME, - AccountManager.KEY_ACCOUNT_TYPE, - AccountManager.KEY_AUTHTOKEN, - AccountManager.KEY_AUTH_TOKEN_LABEL, - AccountManager.KEY_ERROR_MESSAGE, - AccountManager.KEY_PASSWORD, - AccountManager.KEY_ACCOUNT_STATUS_TOKEN}; - - /** - * Keep only documented fields in a Bundle received from AbstractAccountAuthenticator. - */ - protected static Bundle sanitizeBundle(Bundle bundle) { - if (bundle == null) { - return null; - } - Bundle sanitizedBundle = new Bundle(); - Bundle.setDefusable(sanitizedBundle, true); - int updatedKeysCount = 0; - for (String stringKey : sStringBundleKeys) { - if (bundle.containsKey(stringKey)) { - String value = bundle.getString(stringKey); - sanitizedBundle.putString(stringKey, value); - updatedKeysCount++; - } - } - String key = AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY; - if (bundle.containsKey(key)) { - long expiryMillis = bundle.getLong(key, 0L); - sanitizedBundle.putLong(key, expiryMillis); - updatedKeysCount++; - } - key = AccountManager.KEY_BOOLEAN_RESULT; - if (bundle.containsKey(key)) { - boolean booleanResult = bundle.getBoolean(key, false); - sanitizedBundle.putBoolean(key, booleanResult); - updatedKeysCount++; - } - key = AccountManager.KEY_ERROR_CODE; - if (bundle.containsKey(key)) { - int errorCode = bundle.getInt(key, 0); - sanitizedBundle.putInt(key, errorCode); - updatedKeysCount++; - } - key = AccountManager.KEY_INTENT; - if (bundle.containsKey(key)) { - Intent intent = bundle.getParcelable(key, Intent.class); - sanitizedBundle.putParcelable(key, intent); - updatedKeysCount++; - } - if (bundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)) { - // The field is not copied in sanitized bundle. - updatedKeysCount++; - } - if (updatedKeysCount != bundle.size()) { - Log.w(TAG, "Size mismatch after sanitizeBundle call."); - } - return sanitizedBundle; - } - private abstract class Session extends IAccountAuthenticatorResponse.Stub implements IBinder.DeathRecipient, ServiceConnection { private final Object mSessionLock = new Object(); @@ -5304,14 +5229,9 @@ public class AccountManagerService } } } + @Override public void onResult(Bundle result) { - Bundle.setDefusable(result, true); - result = sanitizeBundle(result); - onResultSkipSanitization(result); - } - - public void onResultSkipSanitization(Bundle result) { Bundle.setDefusable(result, true); mNumResults++; Intent intent = null; diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index b9ce8ad0b018..0c92abce7254 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -1163,6 +1163,16 @@ public class AccountManagerServiceTest extends AndroidTestCase { verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); Bundle result = mBundleCaptor.getValue(); + Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE); + assertNotNull(sessionBundle); + // Assert that session bundle is decrypted and hence data is visible. + assertEquals(AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1, + sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1)); + // Assert finishSessionAsUser added calling uid and pid into the sessionBundle + assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_UID)); + assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_PID)); + assertEquals(sessionBundle.getString( + AccountManager.KEY_ANDROID_PACKAGE_NAME), "APCT.package"); // Verify response data assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null)); @@ -2111,6 +2121,12 @@ public class AccountManagerServiceTest extends AndroidTestCase { result.getString(AccountManager.KEY_ACCOUNT_NAME)); assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, result.getString(AccountManager.KEY_ACCOUNT_TYPE)); + + Bundle optionBundle = result.getParcelable( + AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE); + // Assert addAccountAsUser added calling uid and pid into the option bundle + assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_UID)); + assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_PID)); } @SmallTest @@ -3441,52 +3457,6 @@ public class AccountManagerServiceTest extends AndroidTestCase { + (readTotalTime.doubleValue() / readerCount / loopSize)); } - @SmallTest - public void testSanitizeBundle_expectedFields() throws Exception { - Bundle bundle = new Bundle(); - bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name"); - bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, "type"); - bundle.putString(AccountManager.KEY_AUTHTOKEN, "token"); - bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, "label"); - bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error message"); - bundle.putString(AccountManager.KEY_PASSWORD, "password"); - bundle.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN, "status"); - - bundle.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 123L); - bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); - bundle.putInt(AccountManager.KEY_ERROR_CODE, 456); - - Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle); - assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name"); - assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE), "type"); - assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTHTOKEN), "token"); - assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTH_TOKEN_LABEL), "label"); - assertEquals(sanitizedBundle.getString(AccountManager.KEY_ERROR_MESSAGE), "error message"); - assertEquals(sanitizedBundle.getString(AccountManager.KEY_PASSWORD), "password"); - assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN), "status"); - - assertEquals(sanitizedBundle.getLong( - AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0), 123L); - assertEquals(sanitizedBundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false), true); - assertEquals(sanitizedBundle.getInt(AccountManager.KEY_ERROR_CODE, 0), 456); - } - - @SmallTest - public void testSanitizeBundle_filtersUnexpectedFields() throws Exception { - Bundle bundle = new Bundle(); - bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name"); - bundle.putString("unknown_key", "value"); - Bundle sessionBundle = new Bundle(); - bundle.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle); - - Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle); - - assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name"); - assertFalse(sanitizedBundle.containsKey("unknown_key")); - // It is a valid response from Authenticator which will be accessed using original Bundle - assertFalse(sanitizedBundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)); - } - private void waitForCyclicBarrier(CyclicBarrier cyclicBarrier) { try { cyclicBarrier.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); -- GitLab From dfa33e98eee99228e83f8d855c89da368433fb38 Mon Sep 17 00:00:00 2001 From: Mayank Garg Date: Tue, 22 Oct 2024 11:47:30 -0700 Subject: [PATCH 030/652] Always allow System user to change device config in case of Multi-user-multi-display (MUMD) When visible background user (aka MUMD) is enabled, background users are visible on other displays. In this config, some namespaces (right now game_overlay) are not allowed to be updated by any user other than current user. The CL allows SYSTEM user to change that namespace config. Flag: EXEMPT bug fix Bug: 375232833 Bug: 361502556 Bug: 328686115 Test: atest GameManagerStatsTests Test: atest AndroidCarApiTest:android.car.apitest.DeviceConfigTest Test: atest --user-type secondary_user_on_secondary_display AndroidCarApiTest:android.car.apitest.DeviceConfigTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f14825b16b41e97e10deff3c39924a518ecba157) Merged-In: I73cd0fae216ab9aecefc2fdb49a719e919c6437a Change-Id: I73cd0fae216ab9aecefc2fdb49a719e919c6437a --- .../com/android/providers/settings/SettingsProvider.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 749ad0a993b3..6f8947c9bea1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2479,10 +2479,10 @@ public class SettingsProvider extends ContentProvider { final long identity = Binder.clearCallingIdentity(); try { int currentUser = ActivityManager.getCurrentUser(); - if (callingUser == currentUser) { - // enforce the deny list only if the caller is not current user. Currently only auto - // uses background visible user, and auto doesn't support profiles so profiles of - // current users is not checked here. + if (callingUser == currentUser || callingUser == UserHandle.USER_SYSTEM) { + // enforce the deny list only if the caller is not current user or not a system + // user. Currently only auto uses background visible user, and auto doesn't + // support profiles so profiles of current users is not checked here. return; } } finally { -- GitLab From e35050398c862100611b657550163526268589ae Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Tue, 15 Oct 2024 11:56:32 -0400 Subject: [PATCH 031/652] Move ScreenshotProxyService, etc to proxy/*, unify naming The service implementation is internal and usage should only be through the injectable interface 'ScreenshotProxy'. This also unifies the naming to be consistent with the interface and client wrapper. Flag: NONE straight rename no functional changes Test: N/A Bug: 339223847 Change-Id: Ie06aeabfb67f93fddd01981865a03a88c67fc625 --- packages/SystemUI/AndroidManifest.xml | 2 +- .../systemui/screenshot/ActionIntentExecutorTest.kt | 8 ++++---- .../systemui/screenshot/ActionIntentExecutor.kt | 8 ++++---- .../systemui/screenshot/dagger/ScreenshotModule.java | 4 ++-- .../data/repository/DisplayContentRepositoryImpl.kt | 10 +++++----- .../screenshot/{ => proxy}/IOnDoneCallback.aidl | 2 +- .../screenshot/{ => proxy}/IScreenshotProxy.aidl | 4 ++-- .../proxy/{SystemUiProxy.kt => ScreenshotProxy.kt} | 2 +- ...SystemUiProxyClient.kt => ScreenshotProxyClient.kt} | 9 +++------ ...SystemUiProxyModule.kt => ScreenshotProxyModule.kt} | 5 ++--- .../screenshot/{ => proxy}/ScreenshotProxyService.kt | 5 +++-- 11 files changed, 28 insertions(+), 31 deletions(-) rename packages/SystemUI/src/com/android/systemui/screenshot/{ => proxy}/IOnDoneCallback.aidl (93%) rename packages/SystemUI/src/com/android/systemui/screenshot/{ => proxy}/IScreenshotProxy.aidl (89%) rename packages/SystemUI/src/com/android/systemui/screenshot/proxy/{SystemUiProxy.kt => ScreenshotProxy.kt} (97%) rename packages/SystemUI/src/com/android/systemui/screenshot/proxy/{SystemUiProxyClient.kt => ScreenshotProxyClient.kt} (85%) rename packages/SystemUI/src/com/android/systemui/screenshot/proxy/{SystemUiProxyModule.kt => ScreenshotProxyModule.kt} (85%) rename packages/SystemUI/src/com/android/systemui/screenshot/{ => proxy}/ScreenshotProxyService.kt (97%) diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 510c9b742e74..b43b009302ec 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -458,7 +458,7 @@ android:label="@string/screenshot_scroll_label" android:finishOnTaskLaunch="true" /> - diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt index 612d646bb7d4..53a083f9ceae 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt @@ -17,13 +17,13 @@ package com.android.systemui.screenshot import android.content.Intent -import androidx.test.ext.junit.runners.AndroidJUnit4 import android.os.Process.myUserHandle import android.platform.test.annotations.EnableFlags import android.testing.TestableContext +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.systemui.Flags import com.android.systemui.SysuiTestCase -import com.android.systemui.screenshot.proxy.SystemUiProxy +import com.android.systemui.screenshot.proxy.ScreenshotProxy import com.android.systemui.settings.DisplayTracker import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.statusbar.phone.CentralSurfaces @@ -45,7 +45,7 @@ class ActionIntentExecutorTest : SysuiTestCase() { private val testableContext = TestableContext(mContext) private val activityManagerWrapper = mock() - private val systemUiProxy = mock() + private val screenshotProxy = mock() private val displayTracker = mock() @@ -55,7 +55,7 @@ class ActionIntentExecutorTest : SysuiTestCase() { activityManagerWrapper, testScope, mainDispatcher, - systemUiProxy, + screenshotProxy, displayTracker, ) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt index 7b01c36489fb..a365b7c5e9a1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt @@ -37,7 +37,7 @@ import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.screenshot.proxy.SystemUiProxy +import com.android.systemui.screenshot.proxy.ScreenshotProxy import com.android.systemui.settings.DisplayTracker import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.statusbar.phone.CentralSurfaces @@ -55,7 +55,7 @@ constructor( private val activityManagerWrapper: ActivityManagerWrapper, @Application private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, - private val systemUiProxy: SystemUiProxy, + private val screenshotProxy: ScreenshotProxy, private val displayTracker: DisplayTracker, ) { /** @@ -89,7 +89,7 @@ constructor( CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT ) } - systemUiProxy.dismissKeyguard() + screenshotProxy.dismissKeyguard() var transitionOptions: ActivityOptions? = null if (transitionCoordinator?.decor?.isAttachedToWindow == true) { transitionCoordinator.startExit() @@ -127,7 +127,7 @@ constructor( private suspend fun launchCrossProfileIntent( user: UserHandle, intent: Intent, - bundle: Bundle? + bundle: Bundle?, ) { val connector = getCrossProfileConnector(user) val completion = CompletableDeferred() diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java index 90695fa00ceb..253e3a613083 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java @@ -38,7 +38,7 @@ import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService; import com.android.systemui.screenshot.appclips.AppClipsService; import com.android.systemui.screenshot.message.MessageModule; import com.android.systemui.screenshot.policy.ScreenshotPolicyModule; -import com.android.systemui.screenshot.proxy.SystemUiProxyModule; +import com.android.systemui.screenshot.proxy.ScreenshotProxyModule; import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel; import dagger.Binds; @@ -50,7 +50,7 @@ import dagger.multibindings.IntoMap; /** * Defines injectable resources for Screenshots */ -@Module(includes = {ScreenshotPolicyModule.class, SystemUiProxyModule.class, MessageModule.class}) +@Module(includes = {ScreenshotPolicyModule.class, ScreenshotProxyModule.class, MessageModule.class}) public abstract class ScreenshotModule { @Binds diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt index e9599dcb026d..ec34808459c8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepositoryImpl.kt @@ -22,21 +22,21 @@ import android.app.IActivityTaskManager import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.screenshot.data.model.DisplayContentModel import com.android.systemui.screenshot.data.model.SystemUiState -import com.android.systemui.screenshot.proxy.SystemUiProxy +import com.android.systemui.screenshot.proxy.ScreenshotProxy import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext /** * Implements DisplayTaskRepository using [IActivityTaskManager], along with [ProfileTypeRepository] - * and [SystemUiProxy]. + * and [ScreenshotProxy]. */ @SuppressLint("MissingPermission") class DisplayContentRepositoryImpl @Inject constructor( private val atmService: IActivityTaskManager, - private val systemUiProxy: SystemUiProxy, + private val screenshotProxy: ScreenshotProxy, @Background private val background: CoroutineDispatcher, ) : DisplayContentRepository { @@ -53,8 +53,8 @@ constructor( ): DisplayContentModel { return DisplayContentModel( displayId, - SystemUiState(systemUiProxy.isNotificationShadeExpanded()), - rootTasks + SystemUiState(screenshotProxy.isNotificationShadeExpanded()), + rootTasks, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl similarity index 93% rename from packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl index e15030f78234..fb97fa71480b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/IOnDoneCallback.aidl +++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IOnDoneCallback.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.screenshot; +package com.android.systemui.screenshot.proxy; interface IOnDoneCallback { void onDone(boolean success); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl similarity index 89% rename from packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl index d2e3fbd65762..7b2f7e699521 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl +++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/IScreenshotProxy.aidl @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.systemui.screenshot; +package com.android.systemui.screenshot.proxy; -import com.android.systemui.screenshot.IOnDoneCallback; +import com.android.systemui.screenshot.proxy.IOnDoneCallback; /** Interface implemented by ScreenshotProxyService */ interface IScreenshotProxy { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt similarity index 97% rename from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt index e3eb3c4de80a..b44168f70ce3 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxy.kt @@ -24,7 +24,7 @@ package com.android.systemui.screenshot.proxy * * TODO: Rename and relocate 'ScreenshotProxyService' to this package and remove duplicate clients. */ -interface SystemUiProxy { +interface ScreenshotProxy { /** Indicate if the notification shade is "open"... (not in the fully collapsed position) */ suspend fun isNotificationShadeExpanded(): Boolean diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt similarity index 85% rename from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt index dcf58bde5f70..1158e2eb0ae5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyClient.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyClient.kt @@ -22,9 +22,6 @@ import android.content.Intent import android.util.Log import com.android.internal.infra.ServiceConnector import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.screenshot.IOnDoneCallback -import com.android.systemui.screenshot.IScreenshotProxy -import com.android.systemui.screenshot.ScreenshotProxyService import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -32,8 +29,8 @@ import kotlinx.coroutines.CompletableDeferred private const val TAG = "SystemUiProxy" -/** An implementation of [SystemUiProxy] using [ScreenshotProxyService]. */ -class SystemUiProxyClient @Inject constructor(@Application context: Context) : SystemUiProxy { +/** An implementation of [ScreenshotProxy] using [ScreenshotProxyService]. */ +class ScreenshotProxyClient @Inject constructor(@Application context: Context) : ScreenshotProxy { @SuppressLint("ImplicitSamInstance") private val proxyConnector: ServiceConnector = ServiceConnector.Impl( @@ -41,7 +38,7 @@ class SystemUiProxyClient @Inject constructor(@Application context: Context) : S Intent(context, ScreenshotProxyService::class.java), Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE, context.userId, - IScreenshotProxy.Stub::asInterface + IScreenshotProxy.Stub::asInterface, ) override suspend fun isNotificationShadeExpanded(): Boolean = suspendCoroutine { k -> diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt similarity index 85% rename from packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt index 4dd5cc4b55b4..797be9121ae8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/proxy/SystemUiProxyModule.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyModule.kt @@ -18,14 +18,13 @@ package com.android.systemui.screenshot.proxy import android.app.Service import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.screenshot.ScreenshotProxyService import dagger.Binds import dagger.Module import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @Module -interface SystemUiProxyModule { +interface ScreenshotProxyModule { @Binds @IntoMap @@ -34,5 +33,5 @@ interface SystemUiProxyModule { @Binds @SysUISingleton - fun bindSystemUiProxy(systemUiProxyClient: SystemUiProxyClient): SystemUiProxy + fun bindSystemUiProxy(screenshotProxyClient: ScreenshotProxyClient): ScreenshotProxy } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt similarity index 97% rename from packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt rename to packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt index 6df22f036273..a84f55268678 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/proxy/ScreenshotProxyService.kt @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.screenshot + +package com.android.systemui.screenshot.proxy import android.content.Intent import android.os.IBinder @@ -67,7 +68,7 @@ constructor( null, true /* dismissShade */, true /* afterKeyguardGone */, - true /* deferred */ + true, /* deferred */ ) } -- GitLab From 1941110a17571b9039645ced44210c56957cbc74 Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Wed, 30 Oct 2024 11:22:25 +0200 Subject: [PATCH 032/652] KeyguardUpdateMonitor: Add missing getFaceAuthInteractor null check Fixes commit 5e49307013cf55d0e37628b55d84ef71b4855d47 Flag: EXEMPT bugfix Test: unfold foldable device without face auth. Change-Id: Idbfab8dbe671bfd6b992f5945d6636e99feccbc4 --- .../src/com/android/keyguard/KeyguardUpdateMonitor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 7f5839d4f1fb..057fb68bad78 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1873,7 +1873,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (posture == DEVICE_POSTURE_OPENED) { mLogger.d("Posture changed to open - attempting to request active" + " unlock and run face auth"); - getFaceAuthInteractor().onDeviceUnfolded(); + if (getFaceAuthInteractor() != null) { + getFaceAuthInteractor().onDeviceUnfolded(); + } requestActiveUnlockFromWakeReason(PowerManager.WAKE_REASON_UNFOLD_DEVICE, false); } -- GitLab From 5e41bb50027ca08201be826f7d94ff5d0fba5c37 Mon Sep 17 00:00:00 2001 From: "seokgyun.hong" Date: Wed, 30 Oct 2024 17:54:12 +0000 Subject: [PATCH 033/652] Disallow autofill session for visible background users Currently, autofill service does not fully support visible background users. Prevent starting autofill sessions for visible background users to avoid issues with their autofill behavior. Bug: 375500806 Test: manual - check that autofill is not working for a visible background user. Flag: EXEMPT bugfix (cherry picked from https://partner-android-review.googlesource.com/q/commit:9489b856d41b15df98a09e2c5e384e041892ade5) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bc78ceb189e7ec9ce765d83e3dd7f311ccb6de94) Merged-In: Ibe368d7dd01d62ad5ca3e36ea3dd93e02077d7ad Change-Id: Ibe368d7dd01d62ad5ca3e36ea3dd93e02077d7ad --- .../server/autofill/AutofillManagerServiceImpl.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index c9f892907b59..b52c65054e51 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -92,6 +92,7 @@ import com.android.server.autofill.ui.AutoFillUI; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.infra.AbstractPerUserSystemService; import com.android.server.inputmethod.InputMethodManagerInternal; +import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.PrintWriter; @@ -192,6 +193,8 @@ final class AutofillManagerServiceImpl private final ContentCaptureManagerInternal mContentCaptureManagerInternal; + private final UserManagerInternal mUserManagerInternal; + private final DisabledInfoCache mDisabledInfoCache; AutofillManagerServiceImpl(AutofillManagerService master, Object lock, @@ -208,6 +211,7 @@ final class AutofillManagerServiceImpl mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); mContentCaptureManagerInternal = LocalServices.getService( ContentCaptureManagerInternal.class); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mDisabledInfoCache = disableCache; updateLocked(disabled); } @@ -379,6 +383,13 @@ final class AutofillManagerServiceImpl return 0; } + // TODO(b/376482880): remove this check once autofill service supports visible + // background users. + if (mUserManagerInternal.isVisibleBackgroundFullUser(mUserId)) { + Slog.d(TAG, "Currently, autofill service does not support visible background users."); + return 0; + } + if (!forAugmentedAutofillOnly && isAutofillDisabledLocked(clientActivity)) { // Standard autofill is enabled, but service disabled autofill for this activity; that // means no session, unless the activity is allowlisted for augmented autofill -- GitLab From d7fa89a02fd7f4c0ea28e26c519bc56de3716df2 Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Tue, 29 Oct 2024 15:23:09 -0500 Subject: [PATCH 034/652] Use simple equality to compare media notif intents Intent.filterEquals does not compare extra data included in intents, which can lead to false positives for some apps, resulting in stale intents being used Flag: com.android.systemui.media_controls_posts_optimization Bug: 369853463 Test: manual with affected apps Test: MediaDataProcessorTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:d52d5593647e945b172041df267fd53be2b2ab4f) Merged-In: Ib037ece86cdd887ab0bb56b54648db7b73b42fab Change-Id: Ib037ece86cdd887ab0bb56b54648db7b73b42fab --- .../domain/pipeline/MediaProcessingHelper.kt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt index 55d7b1d498e0..2bdee67dd57a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt @@ -42,7 +42,7 @@ fun isSameMediaData( context: Context, newController: MediaController, new: MediaData, - old: MediaData? + old: MediaData?, ): Boolean { if (old == null || !mediaControlsPostsOptimization()) return false @@ -71,7 +71,7 @@ fun isSameMediaData( /** Returns whether actions lists are equal. */ fun areCustomActionListsEqual( first: List?, - second: List? + second: List?, ): Boolean { // Same object, or both null if (first === second) { @@ -94,7 +94,7 @@ fun areCustomActionListsEqual( private fun areCustomActionsEqual( firstAction: PlaybackState.CustomAction, - secondAction: PlaybackState.CustomAction + secondAction: PlaybackState.CustomAction, ): Boolean { if ( firstAction.action != secondAction.action || @@ -139,7 +139,7 @@ private fun areActionsEqual( context: Context, newController: MediaController, new: MediaData, - old: MediaData + old: MediaData, ): Boolean { val oldState = MediaController(context, old.token!!).playbackState return if ( @@ -150,8 +150,7 @@ private fun areActionsEqual( var same = true new.actions.asSequence().zip(old.actions.asSequence()).forEach { if ( - it.first.actionIntent?.intent?.filterEquals(it.second.actionIntent?.intent) != - true || + it.first.actionIntent?.intent != it.second.actionIntent?.intent || it.first.icon != it.second.icon || it.first.contentDescription != it.second.contentDescription ) { @@ -164,7 +163,7 @@ private fun areActionsEqual( oldState?.actions == newController.playbackState?.actions && areCustomActionListsEqual( oldState?.customActions, - newController.playbackState?.customActions + newController.playbackState?.customActions, ) } else { false @@ -172,8 +171,5 @@ private fun areActionsEqual( } private fun areClickIntentsEqual(newIntent: PendingIntent?, oldIntent: PendingIntent?): Boolean { - if ((newIntent == null && oldIntent == null) || newIntent === oldIntent) return true - if (newIntent == null || oldIntent == null) return false - - return newIntent.intent?.filterEquals(oldIntent.intent) == true + return newIntent == oldIntent } -- GitLab From 0af3e3d44ba7207b99d74d610ab7ab4e4c0331bd Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 30 Oct 2024 14:20:03 +0000 Subject: [PATCH 035/652] Revert "AudioService: synchronize audio mode and focus for Telecom" This reverts commit 15f9e56e1f898bd34400db8231f2cda71ca4cc91. Bug: 375018522 Bug: 350415723 Flag: EXEMPT bug fix Test: make (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:5754baa4be5715b164d3a924e2cd0b327823ee74) Merged-In: Id8b8a7933976c2a98f42ae23775f1eee66462b92 Change-Id: Id8b8a7933976c2a98f42ae23775f1eee66462b92 --- .../server/audio/AudioDeviceBroker.java | 13 +- .../android/server/audio/AudioService.java | 130 ++++-------------- 2 files changed, 32 insertions(+), 111 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 2bbe08d22020..0fd22c583192 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1321,9 +1321,9 @@ public class AudioDeviceBroker { sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info); } - /*package*/ void postSetModeOwner(int mode, int pid, int uid, boolean signal) { - sendILMsgNoDelay(MSG_IL_SET_MODE_OWNER, SENDMSG_REPLACE, - signal ? 1 : 0, new AudioModeInfo(mode, pid, uid)); + /*package*/ void postSetModeOwner(int mode, int pid, int uid) { + sendLMsgNoDelay(MSG_I_SET_MODE_OWNER, SENDMSG_REPLACE, + new AudioModeInfo(mode, pid, uid)); } /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) { @@ -2025,7 +2025,7 @@ public class AudioDeviceBroker { mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); } break; - case MSG_IL_SET_MODE_OWNER: + case MSG_I_SET_MODE_OWNER: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { int btScoRequesterUid = bluetoothScoRequestOwnerUid(); @@ -2036,9 +2036,6 @@ public class AudioDeviceBroker { } } } - if (msg.arg1 == 1 /*signal*/) { - mAudioService.decrementAudioModeResetCount(); - } break; case MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT: @@ -2227,7 +2224,7 @@ public class AudioDeviceBroker { private static final int MSG_REPORT_NEW_ROUTES = 13; private static final int MSG_II_SET_HEARING_AID_VOLUME = 14; private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15; - private static final int MSG_IL_SET_MODE_OWNER = 16; + private static final int MSG_I_SET_MODE_OWNER = 16; private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22; private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c37d47149ef5..e1909d91a77d 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1918,7 +1918,7 @@ public class AudioService extends IAudioService.Stub // Restore call state synchronized (mDeviceBroker.mSetModeLock) { onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(), - mContext.getPackageName(), true /*force*/, false /*signal*/); + mContext.getPackageName(), true /*force*/); } final int forSys; synchronized (mSettingsLock) { @@ -4746,47 +4746,14 @@ public class AudioService extends IAudioService.Stub } } if (updateAudioMode) { - postUpdateAudioMode(existingMsgPolicy, AudioSystem.MODE_CURRENT, - android.os.Process.myPid(), mContext.getPackageName(), - false /*signal*/, delay); - } - } - } - - static class UpdateAudioModeInfo { - UpdateAudioModeInfo(int mode, int pid, String packageName, boolean signal) { - mMode = mode; - mPid = pid; - mPackageName = packageName; - mSignal = signal; - } - private final int mMode; - private final int mPid; - private final String mPackageName; - private final boolean mSignal; - - int getMode() { - return mMode; - } - int getPid() { - return mPid; - } - String getPackageName() { - return mPackageName; - } - boolean getSignal() { - return mSignal; - } - } - - void postUpdateAudioMode(int msgPolicy, int mode, int pid, String packageName, - boolean signal, int delay) { - synchronized (mAudioModeResetLock) { - if (signal) { - mAudioModeResetCount++; + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + existingMsgPolicy, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + delay); } - sendMsg(mAudioHandler, MSG_UPDATE_AUDIO_MODE, msgPolicy, 0, 0, - new UpdateAudioModeInfo(mode, pid, packageName, signal), delay); } } @@ -6188,9 +6155,13 @@ public class AudioService extends IAudioService.Stub } else { SetModeDeathHandler h = mSetModeDeathHandlers.get(index); mSetModeDeathHandlers.remove(index); - postUpdateAudioMode(SENDMSG_QUEUE, AudioSystem.MODE_CURRENT, - android.os.Process.myPid(), mContext.getPackageName(), - false /*signal*/, 0); + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + SENDMSG_QUEUE, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + 0); } } } @@ -6436,14 +6407,19 @@ public class AudioService extends IAudioService.Stub } } - postUpdateAudioMode(SENDMSG_REPLACE, mode, pid, callingPackage, - hasModifyPhoneStatePermission && mode == AudioSystem.MODE_NORMAL, 0); + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + SENDMSG_REPLACE, + mode, + pid, + callingPackage, + 0); } } @GuardedBy("mDeviceBroker.mSetModeLock") void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage, - boolean force, boolean signal) { + boolean force) { if (requestedMode == AudioSystem.MODE_CURRENT) { requestedMode = getMode(); } @@ -6458,7 +6434,7 @@ public class AudioService extends IAudioService.Stub } if (DEBUG_MODE) { Log.v(TAG, "onUpdateAudioMode() new mode: " + mode + ", current mode: " - + mMode.get() + " requested mode: " + requestedMode + " signal: " + signal); + + mMode.get() + " requested mode: " + requestedMode); } if (mode != mMode.get() || force) { int status = AudioSystem.SUCCESS; @@ -6504,7 +6480,7 @@ public class AudioService extends IAudioService.Stub // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO // connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwner(mode, pid, uid, signal); + mDeviceBroker.postSetModeOwner(mode, pid, uid); } else { Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode); } @@ -10186,7 +10162,7 @@ public class AudioService extends IAudioService.Stub h.setRecordingActive(isRecordingActiveForUid(h.getUid())); if (wasActive != h.isActive()) { onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(), - mContext.getPackageName(), false /*force*/, false /*signal*/); + mContext.getPackageName(), false /*force*/); } } break; @@ -10216,9 +10192,7 @@ public class AudioService extends IAudioService.Stub case MSG_UPDATE_AUDIO_MODE: synchronized (mDeviceBroker.mSetModeLock) { - UpdateAudioModeInfo info = (UpdateAudioModeInfo) msg.obj; - onUpdateAudioMode(info.getMode(), info.getPid(), info.getPackageName(), - false /*force*/, info.getSignal()); + onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj, false /*force*/); } break; @@ -10921,59 +10895,9 @@ public class AudioService extends IAudioService.Stub return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } mmi.record(); - //delay abandon focus requests from Telecom if an audio mode reset from Telecom - // is still being processed - final boolean abandonFromTelecom = (mContext.checkCallingOrSelfPermission( - MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) - && ((aa != null && aa.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION) - || AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)); - if (abandonFromTelecom) { - synchronized (mAudioModeResetLock) { - final long start = java.lang.System.currentTimeMillis(); - long elapsed = 0; - while (mAudioModeResetCount > 0) { - if (DEBUG_MODE) { - Log.i(TAG, "Abandon focus from Telecom, waiting for mode change"); - } - try { - mAudioModeResetLock.wait( - AUDIO_MODE_RESET_TIMEOUT_MS - elapsed); - } catch (InterruptedException e) { - Log.w(TAG, "Interrupted while waiting for audio mode reset"); - } - elapsed = java.lang.System.currentTimeMillis() - start; - if (elapsed >= AUDIO_MODE_RESET_TIMEOUT_MS) { - Log.e(TAG, "Timeout waiting for audio mode reset"); - break; - } - } - if (DEBUG_MODE && elapsed != 0) { - Log.i(TAG, "Abandon focus from Telecom done waiting"); - } - } - } return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName); } - /** synchronization between setMode(NORMAL) and abandonAudioFocus() frmo Telecom */ - private static final long AUDIO_MODE_RESET_TIMEOUT_MS = 3000; - - private final Object mAudioModeResetLock = new Object(); - - @GuardedBy("mAudioModeResetLock") - private int mAudioModeResetCount = 0; - - void decrementAudioModeResetCount() { - synchronized (mAudioModeResetLock) { - if (mAudioModeResetCount > 0) { - mAudioModeResetCount--; - } else { - Log.w(TAG, "mAudioModeResetCount already 0"); - } - mAudioModeResetLock.notify(); - } - } - /** see {@link AudioManager#abandonAudioFocusForTest(AudioFocusRequest, String)} */ public int abandonAudioFocusForTest(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa, String callingPackageName) { -- GitLab From 73d475bc1bc3c3cc497f4fd2043a1eb441f0d1e9 Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Thu, 17 Oct 2024 16:49:43 +0000 Subject: [PATCH 036/652] Do not handle touches next to shelf Take #2 NSSL would return true for all touches below the last notification, and to the right of the shelf. This is essentially empty space. The reason for this is the SwipeHelper is always returning true, since it is looking at the full width of the NotificationShelf. By using getActualWidth() instead, the touches are more accurately handled. This fixes an issue where the shelf is vertically overlapping the unlock icon, and taking touches from it. Fixes: 358424256 Test: atest NotificationSwipeHelperTest Test: manual - horizontal swipe notifications Test: manual - vertically swipe notifications Test: manual - all touch interactions with notifs Test: manual - tap on shelf to expand shade Test: manual - face auth without bypass, add notifications next to unlock icon, use unlock Flag: com.android.systemui.ignore_touches_next_to_notification_shelf (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bff27edb33096142a76fed89dbf0cc4f91df969c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4ed24bd6ad4933eba9e4e361bbfde04afa42c211) Merged-In: Idf22d6e72e7d618ed98e869142b5cc5dc785985e Change-Id: Idf22d6e72e7d618ed98e869142b5cc5dc785985e --- packages/SystemUI/aconfig/systemui.aconfig | 10 ++++ ...tificationStackScrollLayoutController.java | 11 ++++ .../stack/NotificationSwipeHelper.java | 12 ++++- .../stack/NotificationSwipeHelperTest.java | 54 +++++++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index f8383d94b1ab..554ad8ba64e6 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1431,4 +1431,14 @@ flag { metadata { purpose: PURPOSE_BUGFIX } +} + +flag { + name: "ignore_touches_next_to_notification_shelf" + namespace: "systemui" + description: "The shelf can vertically overlap the unlock icon. Ignore touches if so." + bug: "358424256" + metadata { + purpose: PURPOSE_BUGFIX + } } \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index dad6894a43ce..ae71ca443236 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -24,6 +24,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ import static com.android.server.notification.Flags.screenshareNotificationHiding; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Flags.confineNotificationTouchToViewWidth; +import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener; @@ -604,6 +605,16 @@ public class NotificationStackScrollLayoutController implements Dumpable { true /* requireMinHeight */, false /* ignoreDecors */, !confineNotificationTouchToViewWidth() /* ignoreWidth */); + + // Verify the MotionEvent x,y are actually inside the touch area of the shelf, + // since the shelf may be animated down to a collapsed size on keyguard. + if (ignoreTouchesNextToNotificationShelf()) { + if (child instanceof NotificationShelf shelf) { + if (!NotificationSwipeHelper.isTouchInView(ev, shelf)) { + return null; + } + } + } if (child instanceof ExpandableNotificationRow row) { ExpandableNotificationRow parent = row.getNotificationParent(); if (parent != null && parent.areChildrenExpanded() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index 7e327e66982c..0e94ca351835 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; +import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -39,6 +40,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -503,13 +505,21 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc final int height = (view instanceof ExpandableView) ? ((ExpandableView) view).getActualHeight() : view.getHeight(); + final int width; + if (ignoreTouchesNextToNotificationShelf()) { + width = (view instanceof NotificationShelf) + ? ((NotificationShelf) view).getActualWidth() + : view.getWidth(); + } else { + width = view.getWidth(); + } final int rx = (int) ev.getRawX(); final int ry = (int) ev.getRawY(); int[] temp = new int[2]; view.getLocationOnScreen(temp); final int x = temp[0]; final int y = temp[1]; - Rect rect = new Rect(x, y, x + view.getWidth(), y + height); + Rect rect = new Rect(x, y, x + width, y + height); boolean ret = rect.contains(rx, ry); return ret; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 95db95cd288b..789701f5e4b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -13,6 +13,8 @@ package com.android.systemui.statusbar.notification.stack; +import static com.android.systemui.Flags.FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -36,6 +38,7 @@ import static org.mockito.Mockito.when; import android.animation.Animator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.os.Handler; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.service.notification.StatusBarNotification; import android.testing.TestableLooper; @@ -52,6 +55,7 @@ import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; @@ -85,6 +89,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { private NotificationMenuRowPlugin mMenuRow; private Handler mHandler; private ExpandableNotificationRow mNotificationRow; + private NotificationShelf mShelf; private Runnable mFalsingCheck; private final FeatureFlags mFeatureFlags = new FakeFeatureFlags(); @@ -111,6 +116,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { mEvent = mock(MotionEvent.class); mMenuRow = mock(NotificationMenuRowPlugin.class); mNotificationRow = mock(ExpandableNotificationRow.class); + mShelf = mock(NotificationShelf.class); mHandler = mock(Handler.class); mFalsingCheck = mock(Runnable.class); } @@ -664,6 +670,54 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { mSwipeHelper.isTouchInView(mEvent, mNotificationRow)); } + @Test + @EnableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF) + public void testIsTouchInView_notificationShelf_flagEnabled() { + doReturn(500).when(mShelf).getWidth(); + doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight(); + + Answer answer = (Answer) invocation -> { + int[] arr = invocation.getArgument(0); + arr[0] = 0; + arr[1] = 0; + return null; + }; + + doReturn(5f).when(mEvent).getRawX(); + doReturn(10f).when(mEvent).getRawY(); + doAnswer(answer).when(mShelf).getLocationOnScreen(any()); + assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + + doReturn(50f).when(mEvent).getRawX(); + assertFalse("Touch is not within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + } + + @Test + @DisableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF) + public void testIsTouchInView_notificationShelf_flagDisabled() { + doReturn(500).when(mShelf).getWidth(); + doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight(); + doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight(); + + Answer answer = (Answer) invocation -> { + int[] arr = invocation.getArgument(0); + arr[0] = 0; + arr[1] = 0; + return null; + }; + + doReturn(5f).when(mEvent).getRawX(); + doReturn(10f).when(mEvent).getRawY(); + doAnswer(answer).when(mShelf).getLocationOnScreen(any()); + assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + + doReturn(50f).when(mEvent).getRawX(); + assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf)); + } + @Test public void testContentAlphaRemainsUnchangedWhenNotificationIsNotDismissible() { doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth(); -- GitLab From 79a8c7f000793ed78eb95a517bd01d197c57f516 Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Mon, 4 Nov 2024 12:34:01 -0500 Subject: [PATCH 037/652] Move WMComponent to wm shell This change moves WMComponent into the wm shell library and introduces HasWMComponent interface to allow classes to do member injection where constructor injection is not possible. Flag: EXEMPT refactor Bug: 378075955 Test: m sysuig Change-Id: Ifb0eb7be39eb663b4af56fe73e9783299984ef83 --- .../android/wm/shell/dagger/HasWMComponent.kt | 27 +++++++++++++++++++ .../android/wm/shell}/dagger/WMComponent.java | 12 +++------ .../android/systemui/SystemUIApplication.java | 10 ++++++- .../android/systemui/SystemUIInitializer.java | 2 +- .../systemui/dagger/GlobalRootComponent.java | 1 + .../com/android/systemui/wmshell/WMShell.java | 2 +- 6 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt rename {packages/SystemUI/src/com/android/systemui => libs/WindowManager/Shell/src/com/android/wm/shell}/dagger/WMComponent.java (86%) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt new file mode 100644 index 000000000000..d5e0240ea9ad --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/HasWMComponent.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.wm.shell.dagger + +/** + * An interface implemented by the application that uses [WMComponent]. + * + * This exposes the component to allow classes to do member injection for bindings where constructor + * injection is not possible, e.g. views. + */ +interface HasWMComponent { + fun getWMComponent(): WMComponent +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java similarity index 86% rename from packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java index a3cdb2eff601..c493aadd57b0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java @@ -14,17 +14,14 @@ * limitations under the License. */ -package com.android.systemui.dagger; +package com.android.wm.shell.dagger; import android.os.HandlerThread; import androidx.annotation.Nullable; -import com.android.systemui.SystemUIInitializer; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; -import com.android.wm.shell.dagger.WMShellModule; -import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.keyguard.KeyguardTransitions; @@ -45,12 +42,11 @@ import java.util.Optional; /** * Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported - * from the WM component into the SysUI component (in - * {@link SystemUIInitializer#init(boolean)}), and references the specific dependencies + * from the WM component into the SysUI component, and references the specific dependencies * provided by its particular device/form-factor SystemUI implementation. * - * ie. {@link WMComponent} includes {@link WMShellModule} - * and {@code TvWMComponent} includes {@link com.android.wm.shell.dagger.TvWMShellModule} + *

ie. {@link WMComponent} includes {@link WMShellModule} and {@code TvWMComponent} includes + * {@link TvWMShellModule} */ @WMSingleton @Subcomponent(modules = {WMShellModule.class}) diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 811b47d57c1d..322580e66763 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -45,6 +45,8 @@ import com.android.systemui.process.ProcessWrapper; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.util.NotificationChannels; +import com.android.wm.shell.dagger.HasWMComponent; +import com.android.wm.shell.dagger.WMComponent; import java.lang.reflect.InvocationTargetException; import java.util.ArrayDeque; @@ -61,7 +63,7 @@ import javax.inject.Provider; * Application class for SystemUI. */ public class SystemUIApplication extends Application implements - SystemUIAppComponentFactoryBase.ContextInitializer { + SystemUIAppComponentFactoryBase.ContextInitializer, HasWMComponent { public static final String TAG = "SystemUIService"; private static final boolean DEBUG = false; @@ -486,4 +488,10 @@ public class SystemUIApplication extends Application implements n.addExtras(extras); } + + @NonNull + @Override + public WMComponent getWMComponent() { + return mInitializer.getWMComponent(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java index 5c75a49818a6..f530522fb707 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java @@ -24,9 +24,9 @@ import android.util.Log; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; -import com.android.systemui.dagger.WMComponent; import com.android.systemui.res.R; import com.android.systemui.util.InitializationChecker; +import com.android.wm.shell.dagger.WMComponent; import com.android.wm.shell.dagger.WMShellConcurrencyModule; import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.shared.ShellTransitions; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java index e78ce3bbb0d1..f804c2e80ac6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java @@ -24,6 +24,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.SystemPropertiesHelper; import com.android.systemui.process.ProcessWrapper; import com.android.systemui.util.InitializationChecker; +import com.android.wm.shell.dagger.WMComponent; import dagger.BindsInstance; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 8039e00159f0..75881fac380c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -48,7 +48,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.CoreStartable; import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -60,6 +59,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.wm.shell.dagger.WMComponent; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.onehanded.OneHanded; -- GitLab From bfae72fd0458e84f9e411f4d513419b5b3ed2dca Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Wed, 20 Nov 2024 08:18:05 -0700 Subject: [PATCH 038/652] Add additional documentation to PictureProfileHandle Bug: 337330263 Bug: 379941266 Test: build Flag: DOCS_ONLY Change-Id: Icce2c19a0bbc442fb5c12bc308b729b9a713176f --- .../media/quality/PictureProfileHandle.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/media/java/android/media/quality/PictureProfileHandle.java b/media/java/android/media/quality/PictureProfileHandle.java index 714fd36d664a..d9d21932d09a 100644 --- a/media/java/android/media/quality/PictureProfileHandle.java +++ b/media/java/android/media/quality/PictureProfileHandle.java @@ -28,11 +28,14 @@ import android.os.Parcelable; * A picture profile represents a collection of parameters used to configure picture processing * to enhance the quality of graphic buffers. * + * @see PictureProfile.getHandle + * * @hide */ @SystemApi @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) public final class PictureProfileHandle implements Parcelable { + /** A handle that represents no picture processing configuration. */ public static final @NonNull PictureProfileHandle NONE = new PictureProfileHandle(0); private final long mId; @@ -42,7 +45,16 @@ public final class PictureProfileHandle implements Parcelable { mId = id; } - /** @hide */ + /** + * An ID that uniquely identifies the picture profile across the system. + * + * This ID can be used to construct an NDK PictureProfileHandle to be fed directly into + * IGraphicBufferProducer to couple a picture profile to a graphic buffer. + * + * Note: These IDs are generated randomly and are not stable across reboots. + * + * @hide + */ @SystemApi @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) public long getId() { -- GitLab From aca4ecdccdeb476b48658eed09324f37da371a6b Mon Sep 17 00:00:00 2001 From: Austin Delgado Date: Wed, 20 Nov 2024 11:52:10 -0800 Subject: [PATCH 039/652] Move keyguard lock callback listener to AuthController Test: AuthControllerTest AuthContainerViewTest Bug: 377614813 Flag: EXEMPT bugfix Change-Id: I27b00006bfe1877a44ad59d65094b3f5300a5186 --- .../biometrics/AuthControllerTest.java | 22 ++++++++++++++++++- .../biometrics/AuthContainerView.java | 11 ---------- .../systemui/biometrics/AuthController.java | 11 ++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java index 2dcbdc80f695..bb8d2d7ba8fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -43,6 +43,7 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.ActivityTaskManager; +import android.app.KeyguardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -181,6 +182,8 @@ public class AuthControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor mFaceAuthenticatorsRegisteredCaptor; @Captor + private ArgumentCaptor mKeyguardLockedStateCaptor; + @Captor private ArgumentCaptor mBiometricStateCaptor; @Captor private ArgumentCaptor mModalityCaptor; @@ -191,6 +194,8 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private VibratorHelper mVibratorHelper; @Mock + private KeyguardManager mKeyguardManager; + @Mock private MSDLPlayer mMSDLPlayer; private TestableContext mContextSpy; @@ -271,6 +276,9 @@ public class AuthControllerTest extends SysuiTestCase { mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps); mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps); + verify(mKeyguardManager).addKeyguardLockedStateListener(any(), + mKeyguardLockedStateCaptor.capture()); + // Ensures that the operations posted on the handler get executed. waitForIdleSync(); } @@ -975,6 +983,18 @@ public class AuthControllerTest extends SysuiTestCase { eq(null) /* credentialAttestation */); } + @Test + public void testCloseDialog_whenDeviceLocks() throws Exception { + showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); + + mKeyguardLockedStateCaptor.getValue().onKeyguardLockedStateChanged( + true /* isKeyguardLocked */); + + verify(mReceiver).onDialogDismissed( + eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), + eq(null) /* credentialAttestation */); + } + @Test public void testShowDialog_whenOwnerNotInForeground() { PromptInfo promptInfo = createTestPromptInfo(); @@ -1191,7 +1211,7 @@ public class AuthControllerTest extends SysuiTestCase { mWakefulnessLifecycle, mUserManager, mLockPatternUtils, () -> mUdfpsLogger, () -> mLogContextInteractor, () -> mPromptSelectionInteractor, () -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor, - mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper, + mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper, mKeyguardManager, mLazyViewCapture, mMSDLPlayer); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b491c94db151..12b5fc01845c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -27,7 +27,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlertDialog; -import android.app.KeyguardManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -316,16 +315,6 @@ public class AuthContainerView extends LinearLayout mBiometricCallback = new BiometricCallback(); mMSDLPlayer = msdlPlayer; - // Listener for when device locks from adaptive auth, dismiss prompt - getContext().getSystemService(KeyguardManager.class).addKeyguardLockedStateListener( - getContext().getMainExecutor(), - isKeyguardLocked -> { - if (isKeyguardLocked) { - onStartedGoingToSleep(); - } - } - ); - final BiometricModalities biometricModalities = new BiometricModalities( Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds), Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds)); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index a5bd559dcbf2..d1fae4866941 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -26,6 +26,7 @@ import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityTaskManager; +import android.app.KeyguardManager; import android.app.TaskStackListener; import android.content.BroadcastReceiver; import android.content.Context; @@ -732,6 +733,7 @@ public class AuthController implements @Background DelayableExecutor bgExecutor, @NonNull UdfpsUtils udfpsUtils, @NonNull VibratorHelper vibratorHelper, + @NonNull KeyguardManager keyguardManager, Lazy daggerLazyViewCapture, @NonNull MSDLPlayer msdlPlayer) { mContext = context; @@ -762,6 +764,15 @@ public class AuthController implements mPromptViewModelProvider = promptViewModelProvider; mCredentialViewModelProvider = credentialViewModelProvider; + keyguardManager.addKeyguardLockedStateListener( + context.getMainExecutor(), + isKeyguardLocked -> { + if (isKeyguardLocked) { + closeDialog("Device lock"); + } + } + ); + mOrientationListener = new BiometricDisplayListener( context, mDisplayManager, -- GitLab From 152db50e94e43f355b58833546230a4db00cc5ad Mon Sep 17 00:00:00 2001 From: Miranda Kephart Date: Thu, 21 Nov 2024 13:16:15 -0500 Subject: [PATCH 040/652] Inset screenshot UI from the nav bar In seascape, the screenshot UI overlaps the nav bar (especially visible in 3-button mode). This change adds the nav bar insets to the screenshot padding. Bug: 380280383 Fix: 380280383 Test: manual (visual change) Flag: EXEMPT bugfix Change-Id: Ib2a683040bc9b1ee43831e89abf379163774bdb4 --- .../systemui/screenshot/ui/ScreenshotShelfView.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt index b8ea8f9052ca..c58b48122f63 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt @@ -147,7 +147,12 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) : ) if (cutout == null) { - screenshotStatic.setPadding(0, 0, 0, navBarInsets.bottom) + screenshotStatic.setPadding( + navBarInsets.left, + navBarInsets.top, + navBarInsets.right, + navBarInsets.bottom, + ) } else { val waterfall = cutout.waterfallInsets if (inPortrait) { @@ -164,9 +169,9 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) : ) } else { screenshotStatic.setPadding( - max(cutout.safeInsetLeft, waterfall.left), + max(cutout.safeInsetLeft, waterfall.left, navBarInsets.left), waterfall.top, - max(cutout.safeInsetRight, waterfall.right), + max(cutout.safeInsetRight, waterfall.right, navBarInsets.right), max( navBarInsets.bottom + verticalPadding, waterfall.bottom + verticalPadding, -- GitLab From 0c3d451978771ed54c16a34611936c9e6c4710b6 Mon Sep 17 00:00:00 2001 From: Aishwarya Mallampati Date: Thu, 21 Nov 2024 22:19:31 +0000 Subject: [PATCH 041/652] Add RIL constants related to satellite APIs. The following APIs are added to IRadioNetwork.aidl: - setSatellitePlmn - setSatelliteEnabledForCarrier - isSatelliteEnabledForCarrier Bug: 380319841 Test: Manually tested SMS/MMS/CALLS/DATA FLAG: EXEMPT HAL interface change Change-Id: I4acf9b63c7ffe38fb7f953fcc93dbbd165aa2291 --- .../java/com/android/internal/telephony/RILConstants.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index b7dd4df1c843..461d1579ea45 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -549,6 +549,9 @@ public interface RILConstants { int RIL_REQUEST_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED = 248; int RIL_REQUEST_IS_SECURITY_ALGORITHMS_UPDATED_ENABLED = 249; int RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT = 250; + int RIL_REQUEST_SET_SATELLITE_PLMN = 251; + int RIL_REQUEST_SET_SATELLITE_ENABLED_FOR_CARRIER = 252; + int RIL_REQUEST_IS_SATELLITE_ENABLED_FOR_CARRIER = 253; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; -- GitLab From 7690586c0df43f1055923f4dec1320df03287745 Mon Sep 17 00:00:00 2001 From: Cam Bickel Date: Fri, 22 Nov 2024 17:55:13 +0000 Subject: [PATCH 042/652] Cleanup flag "remove_on_window_infos_changed_..." Full flag name: "remove_on_window_infos_changed_handler" Bug: b/333834990 Test: presubmit Flag: EXEMPT flag cleanup Change-Id: I24ab0c538dac2101e0e9ca36884af6bb7504ad73 --- .../android/server/wm/AccessibilityWindowsPopulator.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index fd2a909f8b05..20493d312873 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -150,11 +150,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { @Override public void onWindowInfosChanged(InputWindowHandle[] windowHandles, DisplayInfo[] displayInfos) { - if (com.android.server.accessibility.Flags.removeOnWindowInfosChangedHandler()) { - onWindowInfosChangedInternal(windowHandles, displayInfos); - } else { - mHandler.post(() -> onWindowInfosChangedInternal(windowHandles, displayInfos)); - } + onWindowInfosChangedInternal(windowHandles, displayInfos); } private void onWindowInfosChangedInternal(InputWindowHandle[] windowHandles, -- GitLab From b40e8fecc22773142184398f3c83f1a901d8124a Mon Sep 17 00:00:00 2001 From: Sally Qi Date: Fri, 22 Nov 2024 21:18:21 +0000 Subject: [PATCH 043/652] [Lut NDK] Add static_assert to ensure that ADISPLAYLUTS_SAMPLINGKEY_CIE_Y is the same as android::gui::LutProperties::SamplingKey::CIE_Y. Bug: 358422255 Change-Id: I31e82bf0b257b6dee6740b53787645589db8c066 Test: builds Flag: EXEMPT NDK --- native/android/display_luts.cpp | 9 +++++---- native/android/surface_control.cpp | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp index 179a32bd1c03..b03a718d4a65 100644 --- a/native/android/display_luts.cpp +++ b/native/android/display_luts.cpp @@ -26,8 +26,9 @@ #define CHECK_NOT_NULL(name) \ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument"); -ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, int32_t dimension, - int32_t key) { +ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, + ADisplayLuts_Dimension dimension, + ADisplayLuts_SamplingKey key) { CHECK_NOT_NULL(buffer); LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT, "the lut raw buffer length is too big to handle"); @@ -64,7 +65,7 @@ void ADisplayLutsEntry_destroy(ADisplayLutsEntry* entry) { ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) { CHECK_NOT_NULL(entry); - return static_cast(entry->properties.dimension); + return entry->properties.dimension; } int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) { @@ -74,7 +75,7 @@ int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) { ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) { CHECK_NOT_NULL(entry); - return static_cast(entry->properties.samplingKey); + return entry->properties.samplingKey; } const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) { diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index fc64e9b48f6d..acd3ede358c3 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -64,6 +64,8 @@ static_assert(static_cast(ADISPLAYLUTS_SAMPLINGKEY_RGB) == static_cast(android::gui::LutProperties::SamplingKey::RGB)); static_assert(static_cast(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) == static_cast(android::gui::LutProperties::SamplingKey::MAX_RGB)); +static_assert(static_cast(ADISPLAYLUTS_SAMPLINGKEY_CIE_Y) == + static_cast(android::gui::LutProperties::SamplingKey::CIE_Y)); Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) { return reinterpret_cast(aSurfaceTransaction); -- GitLab From 507336fe487a8beb0360f74aa0ef526cf326b62a Mon Sep 17 00:00:00 2001 From: lin_yiwen Date: Thu, 19 Sep 2024 11:38:33 +0800 Subject: [PATCH 044/652] Fix cts testMinimalSizeDocked fail when device supports multiwindow feature. Bug: b/367878178 Test: run cts -m CtsWindowManagerDeviceTestCases -t android.server.wm.ManifestLayoutTests#testMinimalSizeDocked Change-Id: Id3f7f9784e2ac2d24a0dec8dfb1a49659edab411 --- .../com/android/server/pm/PackageManagerService.java | 10 ++++++++++ services/java/com/android/server/SystemServer.java | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 69c78eb155eb..34a15fdb2df5 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3019,6 +3019,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService mDexOptHelper.performPackageDexOptUpgradeIfNeeded(); } + public void updateMetricsIfNeeded() { + final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + if (displayManager != null) { + final Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); + if (display != null) { + display.getMetrics(mMetrics); + } + } + } + private void notifyPackageUseInternal(String packageName, int reason) { long time = System.currentTimeMillis(); synchronized (mLock) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cdca948f91ac..4f0234135e27 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1842,6 +1842,10 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("UpdateMetricsIfNeeded"); + mPackageManagerService.updateMetricsIfNeeded(); + t.traceEnd(); + t.traceBegin("PerformFstrimIfNeeded"); try { mPackageManagerService.performFstrimIfNeeded(); -- GitLab From c71850d297ea21a4f9ddfc7fb3ac3be434f6f557 Mon Sep 17 00:00:00 2001 From: Taran Singh Date: Mon, 25 Nov 2024 19:27:04 +0000 Subject: [PATCH 045/652] Writing tools: selectively enable writing tools in TextView 5/n Selectively enabled writing tools on TexView attributes that have inputType of TYPE_CLASS_TEXT and variations that are text and can use writing tools. This criteria is same as what we use for suggestions in isSuggestionsEnabled(). Bug: 350047836 Flag: android.view.inputmethod.adaptive_handwriting_bounds Test: atest CtsInputMethodTestCases TextViewTest Change-Id: I66b2fbdd947f2e927d98e04e71f70eff796610de --- core/java/android/widget/TextView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index cb70466fcd81..912ab664ff70 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10120,6 +10120,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener outAttrs.extras.putBoolean( STYLUS_HANDWRITING_ENABLED_ANDROIDX_EXTRAS_KEY, handwritingEnabled); } + if (android.view.inputmethod.Flags.writingTools()) { + // default to same behavior as isSuggestionsEnabled(). + outAttrs.setWritingToolsEnabled(isSuggestionsEnabled()); + } ArrayList> gestures = new ArrayList<>(); gestures.add(SelectGesture.class); gestures.add(SelectRangeGesture.class); -- GitLab From d61fc450528c6b2cd9ba0060a3e88a637c35cf79 Mon Sep 17 00:00:00 2001 From: Brandon Edens Date: Tue, 19 Nov 2024 16:12:02 +0000 Subject: [PATCH 046/652] dock_observer: allow dock rotation in OOBE Add a resource for configuration on whether or not we allow dock rotation for the OOBE SetupWizard. Bug: 374940810 Test: Enable the configuration and test OOBE + Docking for rotation. Flag: EXEMPT bugfix Change-Id: Ifba159ee2fdcf5f0b2875a84c4b833c7d454623b --- core/res/res/values/config.xml | 5 +++++ core/res/res/values/symbols.xml | 1 + .../java/com/android/server/DockObserver.java | 18 ++++++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7799ff951997..eae8d5542436 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -815,6 +815,11 @@ we rely on gravity to determine the effective orientation. --> true + + false + Got it - + Widgets - - To add Widgets on the lock screen as a shortcut, make sure it is enabled in settings. + + To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings. + + Settings diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt index 8906156252a6..71f29c0062bc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt @@ -17,6 +17,8 @@ package com.android.systemui.keyguard.data.quickaffordance import android.content.Context +import android.content.Intent +import android.provider.Settings import android.util.Log import com.android.systemui.Flags.glanceableHubShortcutButton import com.android.systemui.animation.Expandable @@ -86,7 +88,13 @@ constructor( } else if (!communalInteractor.isCommunalEnabled.value) { Log.i(TAG, "Button disabled in picker: hub not enabled in settings.") KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - context.getString(R.string.glanceable_hub_lockscreen_affordance_disabled_text) + explanation = + context.getString(R.string.glanceable_hub_lockscreen_affordance_disabled_text), + actionText = + context.getString( + R.string.glanceable_hub_lockscreen_affordance_action_button_label + ), + actionIntent = Intent(Settings.ACTION_LOCKSCREEN_SETTINGS), ) } else { KeyguardQuickAffordanceConfig.PickerScreenState.Default() -- GitLab From 1aaeb38e25443572d48505599494f27cc142f242 Mon Sep 17 00:00:00 2001 From: John Johnson Date: Thu, 21 Nov 2024 16:28:16 -0500 Subject: [PATCH 051/652] Launch card intent from qs tile when flag is enabled Bug: 378469025 Test: QuickAccessWalletTileTest Flag: android.service.quickaccesswallet.launch_selected_card_from_qs_tile Change-Id: I7d66d7b5144d6473136806c60c2dcf3a23ff7761 --- .../qs/tiles/QuickAccessWalletTileTest.java | 33 +++++++++++++++++++ .../qs/tiles/QuickAccessWalletTile.java | 12 +++++-- .../QuickAccessWalletController.java | 18 ++++++++++ .../com/android/systemui/SysuiTestCase.java | 1 + 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 03c1f92aad4c..4068d9fd7f3d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -46,6 +46,9 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Handler; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.service.quickaccesswallet.Flags; import android.service.quickaccesswallet.GetWalletCardsError; import android.service.quickaccesswallet.GetWalletCardsResponse; import android.service.quickaccesswallet.QuickAccessWalletClient; @@ -221,6 +224,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + @DisableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE}) public void testHandleClick_startQuickAccessUiIntent_noCard() { setUpWalletCard(/* hasCard= */ false); @@ -234,6 +238,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + @DisableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE}) public void testHandleClick_startQuickAccessUiIntent_hasCard() { setUpWalletCard(/* hasCard= */ true); @@ -246,6 +251,34 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { /* hasCard= */ eq(true)); } + @Test + @EnableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE}) + public void testHandleClick_startCardIntent_noCard() { + setUpWalletCard(/* hasCard= */ false); + + mTile.handleClick(/* view= */ null); + mTestableLooper.processAllMessages(); + + verify(mController).startQuickAccessUiIntent( + eq(mActivityStarter), + eq(null), + /* hasCard= */ eq(false)); + } + + @Test + @EnableFlags({Flags.FLAG_LAUNCH_SELECTED_CARD_FROM_QS_TILE}) + public void testHandleClick_startCardIntent_hasCard() { + setUpWalletCard(/* hasCard= */ true); + + mTile.handleClick(null /* view */); + mTestableLooper.processAllMessages(); + + verify(mController).startWalletCardPendingIntent( + any(), + eq(mActivityStarter), + eq(null)); + } + @Test public void testHandleUpdateState_updateLabelAndIcon() { QSTile.State state = new QSTile.State(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 37d24debe958..78c435c45c72 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -142,8 +142,16 @@ public class QuickAccessWalletTile extends QSTileImpl { InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE); mUiHandler.post( - () -> mController.startQuickAccessUiIntent( - mActivityStarter, animationController, mSelectedCard != null)); + () -> { + if (android.service.quickaccesswallet.Flags.launchSelectedCardFromQsTile() + && mSelectedCard != null) { + mController.startWalletCardPendingIntent( + mSelectedCard, mActivityStarter, animationController); + } else { + mController.startQuickAccessUiIntent( + mActivityStarter, animationController, mSelectedCard != null); + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java index 1d32a4fd69d6..389b6fb4b0ef 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -32,6 +32,7 @@ import android.provider.Settings; import android.service.quickaccesswallet.GetWalletCardsRequest; import android.service.quickaccesswallet.QuickAccessWalletClient; import android.service.quickaccesswallet.QuickAccessWalletClientImpl; +import android.service.quickaccesswallet.WalletCard; import android.util.Log; import com.android.systemui.animation.ActivityTransitionAnimator; @@ -268,6 +269,23 @@ public class QuickAccessWalletController { }); } + /** + * Starts the {@link android.app.PendingIntent} for a {@link WalletCard}. + * + * This should be used to open a selected card from the QuickAccessWallet UI or + * the settings tile. + * + * @param activityStarter an {@link ActivityStarter} to launch the Intent or PendingIntent. + * @param animationController an {@link ActivityTransitionAnimator.Controller} to provide a + * smooth animation for the activity launch. + */ + public void startWalletCardPendingIntent(WalletCard card, + ActivityStarter activityStarter, + ActivityTransitionAnimator.Controller animationController) { + activityStarter.postStartActivityDismissingKeyguard( + card.getPendingIntent(), animationController); + } + private Intent getSysUiWalletIntent() { return new Intent(mContext, WalletActivity.class) .setAction(Intent.ACTION_VIEW); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index 153a8be06adc..3e44364dc6a0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -118,6 +118,7 @@ public abstract class SysuiTestCase { android.net.platform.flags.Flags.class, android.os.Flags.class, android.service.controls.flags.Flags.class, + android.service.quickaccesswallet.Flags.class, com.android.internal.telephony.flags.Flags.class, com.android.server.notification.Flags.class, com.android.systemui.Flags.class); -- GitLab From 250bfa4be0609e69501c07f52d979f52cba04bd5 Mon Sep 17 00:00:00 2001 From: Prince Date: Tue, 26 Nov 2024 15:35:59 +0000 Subject: [PATCH 052/652] Improve Text Color Contrast in Glanceable hub Updated the text color from MaterialTheme.colorScheme.secondary to MaterialTheme.colorScheme.primary to ensure sufficient contrast on both Light and Dark themes. This change aligns with UX recommendations to avoid using secondary text color on primary backgrounds. Test: Device Tested Flag: NONE small ux change Fixes: 378418098 Change-Id: I18b76c9b4af71b580bbe353d1af236ab17b3a5f2 --- .../src/com/android/systemui/communal/ui/compose/CommunalHub.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 787edfb9168c..f3cf521f5fbc 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -908,7 +908,7 @@ private fun EmptyStateCta(contentPadding: PaddingValues, viewModel: BaseCommunal text = titleForEmptyStateCTA, style = MaterialTheme.typography.displaySmall, textAlign = TextAlign.Center, - color = colors.secondary, + color = colors.primary, modifier = Modifier.focusable().semantics(mergeDescendants = true) { contentDescription = titleForEmptyStateCTA -- GitLab From 6491a452d19b663a5a22643c558222b45c0bc41c Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Thu, 7 Nov 2024 09:05:16 -0800 Subject: [PATCH 053/652] Add flag to narrow the meaning of JANK_PERCEPTIBLE. When the jank_perceptible_narrow flag is set, JANK_PERCEPTIBLE is determined based on a much more narrow scope of process states instead of whether the process is cached or not. Bug: 304837972 Flag: android.app.jank_perceptible_narrow Test: Device boots, MPTS memhog test with jank_perceptible_narrow set. Change-Id: I0e0ac21c65a3b03a4927a1fd0a67df7045959d75 --- core/java/android/app/ActivityManager.java | 12 +++++++ core/java/android/app/ActivityThread.java | 32 +++++++++---------- .../java/android/app/activity_manager.aconfig | 7 ++++ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index b447897733e1..6c972c87d089 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1180,6 +1180,18 @@ public class ActivityManager { return procState == PROCESS_STATE_FOREGROUND_SERVICE; } + /** @hide Should this process state be considered jank perceptible? */ + public static final boolean isProcStateJankPerceptible(int procState) { + if (Flags.jankPerceptibleNarrow()) { + return procState == PROCESS_STATE_PERSISTENT_UI + || procState == PROCESS_STATE_TOP + || procState == PROCESS_STATE_IMPORTANT_FOREGROUND + || procState == PROCESS_STATE_TOP_SLEEPING; + } else { + return !isProcStateCached(procState); + } + } + /** @hide requestType for assist context: only basic information. */ public static final int ASSIST_CONTEXT_BASIC = 0; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 60b8f80d8f2d..a95ce0e24a03 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3911,12 +3911,7 @@ public final class ActivityThread extends ClientTransactionHandler if (mLastProcessState == processState) { return; } - // Do not issue a transitional GC if we are transitioning between 2 cached states. - // Only update if the state flips between cached and uncached or vice versa - if (ActivityManager.isProcStateCached(mLastProcessState) - != ActivityManager.isProcStateCached(processState)) { - updateVmProcessState(processState); - } + updateVmProcessState(mLastProcessState, processState); mLastProcessState = processState; if (localLOGV) { Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState @@ -3925,18 +3920,21 @@ public final class ActivityThread extends ClientTransactionHandler } } + /** Converts a process state to a VM process state. */ + private static int toVmProcessState(int processState) { + final int state = ActivityManager.isProcStateJankPerceptible(processState) + ? VM_PROCESS_STATE_JANK_PERCEPTIBLE + : VM_PROCESS_STATE_JANK_IMPERCEPTIBLE; + return state; + } + /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */ - // Currently ART VM only uses state updates for Transitional GC, and thus - // this function initiates a Transitional GC for transitions into Cached apps states. - private void updateVmProcessState(int processState) { - // Only a transition into Cached state should result in a Transitional GC request - // to the ART runtime. Update VM state to JANK_IMPERCEPTIBLE in that case. - // Note that there are 4 possible cached states currently, all of which are - // JANK_IMPERCEPTIBLE from GC point of view. - final int state = ActivityManager.isProcStateCached(processState) - ? VM_PROCESS_STATE_JANK_IMPERCEPTIBLE - : VM_PROCESS_STATE_JANK_PERCEPTIBLE; - VMRuntime.getRuntime().updateProcessState(state); + private void updateVmProcessState(int lastProcessState, int newProcessState) { + final int state = toVmProcessState(newProcessState); + if (lastProcessState == PROCESS_STATE_UNKNOWN + || state != toVmProcessState(lastProcessState)) { + VMRuntime.getRuntime().updateProcessState(state); + } } @Override diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig index 1f31ab5d1849..d4cd02b47e99 100644 --- a/core/java/android/app/activity_manager.aconfig +++ b/core/java/android/app/activity_manager.aconfig @@ -165,3 +165,10 @@ flag { description: "Control ApplicationStartInfo component field and API" bug: "362537357" } + +flag { + name: "jank_perceptible_narrow" + namespace: "system_performance" + description: "Narrow the scope of Jank Perceptible" + bug: "304837972" +} -- GitLab From ce8bee1b93af62a57097ca60c36265488a1d9371 Mon Sep 17 00:00:00 2001 From: Olivier St-Onge Date: Thu, 21 Nov 2024 10:09:56 -0500 Subject: [PATCH 054/652] Pre load all icon drawables for QS tiles This change is flag guarded. Preloading drawables improves performance in Compose and is needed for the QS refactor. Test: manually Test: local perfetto tracing Flag: com.android.systemui.qs_ui_refactor_compose_fragment Bug: 377290311 Change-Id: Ie70debb02f2d58e547d063f5c358286170f4c7b5 --- .../qs/tiles/DeviceControlsTileTest.kt | 205 ++++++++---------- .../ui/compose/infinitegrid/CommonTile.kt | 3 +- .../systemui/qs/tileimpl/QSTileImpl.java | 19 ++ .../systemui/qs/tiles/AirplaneModeTile.java | 2 +- .../android/systemui/qs/tiles/AlarmTile.kt | 57 ++--- .../systemui/qs/tiles/BatterySaverTile.java | 5 +- .../systemui/qs/tiles/BluetoothTile.java | 14 +- .../android/systemui/qs/tiles/CastTile.java | 4 +- .../qs/tiles/ColorCorrectionTile.java | 6 +- .../systemui/qs/tiles/ColorInversionTile.java | 2 +- .../systemui/qs/tiles/DataSaverTile.java | 2 +- .../systemui/qs/tiles/DeviceControlsTile.kt | 92 ++++---- .../android/systemui/qs/tiles/DndTile.java | 2 +- .../android/systemui/qs/tiles/DreamTile.java | 7 +- .../systemui/qs/tiles/FlashlightTile.java | 4 +- .../systemui/qs/tiles/FontScalingTile.kt | 5 +- .../systemui/qs/tiles/HearingDevicesTile.java | 2 +- .../systemui/qs/tiles/HotspotTile.java | 4 +- .../systemui/qs/tiles/InternetTile.java | 28 +-- .../systemui/qs/tiles/LocationTile.java | 2 +- .../android/systemui/qs/tiles/ModesTile.kt | 2 +- .../android/systemui/qs/tiles/NfcTile.java | 7 +- .../systemui/qs/tiles/NightDisplayTile.java | 2 +- .../android/systemui/qs/tiles/NotesTile.kt | 14 +- .../systemui/qs/tiles/OneHandedModeTile.java | 8 +- .../systemui/qs/tiles/QRCodeScannerTile.java | 2 +- .../qs/tiles/QuickAccessWalletTile.java | 2 +- .../systemui/qs/tiles/RecordIssueTile.kt | 4 +- .../qs/tiles/ReduceBrightColorsTile.java | 2 +- .../systemui/qs/tiles/RotationLockTile.java | 7 +- .../systemui/qs/tiles/ScreenRecordTile.java | 5 +- .../qs/tiles/SensorPrivacyToggleTile.java | 2 +- .../systemui/qs/tiles/UiModeNightTile.java | 2 +- .../systemui/qs/tiles/WorkModeTile.java | 9 +- .../shared/ui/model/InternetTileModel.kt | 4 +- 35 files changed, 286 insertions(+), 251 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index 940da9967a68..33748b973f1c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -27,7 +27,6 @@ import androidx.lifecycle.LifecycleOwner import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.classifier.FalsingManagerFake @@ -45,13 +44,14 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger -import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.res.R import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat +import java.util.Optional import org.junit.After import org.junit.Before import org.junit.Test @@ -67,40 +67,27 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.Optional @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class DeviceControlsTileTest : SysuiTestCase() { - @Mock - private lateinit var qsHost: QSHost - @Mock - private lateinit var metricsLogger: MetricsLogger - @Mock - private lateinit var statusBarStateController: StatusBarStateController - @Mock - private lateinit var activityStarter: ActivityStarter - @Mock - private lateinit var qsLogger: QSLogger - @Mock - private lateinit var controlsComponent: ControlsComponent - @Mock - private lateinit var controlsUiController: ControlsUiController - @Mock - private lateinit var controlsListingController: ControlsListingController - @Mock - private lateinit var controlsController: ControlsController - @Mock - private lateinit var serviceInfo: ControlsServiceInfo - @Mock - private lateinit var uiEventLogger: QsEventLogger + @Mock private lateinit var qsHost: QSHost + @Mock private lateinit var metricsLogger: MetricsLogger + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var qsLogger: QSLogger + @Mock private lateinit var controlsComponent: ControlsComponent + @Mock private lateinit var controlsUiController: ControlsUiController + @Mock private lateinit var controlsListingController: ControlsListingController + @Mock private lateinit var controlsController: ControlsController + @Mock private lateinit var serviceInfo: ControlsServiceInfo + @Mock private lateinit var uiEventLogger: QsEventLogger @Captor private lateinit var listingCallbackCaptor: - ArgumentCaptor - @Captor - private lateinit var intentCaptor: ArgumentCaptor + ArgumentCaptor + @Captor private lateinit var intentCaptor: ArgumentCaptor private lateinit var testableLooper: TestableLooper private lateinit var tile: DeviceControlsTile @@ -120,8 +107,11 @@ class DeviceControlsTileTest : SysuiTestCase() { `when`(qsHost.context).thenReturn(spiedContext) `when`(controlsComponent.isEnabled()).thenReturn(true) `when`(controlsController.getPreferredSelection()) - .thenReturn(SelectedItem.StructureItem( - StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()))) + .thenReturn( + SelectedItem.StructureItem( + StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()) + ) + ) secureSettings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 1) setupControlsComponent() @@ -182,10 +172,11 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testObservingCallback() { - verify(controlsListingController).observe( + verify(controlsListingController) + .observe( any(LifecycleOwner::class.java), - any(ControlsListingController.ControlsListingCallback::class.java) - ) + any(ControlsListingController.ControlsListingCallback::class.java), + ) } @Test @@ -205,10 +196,8 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testStateUnavailableIfNoListings() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) listingCallbackCaptor.value.onServicesUpdated(emptyList()) testableLooper.processAllMessages() @@ -218,10 +207,8 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testStateUnavailableIfNotEnabled() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) `when`(controlsComponent.isEnabled()).thenReturn(false) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) @@ -232,18 +219,19 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testStateActiveIfListingsHasControlsFavorited() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) - `when`(controlsController.getPreferredSelection()).thenReturn( - SelectedItem.StructureItem(StructureInfo( - ComponentName("pkg", "cls"), - "structure", - listOf(ControlInfo("id", "title", "subtitle", 1)) - )) - ) + `when`(controlsController.getPreferredSelection()) + .thenReturn( + SelectedItem.StructureItem( + StructureInfo( + ComponentName("pkg", "cls"), + "structure", + listOf(ControlInfo("id", "title", "subtitle", 1)), + ) + ) + ) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -253,14 +241,15 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testStateInactiveIfListingsHasNoControlsFavorited() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) `when`(controlsController.getPreferredSelection()) - .thenReturn(SelectedItem.StructureItem( - StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()))) + .thenReturn( + SelectedItem.StructureItem( + StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()) + ) + ) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -270,13 +259,11 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testStateActiveIfPreferredIsPanel() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) `when`(controlsController.getPreferredSelection()) - .thenReturn(SelectedItem.PanelItem("appName", ComponentName("pkg", "cls"))) + .thenReturn(SelectedItem.PanelItem("appName", ComponentName("pkg", "cls"))) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -286,10 +273,8 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testStateInactiveIfLocked() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) `when`(controlsComponent.getVisibility()) .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK) @@ -301,10 +286,8 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testMoveBetweenStates() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -325,19 +308,20 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun handleClick_available_shownOverLockscreenWhenLocked() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) `when`(controlsUiController.resolveActivity()).thenReturn(ControlsActivity::class.java) - `when`(controlsController.getPreferredSelection()).thenReturn( - SelectedItem.StructureItem(StructureInfo( - ComponentName("pkg", "cls"), - "structure", - listOf(ControlInfo("id", "title", "subtitle", 1)) - )) - ) + `when`(controlsController.getPreferredSelection()) + .thenReturn( + SelectedItem.StructureItem( + StructureInfo( + ComponentName("pkg", "cls"), + "structure", + listOf(ControlInfo("id", "title", "subtitle", 1)), + ) + ) + ) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -345,30 +329,33 @@ class DeviceControlsTileTest : SysuiTestCase() { tile.click(null /* view */) testableLooper.processAllMessages() - verify(activityStarter).startActivity( + verify(activityStarter) + .startActivity( intentCaptor.capture(), eq(true) /* dismissShade */, nullable(ActivityTransitionAnimator.Controller::class.java), - eq(true) /* showOverLockscreenWhenLocked */) + eq(true), /* showOverLockscreenWhenLocked */ + ) assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME) } @Test fun handleClick_availableAfterUnlock_notShownOverLockscreenWhenLocked() { - verify(controlsListingController).observe( - any(LifecycleOwner::class.java), - capture(listingCallbackCaptor) - ) + verify(controlsListingController) + .observe(any(LifecycleOwner::class.java), capture(listingCallbackCaptor)) `when`(controlsComponent.getVisibility()) .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK) `when`(controlsUiController.resolveActivity()).thenReturn(ControlsActivity::class.java) - `when`(controlsController.getPreferredSelection()).thenReturn( - SelectedItem.StructureItem(StructureInfo( - ComponentName("pkg", "cls"), - "structure", - listOf(ControlInfo("id", "title", "subtitle", 1)) - )) - ) + `when`(controlsController.getPreferredSelection()) + .thenReturn( + SelectedItem.StructureItem( + StructureInfo( + ComponentName("pkg", "cls"), + "structure", + listOf(ControlInfo("id", "title", "subtitle", 1)), + ) + ) + ) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -376,26 +363,19 @@ class DeviceControlsTileTest : SysuiTestCase() { tile.click(null /* view */) testableLooper.processAllMessages() - verify(activityStarter).startActivity( + verify(activityStarter) + .startActivity( intentCaptor.capture(), anyBoolean() /* dismissShade */, nullable(ActivityTransitionAnimator.Controller::class.java), - eq(false) /* showOverLockscreenWhenLocked */) + eq(false), /* showOverLockscreenWhenLocked */ + ) assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME) } @Test fun verifyTileEqualsResourceFromComponent() { - assertThat(tile.tileLabel) - .isEqualTo( - context.getText( - controlsComponent.getTileTitleId())) - } - - @Test - fun verifyTileImageEqualsResourceFromComponent() { - assertThat(tile.icon) - .isEqualTo(QSTileImpl.ResourceIcon.get(controlsComponent.getTileImageId())) + assertThat(tile.tileLabel).isEqualTo(context.getText(controlsComponent.getTileTitleId())) } private fun createTile(): DeviceControlsTile { @@ -409,11 +389,12 @@ class DeviceControlsTileTest : SysuiTestCase() { statusBarStateController, activityStarter, qsLogger, - controlsComponent - ).also { - it.initialize() - testableLooper.processAllMessages() - } + controlsComponent, + ) + .also { + it.initialize() + testableLooper.processAllMessages() + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt index dbad60265645..d72d5f127bba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt @@ -21,7 +21,6 @@ import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.Drawable import android.text.TextUtils import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi import androidx.compose.animation.graphics.res.animatedVectorResource import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.vector.AnimatedImageVector @@ -192,7 +191,6 @@ fun LargeTileLabels( } } -@OptIn(ExperimentalAnimationGraphicsApi::class) @Composable fun SmallTileContent( modifier: Modifier = Modifier, @@ -229,6 +227,7 @@ fun SmallTileContent( } } } + is Icon.Loaded -> { LaunchedEffect(loadedDrawable) { if (loadedDrawable is AnimatedVectorDrawable) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 9abc494e56e6..464eeda6a0a8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -31,6 +31,7 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import android.annotation.CallSuper; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; @@ -68,6 +69,7 @@ import com.android.systemui.qs.QSEvent; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SideLabelTileLayout; +import com.android.systemui.qs.flags.QsInCompose; import com.android.systemui.qs.logging.QSLogger; import java.io.PrintWriter; @@ -535,6 +537,23 @@ public abstract class QSTileImpl implements QSTile, Lifecy } } + protected Icon maybeLoadResourceIcon(int id) { + return maybeLoadResourceIcon(id, mContext); + } + + /** + * Returns the {@link QSTile.Icon} for the resource ID, optionally loading the drawable if + * {@link QsInCompose#isEnabled()} is true. + */ + @SuppressLint("UseCompatLoadingForDrawables") + public static Icon maybeLoadResourceIcon(int id, Context context) { + if (QsInCompose.isEnabled()) { + return new DrawableIconWithRes(context.getDrawable(id), id); + } else { + return ResourceIcon.get(id); + } + } + @Override public String getMetricsSpec() { return mTileSpec; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 71b69c92b87d..bb818fa5e164 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -160,7 +160,7 @@ public class AirplaneModeTile extends QSTileImpl { final boolean airplaneMode = value != 0; state.value = airplaneMode; state.label = mContext.getString(R.string.airplane_mode); - state.icon = ResourceIcon.get(state.value + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_airplane_icon_on : R.drawable.qs_airplane_icon_off); state.state = airplaneMode ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.contentDescription = state.label; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt index 73d991f6efe7..75debb6c4032 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt @@ -42,35 +42,35 @@ constructor( activityStarter: ActivityStarter, qsLogger: QSLogger, private val userTracker: UserTracker, - nextAlarmController: NextAlarmController -) : QSTileImpl( - host, - uiEventLogger, - backgroundLooper, - mainHandler, - falsingManager, - metricsLogger, - statusBarStateController, - activityStarter, - qsLogger -) { + nextAlarmController: NextAlarmController, +) : + QSTileImpl( + host, + uiEventLogger, + backgroundLooper, + mainHandler, + falsingManager, + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger, + ) { private var lastAlarmInfo: AlarmManager.AlarmClockInfo? = null - private val icon = ResourceIcon.get(R.drawable.ic_alarm) + private var icon: QSTile.Icon? = null @VisibleForTesting internal val defaultIntent = Intent(AlarmClock.ACTION_SHOW_ALARMS) - private val callback = NextAlarmController.NextAlarmChangeCallback { nextAlarm -> - lastAlarmInfo = nextAlarm - refreshState() - } + private val callback = + NextAlarmController.NextAlarmChangeCallback { nextAlarm -> + lastAlarmInfo = nextAlarm + refreshState() + } init { nextAlarmController.observe(this, callback) } override fun newTileState(): QSTile.State { - return QSTile.State().apply { - handlesLongClick = false - } + return QSTile.State().apply { handlesLongClick = false } } override fun handleClick(expandable: Expandable?) { @@ -82,21 +82,28 @@ constructor( if (pendingIntent != null) { mActivityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController) } else { - mActivityStarter.postStartActivityDismissingKeyguard(defaultIntent, 0, - animationController) + mActivityStarter.postStartActivityDismissingKeyguard( + defaultIntent, + 0, + animationController, + ) } } override fun handleUpdateState(state: QSTile.State, arg: Any?) { + if (icon == null) { + icon = maybeLoadResourceIcon(R.drawable.ic_alarm) + } state.icon = icon state.label = tileLabel lastAlarmInfo?.let { state.secondaryLabel = formatNextAlarm(it) state.state = Tile.STATE_ACTIVE - } ?: run { - state.secondaryLabel = mContext.getString(R.string.qs_alarm_tile_no_alarm) - state.state = Tile.STATE_INACTIVE } + ?: run { + state.secondaryLabel = mContext.getString(R.string.qs_alarm_tile_no_alarm) + state.state = Tile.STATE_INACTIVE + } state.contentDescription = TextUtils.concat(state.label, ", ", state.secondaryLabel) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 7c0ce4cc75a9..9df4e42d1898 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -147,9 +147,8 @@ public class BatterySaverTile extends QSTileImpl implements protected void handleUpdateState(BooleanState state, Object arg) { state.state = mPluggedIn ? Tile.STATE_UNAVAILABLE : mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.icon = ResourceIcon.get(mPowerSave - ? R.drawable.qs_battery_saver_icon_on - : R.drawable.qs_battery_saver_icon_off); + state.icon = maybeLoadResourceIcon(mPowerSave + ? R.drawable.qs_battery_saver_icon_on : R.drawable.qs_battery_saver_icon_off); state.label = mContext.getString(R.string.battery_detail_switch_title); state.secondaryLabel = ""; state.contentDescription = state.label; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 7bff827dee03..7eb0aaabb7e1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -59,13 +59,13 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.BluetoothController; +import kotlinx.coroutines.Job; + import java.util.List; import java.util.concurrent.Executor; import javax.inject.Inject; -import kotlinx.coroutines.Job; - /** Quick settings tile: Bluetooth **/ public class BluetoothTile extends QSTileImpl { @@ -201,7 +201,7 @@ public class BluetoothTile extends QSTileImpl { if (enabled) { if (connected) { - state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_on); + state.icon = maybeLoadResourceIcon(R.drawable.qs_bluetooth_icon_on); if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) { state.label = mController.getConnectedDeviceName(); } @@ -209,17 +209,15 @@ public class BluetoothTile extends QSTileImpl { mContext.getString(R.string.accessibility_bluetooth_name, state.label) + ", " + state.secondaryLabel; } else if (state.isTransient) { - state.icon = ResourceIcon.get( - R.drawable.qs_bluetooth_icon_search); + state.icon = maybeLoadResourceIcon(R.drawable.qs_bluetooth_icon_search); state.stateDescription = state.secondaryLabel; } else { - state.icon = - ResourceIcon.get(R.drawable.qs_bluetooth_icon_off); + state.icon = maybeLoadResourceIcon(R.drawable.qs_bluetooth_icon_off); state.stateDescription = mContext.getString(R.string.accessibility_not_connected); } state.state = Tile.STATE_ACTIVE; } else { - state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_off); + state.icon = maybeLoadResourceIcon(R.drawable.qs_bluetooth_icon_off); state.state = Tile.STATE_INACTIVE; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 8a72e8db7216..ad027b4346d0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -290,8 +290,8 @@ public class CastTile extends QSTileImpl { if (connecting && !state.value) { state.secondaryLabel = mContext.getString(R.string.quick_settings_connecting); } - state.icon = ResourceIcon.get(state.value ? R.drawable.ic_cast_connected - : R.drawable.ic_cast); + state.icon = maybeLoadResourceIcon(state.value + ? R.drawable.ic_cast_connected : R.drawable.ic_cast); if (canCastToNetwork() || state.value) { state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; if (!state.value) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java index 871973dfcb7f..c2e609ddfc3a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java @@ -50,7 +50,8 @@ public class ColorCorrectionTile extends QSTileImpl { public static final String TILE_SPEC = "color_correction"; - private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_color_correction); + @Nullable + private Icon mIcon = null; private final UserSettingObserver mSetting; @Inject @@ -122,6 +123,9 @@ public class ColorCorrectionTile extends QSTileImpl { protected void handleUpdateState(BooleanState state, Object arg) { final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue(); final boolean enabled = value != 0; + if (mIcon == null) { + mIcon = maybeLoadResourceIcon(R.drawable.ic_qs_color_correction); + } state.value = enabled; state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.label = mContext.getString(R.string.quick_settings_color_correction_label); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 58969107ad22..ce80133e67a2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -124,7 +124,7 @@ public class ColorInversionTile extends QSTileImpl { state.value = enabled; state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.label = mContext.getString(R.string.quick_settings_inversion_label); - state.icon = ResourceIcon.get(state.value + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_invert_colors_icon_on : R.drawable.qs_invert_colors_icon_off); state.expandedAccessibilityClassName = Switch.class.getName(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java index 7760943476bf..deeef550b33f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java @@ -147,7 +147,7 @@ public class DataSaverTile extends QSTileImpl implements state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.label = mContext.getString(R.string.data_saver); state.contentDescription = state.label; - state.icon = ResourceIcon.get(state.value ? R.drawable.qs_data_saver_icon_on + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_data_saver_icon_on : R.drawable.qs_data_saver_icon_off); state.expandedAccessibilityClassName = Switch.class.getName(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt index cc8a73423174..404ace18d0f9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt @@ -1,4 +1,3 @@ - /* * Copyright (C) 2021 The Android Open Source Project * @@ -22,10 +21,8 @@ import android.content.Intent import android.os.Handler import android.os.Looper import android.service.quicksettings.Tile -import androidx.annotation.VisibleForTesting import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.MetricsLogger -import com.android.systemui.res.R import com.android.systemui.animation.Expandable import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.dagger.ControlsComponent @@ -43,10 +40,13 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.res.R import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -class DeviceControlsTile @Inject constructor( +class DeviceControlsTile +@Inject +constructor( host: QSHost, uiEventLogger: QsEventLogger, @Background backgroundLooper: Looper, @@ -56,32 +56,34 @@ class DeviceControlsTile @Inject constructor( statusBarStateController: StatusBarStateController, activityStarter: ActivityStarter, qsLogger: QSLogger, - private val controlsComponent: ControlsComponent -) : QSTileImpl( - host, - uiEventLogger, - backgroundLooper, - mainHandler, - falsingManager, - metricsLogger, - statusBarStateController, - activityStarter, - qsLogger -) { + private val controlsComponent: ControlsComponent, +) : + QSTileImpl( + host, + uiEventLogger, + backgroundLooper, + mainHandler, + falsingManager, + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger, + ) { private var hasControlsApps = AtomicBoolean(false) - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - val icon: QSTile.Icon - get() = ResourceIcon.get(controlsComponent.getTileImageId()) + private var icon: QSTile.Icon? = null - private val listingCallback = object : ControlsListingController.ControlsListingCallback { - override fun onServicesUpdated(serviceInfos: List) { - if (hasControlsApps.compareAndSet(serviceInfos.isEmpty(), serviceInfos.isNotEmpty())) { - refreshState() + private val listingCallback = + object : ControlsListingController.ControlsListingCallback { + override fun onServicesUpdated(serviceInfos: List) { + if ( + hasControlsApps.compareAndSet(serviceInfos.isEmpty(), serviceInfos.isNotEmpty()) + ) { + refreshState() + } } } - } init { controlsComponent.getControlsListingController().ifPresent { @@ -105,15 +107,19 @@ class DeviceControlsTile @Inject constructor( return } - val intent = Intent().apply { - component = ComponentName(mContext, controlsComponent.getControlsUiController().get() - .resolveActivity()) - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - putExtra(ControlsUiController.EXTRA_ANIMATE, true) - } + val intent = + Intent().apply { + component = + ComponentName( + mContext, + controlsComponent.getControlsUiController().get().resolveActivity(), + ) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + putExtra(ControlsUiController.EXTRA_ANIMATE, true) + } val animationController = expandable?.activityTransitionController( - InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE ) mUiHandler.post { @@ -130,17 +136,23 @@ class DeviceControlsTile @Inject constructor( override fun handleUpdateState(state: QSTile.State, arg: Any?) { state.label = tileLabel state.contentDescription = state.label + if (icon == null) { + icon = maybeLoadResourceIcon(controlsComponent.getTileImageId()) + } state.icon = icon if (controlsComponent.isEnabled() && hasControlsApps.get()) { if (controlsComponent.getVisibility() == AVAILABLE) { - val selection = controlsComponent - .getControlsController().get().getPreferredSelection() - state.state = if (selection is SelectedItem.StructureItem && - selection.structure.controls.isEmpty()) { - Tile.STATE_INACTIVE - } else { - Tile.STATE_ACTIVE - } + val selection = + controlsComponent.getControlsController().get().getPreferredSelection() + state.state = + if ( + selection is SelectedItem.StructureItem && + selection.structure.controls.isEmpty() + ) { + Tile.STATE_INACTIVE + } else { + Tile.STATE_ACTIVE + } val label = selection.name state.secondaryLabel = if (label == tileLabel) null else label } else { @@ -170,4 +182,4 @@ class DeviceControlsTile @Inject constructor( companion object { const val TILE_SPEC = "controls" } -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index ad76b4f21bfb..04f0b8736598 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -229,7 +229,7 @@ public class DndTile extends QSTileImpl { state.dualTarget = true; state.value = newValue; state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.icon = ResourceIcon.get(state.value + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_dnd_icon_on : R.drawable.qs_dnd_icon_off); state.label = getTileLabel(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java index 0d3d980f71f4..374bcda5f46a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java @@ -64,9 +64,6 @@ public class DreamTile extends QSTileImpl { public static final String TILE_SPEC = "dream"; private static final String LOG_TAG = "QSDream"; - // TODO: consider 1 animated icon instead - private final Icon mIconDocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver); - private final Icon mIconUndocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked); private final IDreamManager mDreamManager; private final BroadcastDispatcher mBroadcastDispatcher; private final UserSettingObserver mEnabledSettingObserver; @@ -170,7 +167,9 @@ public class DreamTile extends QSTileImpl { state.label = getTileLabel(); state.secondaryLabel = getActiveDreamName(); state.contentDescription = getContentDescription(state.secondaryLabel); - state.icon = mIsDocked ? mIconDocked : mIconUndocked; + // TODO: consider 1 animated icon instead + state.icon = maybeLoadResourceIcon(mIsDocked + ? R.drawable.ic_qs_screen_saver : R.drawable.ic_qs_screen_saver_undocked); if (getActiveDream() == null || !isScreensaverEnabled()) { state.state = Tile.STATE_UNAVAILABLE; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index 848ff3c533ba..2b127d60b2be 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -128,7 +128,7 @@ public class FlashlightTile extends QSTileImpl implements R.string.quick_settings_flashlight_camera_in_use); state.stateDescription = state.secondaryLabel; state.state = Tile.STATE_UNAVAILABLE; - state.icon = ResourceIcon.get(R.drawable.qs_flashlight_icon_off); + state.icon = maybeLoadResourceIcon(R.drawable.qs_flashlight_icon_off); return; } if (arg instanceof Boolean) { @@ -143,7 +143,7 @@ public class FlashlightTile extends QSTileImpl implements state.contentDescription = mContext.getString(R.string.quick_settings_flashlight_label); state.expandedAccessibilityClassName = Switch.class.getName(); state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.icon = ResourceIcon.get(state.value + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_flashlight_icon_on : R.drawable.qs_flashlight_icon_off); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt index 7606293454f8..43e84a0ee2b4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt @@ -68,7 +68,7 @@ constructor( activityStarter, qsLogger, ) { - private val icon = ResourceIcon.get(R.drawable.ic_qs_font_scaling) + private var icon: QSTile.Icon? = null override fun newTileState(): QSTile.State { return QSTile.State() @@ -108,6 +108,9 @@ constructor( } override fun handleUpdateState(state: QSTile.State?, arg: Any?) { + if (icon == null) { + icon = maybeLoadResourceIcon(R.drawable.ic_qs_font_scaling) + } state?.label = mContext.getString(R.string.quick_settings_font_scaling_label) state?.icon = icon state?.contentDescription = state?.label diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java index f723ff264e0c..48b39dcf29ba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java @@ -106,7 +106,7 @@ public class HearingDevicesTile extends QSTileImpl { checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH); state.label = mContext.getString(R.string.quick_settings_hearing_devices_label); - state.icon = ResourceIcon.get(R.drawable.qs_hearing_devices_icon); + state.icon = maybeLoadResourceIcon(R.drawable.qs_hearing_devices_icon); state.forceExpandIcon = true; boolean isBonded = mDevicesChecker.isAnyPairedHearingDevice(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index ea3993ea88a9..03bbbd7017ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -151,10 +151,10 @@ public class HotspotTile extends QSTileImpl { state.label = mContext.getString(R.string.quick_settings_hotspot_label); state.isTransient = isTransient; if (state.isTransient) { - state.icon = ResourceIcon.get( + state.icon = maybeLoadResourceIcon( R.drawable.qs_hotspot_icon_search); } else { - state.icon = ResourceIcon.get(state.value + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_hotspot_icon_on : R.drawable.qs_hotspot_icon_off); } state.expandedAccessibilityClassName = Switch.class.getName(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 02f6f80d7282..e9c5f4ae9b10 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -529,10 +529,10 @@ public class InternetTile extends QSTileImpl { if (cb.mAirplaneModeEnabled) { if (!state.value) { state.state = Tile.STATE_INACTIVE; - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.status_bar_airplane); } else if (!wifiConnected) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qs_no_internet_unavailable); if (cb.mNoNetworksAvailable) { state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); @@ -541,28 +541,28 @@ public class InternetTile extends QSTileImpl { r.getString(R.string.quick_settings_networks_available); } } else { - state.icon = ResourceIcon.get(cb.mWifiSignalIconId); + state.icon = maybeLoadResourceIcon(cb.mWifiSignalIconId); } } else if (cb.mNoDefaultNetwork) { if (cb.mNoNetworksAvailable || !cb.mEnabled) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); } else { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qs_no_internet_available); state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); } } else if (cb.mIsTransient) { - state.icon = ResourceIcon.get( + state.icon = maybeLoadResourceIcon( com.android.internal.R.drawable.ic_signal_wifi_transient_animation); } else if (!state.value) { state.state = Tile.STATE_INACTIVE; - state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED); + state.icon = maybeLoadResourceIcon(WifiIcons.QS_WIFI_DISABLED); } else if (wifiConnected) { - state.icon = ResourceIcon.get(cb.mWifiSignalIconId); + state.icon = maybeLoadResourceIcon(cb.mWifiSignalIconId); } else if (wifiNotConnected) { - state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); + state.icon = maybeLoadResourceIcon(WifiIcons.QS_WIFI_NO_NETWORK); } else { - state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); + state.icon = maybeLoadResourceIcon(WifiIcons.QS_WIFI_NO_NETWORK); } minimalContentDescription.append( mContext.getString(R.string.quick_settings_internet_label)).append(","); @@ -598,14 +598,14 @@ public class InternetTile extends QSTileImpl { if (cb.mAirplaneModeEnabled && cb.mQsTypeIcon != TelephonyIcons.ICON_CWF) { state.state = Tile.STATE_INACTIVE; - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.status_bar_airplane); } else if (cb.mNoDefaultNetwork) { if (cb.mNoNetworksAvailable || !mSignalCallback.mWifiInfo.mEnabled) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); } else { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qs_no_internet_available); state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); } } else { @@ -637,7 +637,7 @@ public class InternetTile extends QSTileImpl { final Resources r = mContext.getResources(); state.label = r.getString(R.string.quick_settings_internet_label); state.state = Tile.STATE_ACTIVE; - state.icon = ResourceIcon.get(cb.mEthernetSignalIconId); + state.icon = maybeLoadResourceIcon(cb.mEthernetSignalIconId); state.secondaryLabel = cb.mEthernetContentDescription; if (DEBUG) { Log.d(TAG, "handleUpdateEthernetState: " + "BooleanState = " + state.toString()); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index cad5c0d12d1d..f35c25f24162 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -122,7 +122,7 @@ public class LocationTile extends QSTileImpl { if (state.disabledByPolicy == false) { checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION); } - state.icon = ResourceIcon.get(state.value + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_location_icon_on : R.drawable.qs_location_icon_off); state.label = mContext.getString(R.string.quick_settings_location_label); state.contentDescription = state.label; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt index fef5a745c1ca..9c6345666403 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt @@ -120,7 +120,7 @@ constructor( tileState = tileMapper.map(config, model) state?.apply { this.state = tileState.activationState.legacyState - icon = tileState.icon?.asQSTileIcon() ?: ResourceIcon.get(ICON_RES_ID) + icon = tileState.icon?.asQSTileIcon() ?: maybeLoadResourceIcon(ICON_RES_ID) label = tileLabel secondaryLabel = tileState.secondaryLabel contentDescription = tileState.contentDescription diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index 136eea8331df..683e4e93cf4b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -55,7 +55,8 @@ public class NfcTile extends QSTileImpl { public static final String TILE_SPEC = "nfc"; private static final String NFC = TILE_SPEC; - private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc); + @Nullable + private Icon mIcon = null; @Nullable private NfcAdapter mAdapter; @@ -137,6 +138,10 @@ public class NfcTile extends QSTileImpl { @Override protected void handleUpdateState(BooleanState state, Object arg) { + if (mIcon == null) { + mIcon = maybeLoadResourceIcon(R.drawable.ic_qs_nfc); + } + state.value = getAdapter() != null && getAdapter().isEnabled(); state.state = getAdapter() == null ? Tile.STATE_UNAVAILABLE diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index ac762de6d544..2f5908752111 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -150,7 +150,7 @@ public class NightDisplayTile extends QSTileImpl implements state.label = mContext.getString(R.string.quick_settings_night_display_label); state.expandedAccessibilityClassName = Switch.class.getName(); state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.icon = ResourceIcon.get(state.value ? R.drawable.qs_nightlight_icon_on + state.icon = maybeLoadResourceIcon(state.value ? R.drawable.qs_nightlight_icon_on : R.drawable.qs_nightlight_icon_off); state.secondaryLabel = getSecondaryLabel(state.value); state.contentDescription = TextUtils.isEmpty(state.secondaryLabel) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt index 69df0961bf66..989fc0fd6f44 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt @@ -40,14 +40,14 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.res.R import javax.inject.Inject -import kotlinx.coroutines.runBlocking /** Quick settings tile: Notes */ class NotesTile -@Inject constructor( +@Inject +constructor( private val host: QSHost, private val uiEventLogger: QsEventLogger, - @Background private val backgroundLooper: Looper, + @Background private val backgroundLooper: Looper, @Main private val mainHandler: Handler, private val falsingManager: FalsingManager, private val metricsLogger: MetricsLogger, @@ -74,8 +74,7 @@ class NotesTile private lateinit var tileState: QSTileState private val config = qsTileConfigProvider.getConfig(TILE_SPEC) - override fun getTileLabel(): CharSequence = - mContext.getString(config.uiConfig.labelRes) + override fun getTileLabel(): CharSequence = mContext.getString(config.uiConfig.labelRes) override fun newTileState(): QSTile.State? { return QSTile.State().apply { state = Tile.STATE_INACTIVE } @@ -88,13 +87,12 @@ class NotesTile override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent override fun handleUpdateState(state: QSTile.State?, arg: Any?) { - val model = - if (arg is NotesTileModel) arg else dataInteractor.getCurrentTileModel() + val model = if (arg is NotesTileModel) arg else dataInteractor.getCurrentTileModel() tileState = tileMapper.map(config, model) state?.apply { this.state = tileState.activationState.legacyState - icon = ResourceIcon.get(tileState.iconRes ?: R.drawable.ic_qs_notes) + icon = maybeLoadResourceIcon(tileState.iconRes ?: R.drawable.ic_qs_notes) label = tileState.label contentDescription = tileState.contentDescription expandedAccessibilityClassName = tileState.expandedAccessibilityClassName diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java index 450c95411c3f..c605ac8d80b1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java @@ -51,8 +51,8 @@ public class OneHandedModeTile extends QSTileImpl { public static final String TILE_SPEC = "onehanded"; - private final Icon mIcon = ResourceIcon.get( - com.android.internal.R.drawable.ic_qs_one_handed_mode); + @Nullable + private Icon mIcon = null; private final UserSettingObserver mSetting; @Inject @@ -125,6 +125,10 @@ public class OneHandedModeTile extends QSTileImpl { @Override protected void handleUpdateState(BooleanState state, Object arg) { + if (mIcon == null) { + mIcon = maybeLoadResourceIcon(com.android.internal.R.drawable.ic_qs_one_handed_mode); + } + final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue(); final boolean enabled = value != 0; state.value = enabled; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java index 9766fac7965e..93a51cfacf02 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java @@ -119,7 +119,7 @@ public class QRCodeScannerTile extends QSTileImpl { protected void handleUpdateState(State state, Object arg) { state.label = mContext.getString(R.string.qr_code_scanner_title); state.contentDescription = state.label; - state.icon = ResourceIcon.get(R.drawable.ic_qr_code_scanner); + state.icon = maybeLoadResourceIcon(R.drawable.ic_qr_code_scanner); state.state = mQRCodeScannerController.isAbleToLaunchScannerActivity() ? Tile.STATE_INACTIVE : Tile.STATE_UNAVAILABLE; // The assumption is that if the OEM has the QR code scanner module enabled then the scanner diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 37d24debe958..3d039e6ef824 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -154,7 +154,7 @@ public class QuickAccessWalletTile extends QSTileImpl { Drawable tileIcon = mController.getWalletClient().getTileIcon(); state.icon = tileIcon == null - ? ResourceIcon.get(R.drawable.ic_wallet_lockscreen) + ? maybeLoadResourceIcon(R.drawable.ic_wallet_lockscreen) : new DrawableIcon(tileIcon); boolean isDeviceLocked = !mKeyguardStateController.isUnlocked(); if (mController.getWalletClient().isWalletServiceAvailable() diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt index 028ac6f4ac18..ca9d96ebf3e3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -221,13 +221,13 @@ constructor( state = Tile.STATE_ACTIVE forceExpandIcon = false secondaryLabel = mContext.getString(R.string.qs_record_issue_stop) - icon = ResourceIcon.get(R.drawable.qs_record_issue_icon_on) + icon = maybeLoadResourceIcon(R.drawable.qs_record_issue_icon_on) } else { value = false state = Tile.STATE_INACTIVE forceExpandIcon = true secondaryLabel = mContext.getString(R.string.qs_record_issue_start) - icon = ResourceIcon.get(R.drawable.qs_record_issue_icon_off) + icon = maybeLoadResourceIcon(R.drawable.qs_record_issue_icon_off) } label = tileLabel contentDescription = diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java index d624d989f42a..26d43ee92bbc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java @@ -141,7 +141,7 @@ public class ReduceBrightColorsTile extends QSTileImpl state.label = mContext.getString(R.string.reduce_bright_colors_feature_name); state.expandedAccessibilityClassName = Switch.class.getName(); state.contentDescription = state.label; - state.icon = ResourceIcon.get(state.value + state.icon = maybeLoadResourceIcon(state.value ? drawable.qs_extra_dim_icon_on : drawable.qs_extra_dim_icon_off); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 35e43b6fed9e..e361bb8ce883 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -63,7 +63,8 @@ public class RotationLockTile extends QSTileImpl implements private static final String EMPTY_SECONDARY_STRING = ""; - private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate); + private final Icon mIcon = + maybeLoadResourceIcon(com.android.internal.R.drawable.ic_qs_auto_rotate); private final RotationLockController mController; private final SensorPrivacyManager mPrivacyManager; private final BatteryController mBatteryController; @@ -153,13 +154,13 @@ public class RotationLockTile extends QSTileImpl implements && mController.isCameraRotationEnabled(); state.value = !rotationLocked; state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); - state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off); + state.icon = maybeLoadResourceIcon(R.drawable.qs_auto_rotate_icon_off); state.contentDescription = getAccessibilityString(rotationLocked); if (!rotationLocked) { state.secondaryLabel = cameraRotation ? mContext.getResources().getString( R.string.rotation_lock_camera_rotation_on) : EMPTY_SECONDARY_STRING; - state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on); + state.icon = maybeLoadResourceIcon(R.drawable.qs_auto_rotate_icon_on); } else { state.secondaryLabel = EMPTY_SECONDARY_STRING; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index f3be340f4951..6b0287ef6848 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -138,9 +138,8 @@ public class ScreenRecordTile extends QSTileImpl state.value = isRecording || isStarting; state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.label = mContext.getString(R.string.quick_settings_screen_record_label); - state.icon = ResourceIcon.get(state.value - ? R.drawable.qs_screen_record_icon_on - : R.drawable.qs_screen_record_icon_off); + state.icon = maybeLoadResourceIcon(state.value + ? R.drawable.qs_screen_record_icon_on : R.drawable.qs_screen_record_icon_off); // Show expand icon when clicking will open a dialog state.forceExpandIcon = state.state == Tile.STATE_INACTIVE; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java index 036ce080c543..b62e858e6ade 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -119,7 +119,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl implements } else { state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; } - state.icon = ResourceIcon.get(state.state == Tile.STATE_ACTIVE + state.icon = maybeLoadResourceIcon(state.state == Tile.STATE_ACTIVE ? R.drawable.qs_light_dark_theme_icon_on : R.drawable.qs_light_dark_theme_icon_off); state.expandedAccessibilityClassName = Switch.class.getName(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index 1750347fd2ae..f6f89f759e0d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -53,8 +53,8 @@ public class WorkModeTile extends QSTileImpl implements public static final String TILE_SPEC = "work"; - private final Icon mIcon = ResourceIcon.get( - com.android.internal.R.drawable.stat_sys_managed_profile_status); + @Nullable + private Icon mIcon = null; private final ManagedProfileController mProfileController; @@ -129,6 +129,11 @@ public class WorkModeTile extends QSTileImpl implements state.value = mProfileController.isWorkModeEnabled(); } + if (mIcon == null) { + mIcon = maybeLoadResourceIcon( + com.android.internal.R.drawable.stat_sys_managed_profile_status); + } + state.icon = mIcon; state.label = getTileLabel(); state.contentDescription = state.label; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt index ed0eb6d44508..d3e37119fdcb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.model +import android.annotation.SuppressLint import android.content.Context import android.graphics.drawable.Drawable import android.service.quicksettings.Tile @@ -36,6 +37,7 @@ sealed interface InternetTileModel { val stateDescription: ContentDescription? val contentDescription: ContentDescription? + @SuppressLint("UseCompatLoadingForDrawables") fun applyTo(state: QSTile.BooleanState, context: Context) { if (secondaryLabel != null) { state.secondaryLabel = secondaryLabel.loadText(context) @@ -50,7 +52,7 @@ sealed interface InternetTileModel { if (icon != null) { state.icon = icon } else if (iconId != null) { - state.icon = QSTileImpl.ResourceIcon.get(iconId!!) + state.icon = QSTileImpl.maybeLoadResourceIcon(iconId!!, context) } state.state = -- GitLab From 04d8822850021386cfa5a45cce9d24a712818406 Mon Sep 17 00:00:00 2001 From: Preethi Kandhalu Date: Tue, 12 Nov 2024 10:36:52 -0800 Subject: [PATCH 055/652] [Media Quality] Implement Remove Picture Profile API & temp map functionality Test: logs and CTS Bug: 378705930 Flag: android.media.tv.flags.media_quality_fw Change-Id: If095b33ceb24f13f07cf53bd1c919f8a7918b544 --- services/core/Android.bp | 1 + .../media/quality/MediaQualityService.java | 73 ++++++++++++++----- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/services/core/Android.bp b/services/core/Android.bp index b9540eba938f..c6e59a5f567c 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -220,6 +220,7 @@ java_library_static { "securebox", "apache-commons-math", "battery_saver_flag_lib", + "guava", "notification_flags_lib", "power_hint_flags_lib", "biometrics_flags_lib", diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java index 65d0ab337400..2ce03aee2f41 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityService.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java @@ -35,6 +35,9 @@ import android.util.Log; import com.android.server.SystemService; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + import org.json.JSONException; import org.json.JSONObject; @@ -43,6 +46,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; +import java.util.UUID; /** * This service manage picture profile and sound profile for TV setting. Also communicates with the @@ -54,10 +58,12 @@ public class MediaQualityService extends SystemService { private static final String TAG = "MediaQualityService"; private final Context mContext; private final MediaQualityDbHelper mMediaQualityDbHelper; + private final BiMap mTempIdMap; public MediaQualityService(Context context) { super(context); mContext = context; + mTempIdMap = HashBiMap.create(); mMediaQualityDbHelper = new MediaQualityDbHelper(mContext); mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true); mMediaQualityDbHelper.setIdleConnectionTimeout(30); @@ -83,8 +89,21 @@ public class MediaQualityService extends SystemService { values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters())); // id is auto-generated by SQLite upon successful insertion of row - long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values); - return new PictureProfile.Builder(pp).setProfileId(Long.toString(id)).build(); + Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, + null, values); + populateTempIdMap(id); + pp.setProfileId(mTempIdMap.get(id)); + return pp; + } + + private void populateTempIdMap(Long id) { + if (id != null && mTempIdMap.get(id) == null) { + String uuid = UUID.randomUUID().toString(); + while (mTempIdMap.inverse().containsKey(uuid)) { + uuid = UUID.randomUUID().toString(); + } + mTempIdMap.put(id, uuid); + } } @Override @@ -94,7 +113,15 @@ public class MediaQualityService extends SystemService { @Override public void removePictureProfile(String id, int userId) { - // TODO: implement + Long intId = mTempIdMap.inverse().get(id); + if (intId != null) { + SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); + String selection = BaseParameters.PARAMETER_ID + " = ?"; + String[] selectionArgs = {Long.toString(intId)}; + db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection, + selectionArgs); + mTempIdMap.remove(intId); + } } @Override @@ -126,7 +153,7 @@ public class MediaQualityService extends SystemService { return null; } cursor.moveToFirst(); - return getPictureProfileFromCursor(cursor); + return getPictureProfileWithTempIdFromCursor(cursor); } } @@ -187,20 +214,28 @@ public class MediaQualityService extends SystemService { }; } - private PictureProfile getPictureProfileFromCursor(Cursor cursor) { - String returnId = cursor.getString(cursor.getColumnIndexOrThrow( - BaseParameters.PARAMETER_ID)); - int type = cursor.getInt(cursor.getColumnIndexOrThrow( - BaseParameters.PARAMETER_TYPE)); - String name = cursor.getString(cursor.getColumnIndexOrThrow( - BaseParameters.PARAMETER_NAME)); - String inputId = cursor.getString(cursor.getColumnIndexOrThrow( - BaseParameters.PARAMETER_INPUT_ID)); - String packageName = cursor.getString(cursor.getColumnIndexOrThrow( - BaseParameters.PARAMETER_PACKAGE)); - String settings = cursor.getString( - cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS)); - return new PictureProfile(returnId, type, name, inputId, + private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) { + int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID); + Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null; + populateTempIdMap(dbId); + String tempId = mTempIdMap.get(dbId); + + colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE); + int type = colIndex != -1 ? cursor.getInt(colIndex) : 0; + + colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME); + String name = colIndex != -1 ? cursor.getString(colIndex) : null; + + colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID); + String inputId = colIndex != -1 ? cursor.getString(colIndex) : null; + + colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE); + String packageName = colIndex != -1 ? cursor.getString(colIndex) : null; + + colIndex = cursor.getColumnIndex(mMediaQualityDbHelper.SETTINGS); + String settings = colIndex != -1 ? cursor.getString(colIndex) : null; + + return new PictureProfile(tempId, type, name, inputId, packageName, jsonToBundle(settings)); } @@ -243,7 +278,7 @@ public class MediaQualityService extends SystemService { ) { List pictureProfiles = new ArrayList<>(); while (cursor.moveToNext()) { - pictureProfiles.add(getPictureProfileFromCursor(cursor)); + pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor)); } return pictureProfiles; } -- GitLab From 2c1a837847e58fef587c2911b242be7ce70ff138 Mon Sep 17 00:00:00 2001 From: Preethi Kandhalu Date: Tue, 19 Nov 2024 16:10:09 -0800 Subject: [PATCH 056/652] [Media Quality] Add Temp Id Map to Sound Profile and refactor Test: logs and CTS Flag: android.media.tv.flags.media_quality_fw Bug: 378708165 Change-Id: I962a88849934cee3eb3da7e5ddbb3235e79b25d8 --- .../media/quality/MediaQualityService.java | 368 +++++++++--------- 1 file changed, 186 insertions(+), 182 deletions(-) diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java index 2ce03aee2f41..14eeb3dc78f8 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityService.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java @@ -58,12 +58,14 @@ public class MediaQualityService extends SystemService { private static final String TAG = "MediaQualityService"; private final Context mContext; private final MediaQualityDbHelper mMediaQualityDbHelper; - private final BiMap mTempIdMap; + private final BiMap mPictureProfileTempIdMap; + private final BiMap mSoundProfileTempIdMap; public MediaQualityService(Context context) { super(context); mContext = context; - mTempIdMap = HashBiMap.create(); + mPictureProfileTempIdMap = HashBiMap.create(); + mSoundProfileTempIdMap = HashBiMap.create(); mMediaQualityDbHelper = new MediaQualityDbHelper(mContext); mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true); mMediaQualityDbHelper.setIdleConnectionTimeout(30); @@ -86,26 +88,16 @@ public class MediaQualityService extends SystemService { values.put(BaseParameters.PARAMETER_NAME, pp.getName()); values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName()); values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId()); - values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters())); + values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(pp.getParameters())); // id is auto-generated by SQLite upon successful insertion of row Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values); - populateTempIdMap(id); - pp.setProfileId(mTempIdMap.get(id)); + populateTempIdMap(mPictureProfileTempIdMap, id); + pp.setProfileId(mPictureProfileTempIdMap.get(id)); return pp; } - private void populateTempIdMap(Long id) { - if (id != null && mTempIdMap.get(id) == null) { - String uuid = UUID.randomUUID().toString(); - while (mTempIdMap.inverse().containsKey(uuid)) { - uuid = UUID.randomUUID().toString(); - } - mTempIdMap.put(id, uuid); - } - } - @Override public void updatePictureProfile(String id, PictureProfile pp, int userId) { // TODO: implement @@ -113,34 +105,27 @@ public class MediaQualityService extends SystemService { @Override public void removePictureProfile(String id, int userId) { - Long intId = mTempIdMap.inverse().get(id); + Long intId = mPictureProfileTempIdMap.inverse().get(id); if (intId != null) { SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); String selection = BaseParameters.PARAMETER_ID + " = ?"; String[] selectionArgs = {Long.toString(intId)}; db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection, selectionArgs); - mTempIdMap.remove(intId); + mPictureProfileTempIdMap.remove(intId); } } @Override public PictureProfile getPictureProfile(int type, String name, int userId) { - SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase(); - String selection = BaseParameters.PARAMETER_TYPE + " = ? AND " + BaseParameters.PARAMETER_NAME + " = ?"; String[] selectionArguments = {Integer.toString(type), name}; try ( - Cursor cursor = db.query( + Cursor cursor = getCursorAfterQuerying( mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, - getAllPictureProfileColumns(), - selection, - selectionArguments, - /*groupBy=*/ null, - /*having=*/ null, - /*orderBy=*/ null) + getAllMediaProfileColumns(), selection, selectionArguments) ) { int count = cursor.getCount(); if (count == 0) { @@ -149,7 +134,7 @@ public class MediaQualityService extends SystemService { if (count > 1) { Log.wtf(TAG, String.format(Locale.US, "%d entries found for type=%d and name=%s" + " in %s. Should only ever be 0 or 1.", count, type, name, - mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME)); + mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME)); return null; } cursor.moveToFirst(); @@ -157,93 +142,11 @@ public class MediaQualityService extends SystemService { } } - private String bundleToJson(PersistableBundle bundle) { - JSONObject jsonObject = new JSONObject(); - if (bundle == null) { - return jsonObject.toString(); - } - for (String key : bundle.keySet()) { - try { - jsonObject.put(key, bundle.getString(key)); - } catch (JSONException e) { - Log.e(TAG, "Unable to serialize ", e); - } - } - return jsonObject.toString(); - } - - private PersistableBundle jsonToBundle(String jsonString) { - JSONObject jsonObject = null; - PersistableBundle bundle = new PersistableBundle(); - - try { - jsonObject = new JSONObject(jsonString); - - Iterator keys = jsonObject.keys(); - while (keys.hasNext()) { - String key = keys.next(); - Object value = jsonObject.get(key); - - if (value instanceof String) { - bundle.putString(key, (String) value); - } else if (value instanceof Integer) { - bundle.putInt(key, (Integer) value); - } else if (value instanceof Boolean) { - bundle.putBoolean(key, (Boolean) value); - } else if (value instanceof Double) { - bundle.putDouble(key, (Double) value); - } else if (value instanceof Long) { - bundle.putLong(key, (Long) value); - } - } - } catch (JSONException e) { - throw new RuntimeException(e); - } - - return bundle; - } - - private String[] getAllPictureProfileColumns() { - return new String[]{ - BaseParameters.PARAMETER_ID, - BaseParameters.PARAMETER_TYPE, - BaseParameters.PARAMETER_NAME, - BaseParameters.PARAMETER_INPUT_ID, - BaseParameters.PARAMETER_PACKAGE, - mMediaQualityDbHelper.SETTINGS - }; - } - - private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) { - int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID); - Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null; - populateTempIdMap(dbId); - String tempId = mTempIdMap.get(dbId); - - colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE); - int type = colIndex != -1 ? cursor.getInt(colIndex) : 0; - - colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME); - String name = colIndex != -1 ? cursor.getString(colIndex) : null; - - colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID); - String inputId = colIndex != -1 ? cursor.getString(colIndex) : null; - - colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE); - String packageName = colIndex != -1 ? cursor.getString(colIndex) : null; - - colIndex = cursor.getColumnIndex(mMediaQualityDbHelper.SETTINGS); - String settings = colIndex != -1 ? cursor.getString(colIndex) : null; - - return new PictureProfile(tempId, type, name, inputId, - packageName, jsonToBundle(settings)); - } - @Override public List getPictureProfilesByPackage(String packageName, int userId) { String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; String[] selectionArguments = {packageName}; - return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection, + return getPictureProfilesBasedOnConditions(getAllMediaProfileColumns(), selection, selectionArguments); } @@ -254,36 +157,15 @@ public class MediaQualityService extends SystemService { @Override public List getPictureProfilePackageNames(int userId) { - String [] column = {BaseParameters.PARAMETER_NAME}; + String [] column = {BaseParameters.PARAMETER_PACKAGE}; List pictureProfiles = getPictureProfilesBasedOnConditions(column, null, null); return pictureProfiles.stream() - .map(PictureProfile::getName) + .map(PictureProfile::getPackageName) + .distinct() .collect(Collectors.toList()); } - private List getPictureProfilesBasedOnConditions(String[] columns, - String selection, String[] selectionArguments) { - SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase(); - - try ( - Cursor cursor = db.query( - mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, - columns, - selection, - selectionArguments, - /*groupBy=*/ null, - /*having=*/ null, - /*orderBy=*/ null) - ) { - List pictureProfiles = new ArrayList<>(); - while (cursor.moveToNext()) { - pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor)); - } - return pictureProfiles; - } - } - @Override public PictureProfileHandle getPictureProfileHandle(String id, int userId) { return null; @@ -294,13 +176,18 @@ public class MediaQualityService extends SystemService { SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); + values.put(BaseParameters.PARAMETER_TYPE, sp.getProfileType()); values.put(BaseParameters.PARAMETER_NAME, sp.getName()); values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName()); values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId()); - values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(sp.getParameters())); + values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(sp.getParameters())); - long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values); - return new SoundProfile.Builder(sp).setProfileId(Long.toString(id)).build(); + // id is auto-generated by SQLite upon successful insertion of row + Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, + null, values); + populateTempIdMap(mSoundProfileTempIdMap, id); + sp.setProfileId(mSoundProfileTempIdMap.get(id)); + return sp; } @Override @@ -310,28 +197,27 @@ public class MediaQualityService extends SystemService { @Override public void removeSoundProfile(String id, int userId) { - SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); - String selection = BaseParameters.PARAMETER_ID + " = ?"; - String[] selectionArgs = {id}; - db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, selectionArgs); + Long intId = mSoundProfileTempIdMap.inverse().get(id); + if (intId != null) { + SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); + String selection = BaseParameters.PARAMETER_ID + " = ?"; + String[] selectionArgs = {Long.toString(intId)}; + db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, + selectionArgs); + mSoundProfileTempIdMap.remove(intId); + } } @Override public SoundProfile getSoundProfile(int type, String id, int userId) { - SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase(); - - String selection = BaseParameters.PARAMETER_ID + " = ?"; - String[] selectionArguments = {id}; + String selection = BaseParameters.PARAMETER_TYPE + " = ? AND " + + BaseParameters.PARAMETER_NAME + " = ?"; + String[] selectionArguments = {String.valueOf(type), id}; try ( - Cursor cursor = db.query( + Cursor cursor = getCursorAfterQuerying( mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, - getAllSoundProfileColumns(), - selection, - selectionArguments, - /*groupBy=*/ null, - /*having=*/ null, - /*orderBy=*/ null) + getAllMediaProfileColumns(), selection, selectionArguments) ) { int count = cursor.getCount(); if (count == 0) { @@ -344,7 +230,7 @@ public class MediaQualityService extends SystemService { return null; } cursor.moveToFirst(); - return getSoundProfileFromCursor(cursor); + return getSoundProfileWithTempIdFromCursor(cursor); } } @@ -352,7 +238,7 @@ public class MediaQualityService extends SystemService { public List getSoundProfilesByPackage(String packageName, int userId) { String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; String[] selectionArguments = {packageName}; - return getSoundProfilesBasedOnConditions(getAllSoundProfileColumns(), selection, + return getSoundProfilesBasedOnConditions(getAllMediaProfileColumns(), selection, selectionArguments); } @@ -367,13 +253,79 @@ public class MediaQualityService extends SystemService { List soundProfiles = getSoundProfilesBasedOnConditions(column, null, null); return soundProfiles.stream() - .map(SoundProfile::getName) + .map(SoundProfile::getPackageName) + .distinct() .collect(Collectors.toList()); } - private String[] getAllSoundProfileColumns() { + private void populateTempIdMap(BiMap map, Long id) { + if (id != null && map.get(id) == null) { + String uuid = UUID.randomUUID().toString(); + while (map.inverse().containsKey(uuid)) { + uuid = UUID.randomUUID().toString(); + } + map.put(id, uuid); + } + } + + private String persistableBundleToJson(PersistableBundle bundle) { + JSONObject json = new JSONObject(); + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + try { + if (value instanceof String) { + json.put(key, bundle.getString(key)); + } else if (value instanceof Integer) { + json.put(key, bundle.getInt(key)); + } else if (value instanceof Long) { + json.put(key, bundle.getLong(key)); + } else if (value instanceof Boolean) { + json.put(key, bundle.getBoolean(key)); + } else if (value instanceof Double) { + json.put(key, bundle.getDouble(key)); + } + } catch (JSONException e) { + Log.e(TAG, "Unable to serialize ", e); + } + } + return json.toString(); + } + + private PersistableBundle jsonToBundle(String jsonString) { + PersistableBundle bundle = new PersistableBundle(); + if (jsonString != null) { + JSONObject jsonObject = null; + try { + jsonObject = new JSONObject(jsonString); + + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = jsonObject.get(key); + + if (value instanceof String) { + bundle.putString(key, (String) value); + } else if (value instanceof Integer) { + bundle.putInt(key, (Integer) value); + } else if (value instanceof Boolean) { + bundle.putBoolean(key, (Boolean) value); + } else if (value instanceof Double) { + bundle.putDouble(key, (Double) value); + } else if (value instanceof Long) { + bundle.putLong(key, (Long) value); + } + } + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return bundle; + } + + private String[] getAllMediaProfileColumns() { return new String[]{ BaseParameters.PARAMETER_ID, + BaseParameters.PARAMETER_TYPE, BaseParameters.PARAMETER_NAME, BaseParameters.PARAMETER_INPUT_ID, BaseParameters.PARAMETER_PACKAGE, @@ -381,40 +333,92 @@ public class MediaQualityService extends SystemService { }; } - private SoundProfile getSoundProfileFromCursor(Cursor cursor) { - String returnId = cursor.getString( - cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_ID)); - int type = cursor.getInt( - cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_TYPE)); - String name = cursor.getString( - cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_NAME)); - String inputId = cursor.getString( - cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_INPUT_ID)); - String packageName = cursor.getString( - cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_PACKAGE)); - String settings = cursor.getString( - cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS)); - return new SoundProfile(returnId, type, name, inputId, packageName, - jsonToBundle(settings)); + private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) { + return new PictureProfile( + getTempId(mPictureProfileTempIdMap, cursor), + getType(cursor), + getName(cursor), + getInputId(cursor), + getPackageName(cursor), + jsonToBundle(getSettingsString(cursor)) + ); + } + + private SoundProfile getSoundProfileWithTempIdFromCursor(Cursor cursor) { + return new SoundProfile( + getTempId(mSoundProfileTempIdMap, cursor), + getType(cursor), + getName(cursor), + getInputId(cursor), + getPackageName(cursor), + jsonToBundle(getSettingsString(cursor)) + ); + } + + private String getTempId(BiMap map, Cursor cursor) { + int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID); + Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null; + populateTempIdMap(map, dbId); + return map.get(dbId); } - private List getSoundProfilesBasedOnConditions(String[] columns, - String selection, String[] selectionArguments) { + private int getType(Cursor cursor) { + int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE); + return colIndex != -1 ? cursor.getInt(colIndex) : 0; + } + + private String getName(Cursor cursor) { + int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME); + return colIndex != -1 ? cursor.getString(colIndex) : null; + } + + private String getInputId(Cursor cursor) { + int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID); + return colIndex != -1 ? cursor.getString(colIndex) : null; + } + + private String getPackageName(Cursor cursor) { + int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE); + return colIndex != -1 ? cursor.getString(colIndex) : null; + } + + private String getSettingsString(Cursor cursor) { + int colIndex = cursor.getColumnIndex(mMediaQualityDbHelper.SETTINGS); + return colIndex != -1 ? cursor.getString(colIndex) : null; + } + + private Cursor getCursorAfterQuerying(String table, String[] columns, String selection, + String[] selectionArgs) { SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase(); + return db.query(table, columns, selection, selectionArgs, + /*groupBy=*/ null, /*having=*/ null, /*orderBy=*/ null); + } + private List getPictureProfilesBasedOnConditions(String[] columns, + String selection, String[] selectionArguments) { try ( - Cursor cursor = db.query( - mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, - columns, - selection, - selectionArguments, - /*groupBy=*/ null, - /*having=*/ null, - /*orderBy=*/ null) + Cursor cursor = getCursorAfterQuerying( + mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, columns, selection, + selectionArguments) + ) { + List pictureProfiles = new ArrayList<>(); + while (cursor.moveToNext()) { + pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor)); + } + return pictureProfiles; + } + } + + private List getSoundProfilesBasedOnConditions(String[] columns, + String selection, String[] selectionArguments) { + try ( + Cursor cursor = getCursorAfterQuerying( + mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, columns, selection, + selectionArguments) ) { List soundProfiles = new ArrayList<>(); while (cursor.moveToNext()) { - soundProfiles.add(getSoundProfileFromCursor(cursor)); + soundProfiles.add(getSoundProfileWithTempIdFromCursor(cursor)); } return soundProfiles; } -- GitLab From 4e5754eb549f417430a1b611b0e418f2b88df831 Mon Sep 17 00:00:00 2001 From: Marcelo Arteiro Date: Mon, 25 Nov 2024 13:17:00 +0000 Subject: [PATCH 057/652] Migrates Monet's Style Enum to @IntDef This is part of the effort to move all Color System depenencies to server. Enum are not allowerd there. Bug: 335429258 Test: None Flag: EXEMPT rearchitecture Change-Id: I24f22e3057d81ba32c9e66997b15bbbd75025cc3 --- .../android/systemui/shared/clocks/ClockDesign.kt | 2 +- .../systemui/theme/ThemeOverlayControllerTest.java | 8 ++++---- .../keyguard/ui/preview/KeyguardPreviewRenderer.kt | 7 +++++-- .../media/controls/ui/util/MediaArtworkHelper.kt | 6 +++++- .../systemui/theme/ThemeOverlayController.java | 12 +++++++----- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockDesign.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockDesign.kt index bcf055bd2c40..15373d354ef6 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockDesign.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockDesign.kt @@ -33,7 +33,7 @@ data class ClockDesign( val thumbnail: String? = null, val large: ClockFace? = null, val small: ClockFace? = null, - val colorPalette: MonetStyle? = null, + @MonetStyle.Type val colorPalette: Int? = null, ) /** Describes a clock using layers */ diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index e7ca1dd3e4b7..7b52dd836b51 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -434,13 +434,13 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { @Test public void onSettingChanged_honorThemeStyle() { when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true); - List - - -- GitLab From 3c2cb40abc5508b8fe97a8ea96dafd173a9d7d4a Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Mon, 2 Dec 2024 12:59:42 +0100 Subject: [PATCH 143/652] SilkFX: Migrate layouts to display edge-to-edge Bug: 309578419 Test: Manual (open each of the screens and check layout hierarchy) Flag: com.android.window.flags.disable_opt_out_edge_to_edge Change-Id: Ie4736d2602d0588199252e28055a42fba212fc2a --- .../res/layout/activity_background_blur.xml | 277 +++++++++--------- .../SilkFX/res/layout/activity_glass.xml | 3 +- .../SilkFX/res/layout/color_mode_controls.xml | 3 +- .../SilkFX/res/layout/common_base.xml | 3 +- .../graphics/SilkFX/res/layout/hdr_glows.xml | 3 +- tests/graphics/SilkFX/res/values/style.xml | 2 +- .../src/com/android/test/silkfx/Main.kt | 1 + 7 files changed, 152 insertions(+), 140 deletions(-) diff --git a/tests/graphics/SilkFX/res/layout/activity_background_blur.xml b/tests/graphics/SilkFX/res/layout/activity_background_blur.xml index f13c0883cb01..27eca82dcb23 100644 --- a/tests/graphics/SilkFX/res/layout/activity_background_blur.xml +++ b/tests/graphics/SilkFX/res/layout/activity_background_blur.xml @@ -13,161 +13,168 @@ ~ 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:layout_gravity="center" + android:padding="15dp" + android:orientation="vertical"> - - - - + android:text="Hello blurry world!"/> - - - - - + android:orientation="horizontal"> + - - - - - + + + + android:orientation="horizontal"> + - - + + + - - - + android:orientation="horizontal"> + -