From ec361daff7059b32cd5fd37f44cf1d6fc3cbe6c4 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Tue, 10 Aug 2021 01:39:40 +0800 Subject: [PATCH 01/12] Improve InsetsPolicy#adjustVisibilityForIme when switching apps When switching tasks from the task has IME shown to another task without showing IME (e.g. youtube apps.), the flickering might happen because the next activity accidentally received the IME insets attached in the previous activity. If the IME is attached to one app window on that time, even the next app window is behind the IME window, conceptually the window should not receive the IME insets if the next window is not eligible IME requester and ready to show IME on top of it. Previously, we introduced CL[1] with checking task animating state and check if the window has request IME visiblity to judge if the next focus window is eligible to get IME insets. However, CL[1] overlooked that some apps design may using a child window to receive IME insets but not the IME requester (the requester is activity main window), so makes the regression that the child window may not able to receive IME to adjust editor layout on the main window like Bug 195385541 and Bug 195846009. As the result, in this CL we introduce another way to fix this improper IME insets delivering. The fix way is straight forward. when the task with showing IME is leaving, we'll set a flag to froze IME insets state for all windows until the next task's IME input target has focused and reprorted to WM site. As this fix approach could be more simple and preversing the original insets behavior. Refine the logic in InsetsPolicy#adjustVisibilityForIme with the above approach. [1]: Ib3a1eacedd3763bcf1e6eed82e7efdf01c50b204 Fix: 192337037 Test: atest WindowStateTests#\ testAdjustImeInsetsVisibilityWhenSwitchingApps Test: atest ActivityRecordTests Test: manual as below steps: 1) Switch tasks with the task has IME shown (e.g. Android Message) and youtube apps. 2) Expect youtube no flickering when switched the task from Android Message. Change-Id: Id8a56c68a06a774ef12ba444fd9a368148ea4539 Merged-In: Id8a56c68a06a774ef12ba444fd9a368148ea4539 (cherry picked from commit 4950787a4313d8c0fa6685a9f3dbfb0b05671cf0) --- .../com/android/server/wm/ActivityRecord.java | 23 +++++++ .../com/android/server/wm/DisplayContent.java | 3 + .../com/android/server/wm/InsetsPolicy.java | 41 ++++++++--- .../server/wm/ActivityRecordTests.java | 68 +++++++++++++++++++ .../server/wm/SystemServicesTestRule.java | 1 + .../android/server/wm/WindowStateTests.java | 34 ++++++++++ 6 files changed, 160 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 308df2f4e9a00..c2cfe0b7bd927 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -655,6 +655,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Whether the IME is showing when transitioning away from this activity. */ boolean mLastImeShown; + /** + * When set to true, the IME insets will be frozen until the next app becomes IME input target. + * @see InsetsPolicy#adjustVisibilityForIme + */ + boolean mImeInsetsFrozenUntilStartInput; + /** * A flag to determine if this AR is in the process of closing or entering PIP. This is needed * to help AR know that the app is in the process of closing but hasn't yet started closing on @@ -1363,6 +1369,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (newTask != null && isState(RESUMED)) { newTask.setResumedActivity(this, "onParentChanged"); + mImeInsetsFrozenUntilStartInput = false; } if (rootTask != null && rootTask.topRunningActivity() == this) { @@ -4759,6 +4766,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && imeInputTarget.getWindow().mActivityRecord == this && mDisplayContent.mInputMethodWindow != null && mDisplayContent.mInputMethodWindow.isVisible(); + mImeInsetsFrozenUntilStartInput = true; } final DisplayContent displayContent = getDisplayContent(); @@ -5877,6 +5885,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // closing activity having to wait until idle timeout to be stopped or destroyed if the // next activity won't report idle (e.g. repeated view animation). mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); + + // If the activity is visible, but no windows are eligible to start input, unfreeze + // to avoid permanently frozen IME insets. + if (mImeInsetsFrozenUntilStartInput && getWindow( + win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags)) + == null) { + mImeInsetsFrozenUntilStartInput = false; + } } } @@ -7792,6 +7808,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + @Override + void onResize() { + // Reset freezing IME insets flag when the activity resized. + mImeInsetsFrozenUntilStartInput = false; + super.onResize(); + } + /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { final int orientation = getRequestedOrientation(); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index dbc1116ad3890..ca696b71393a5 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3971,6 +3971,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateImeInputAndControlTarget(WindowState target) { if (mImeInputTarget != target) { ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target); + if (target != null && target.mActivityRecord != null) { + target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false; + } setImeInputTarget(target); updateImeControlTarget(); } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index f2f192686ad55..a8e1c1cda72bd 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -211,7 +211,7 @@ class InsetsPolicy { InsetsState getInsetsForWindow(WindowState target) { final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState state = adjustVisibilityForTransientTypes(originalState); - return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state; + return adjustVisibilityForIme(target, state, state == originalState); } /** @@ -241,16 +241,37 @@ class InsetsPolicy { return state; } - // Navigation bar insets is always visible to IME. - private static InsetsState adjustVisibilityForIme(InsetsState originalState, + private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState) { - final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); - if (originalNavSource != null && !originalNavSource.isVisible()) { - final InsetsState state = copyState ? new InsetsState(originalState) : originalState; - final InsetsSource navSource = new InsetsSource(originalNavSource); - navSource.setVisible(true); - state.addSource(navSource); - return state; + if (w.mIsImWindow) { + // Navigation bar insets is always visible to IME. + final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); + if (originalNavSource != null && !originalNavSource.isVisible()) { + final InsetsState state = copyState ? new InsetsState(originalState) + : originalState; + final InsetsSource navSource = new InsetsSource(originalNavSource); + navSource.setVisible(true); + state.addSource(navSource); + return state; + } + } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { + // During switching tasks with gestural navigation, if the IME is attached to + // one app window on that time, even the next app window is behind the IME window, + // conceptually the window should not receive the IME insets if the next window is + // not eligible IME requester and ready to show IME on top of it. + final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); + final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); + + if (shouldImeAttachedToApp && originalImeSource != null) { + final boolean imeVisibility = + w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); + final InsetsState state = copyState ? new InsetsState(originalState) + : originalState; + final InsetsSource imeSource = new InsetsSource(originalImeSource); + imeSource.setVisible(imeVisibility); + state.addSource(imeSource); + return state; + } } return originalState; } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 293e862a6b74c..6f04f176afd86 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -39,6 +39,7 @@ import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -2816,6 +2817,73 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); } + @Test + public void testImeInsetsFrozenFlag_resetWhenReparented() { + final ActivityRecord activity = createActivityWithTask(); + final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app"); + final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + final Task newTask = new TaskBuilder(mSupervisor).build(); + makeWindowVisible(app, imeWindow); + mDisplayContent.mInputMethodWindow = imeWindow; + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity is reparent to the new task. + activity.setState(RESUMED, "test"); + activity.reparent(newTask, 0 /* top */, "test"); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testImeInsetsFrozenFlag_resetWhenResized() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + makeWindowVisibleAndDrawn(app, mImeWindow); + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity is reparent to the new task. + app.mActivityRecord.onResize(); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + makeWindowVisibleAndDrawn(app, mImeWindow); + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + app.mActivityRecord.onWindowsGone(); + + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity has no IME focusable window. + app.mActivityRecord.forAllWindowsUnchecked(w -> { + w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; + return true; + }, true); + + app.mActivityRecord.commitVisibility(true, false); + app.mActivityRecord.onWindowsVisible(); + + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + private void assertHasStartingWindow(ActivityRecord atoken) { assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index a8e17534e6ed7..8e7ba4bc3293f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule { final ActivityManagerInternal amInternal = mAmService.mInternal; spyOn(amInternal); doNothing().when(amInternal).trimApplications(); + doNothing().when(amInternal).scheduleAppGcs(); doNothing().when(amInternal).updateCpuStats(); doNothing().when(amInternal).updateOomAdj(); doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 92b670ed96995..d88ac256be5ca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -891,6 +891,40 @@ public class WindowStateTests extends WindowTestsBase { assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); } + @Test + public void testAdjustImeInsetsVisibilityWhenSwitchingApps() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); + final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + spyOn(imeWindow); + doReturn(true).when(imeWindow).isVisible(); + mDisplayContent.mInputMethodWindow = imeWindow; + + final InsetsStateController controller = mDisplayContent.getInsetsStateController(); + controller.getImeSourceProvider().setWindow(imeWindow, null, null); + + // Simulate app requests IME with updating all windows Insets State when IME is above app. + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + assertTrue(mDisplayContent.shouldImeAttachedToApp()); + controller.getImeSourceProvider().scheduleShowImePostLayout(app); + controller.getImeSourceProvider().getSource().setVisible(true); + controller.updateAboveInsetsState(imeWindow, false); + + // Expect all app windows behind IME can receive IME insets visible. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertTrue(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + + // Simulate app plays closing transition to app2. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Verify the IME insets is visible on app, but not for app2 during app task switching. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + } + @UseTestDisplay(addWindows = { W_ACTIVITY }) @Test public void testUpdateImeControlTargetWhenLeavingMultiWindow() { From 58334b0b04e79d17c27c8fa1d261c3d53208c6ed Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Mon, 9 Aug 2021 15:15:28 -0700 Subject: [PATCH 02/12] Notification playback: pause on release When releasing a MediaPlayer outside of a playback completion (back to back notifications), pause and wait 100ms before releasing the player. Bug: 193832902 Test: create back-to-back notifications, check for glitch Change-Id: Ib8baba4bf2b41e75b526e8490b6c2f31c85c193c (cherry picked from commit c69743ea169f8e0c87ecb48d6d9e81a1286605ee) --- media/java/android/media/PlayerBase.java | 7 +++++++ .../systemui/media/NotificationPlayer.java | 20 ++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 86ed50bacb632..72ee00f03774d 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -102,6 +102,13 @@ public abstract class PlayerBase { mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE; }; + /** @hide */ + public int getPlayerIId() { + synchronized (mLock) { + return mPlayerIId; + } + } + /** * Call from derived class when instantiation / initialization is successful */ diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index 89786ee880ad5..a617850ef0ae8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -139,7 +139,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener + " with ducking", e); } player.start(); - if (DEBUG) { Log.d(mTag, "player.start"); } + if (DEBUG) { Log.d(mTag, "player.start piid:" + player.getPlayerIId()); } } catch (Exception e) { if (player != null) { player.release(); @@ -155,7 +155,13 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener mPlayer = player; } if (mp != null) { - if (DEBUG) { Log.d(mTag, "mPlayer.release"); } + if (DEBUG) { + Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId()); + } + mp.pause(); + try { + Thread.sleep(100); + } catch (InterruptedException ie) { } mp.release(); } this.notify(); @@ -244,6 +250,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener try { mp.stop(); } catch (Exception e) { } + if (DEBUG) { + Log.i(mTag, "About to release MediaPlayer piid:" + + mp.getPlayerIId() + " due to notif cancelled"); + } mp.release(); synchronized(mQueueAudioFocusLock) { if (mAudioManagerWithAudioFocus != null) { @@ -284,7 +294,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener public void onCompletion(MediaPlayer mp) { synchronized(mQueueAudioFocusLock) { if (mAudioManagerWithAudioFocus != null) { - if (DEBUG) Log.d(mTag, "onCompletion() abandonning AudioFocus"); + if (DEBUG) Log.d(mTag, "onCompletion() abandoning AudioFocus"); mAudioManagerWithAudioFocus.abandonAudioFocus(null); mAudioManagerWithAudioFocus = null; } else { @@ -310,6 +320,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener } } if (mp != null) { + if (DEBUG) { + Log.i("NotificationPlayer", "About to release MediaPlayer piid:" + + mp.getPlayerIId() + " due to onCompletion"); + } mp.release(); } } From d4fcdd2839848082fa86b16bc2dece39fd7ef485 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Thu, 26 Aug 2021 18:31:01 +0800 Subject: [PATCH 03/12] Fix IME flickering when swiping out to home When switching tasks between different users (i.e. primary and work-profile user), if the source task has shown IME, when going to the target task, IMMS will first unbind the current input method of the current user and hide the soft-keyboard and the window token by HIDE_SWITCH_USER reason, and then re-bind the input method of the target user when focused to the target task. If the hide soft-keyboard somehow being delayed and comes after the target task has been focused and be the next IME target, user will see the window removal transition on top of this IME target window. (i.e. launching Google chat apps with IME shown from work-profile and then swiping out to home screen) Even if we disable window animation in Activity#onDestory in IME client side, the next relayout can come before that, so window exit transition still happens. To fix this timing issue, add animateExit parameter in WindowManagerInternal#removeToken for IMMS not to perform window exit animation and to hide the window surface permanentaly, so we can avoid the window surface becoming visible again unexpectedly during relayout. Fix: 197494607 Fix: 195449326 Test: atest WindowTokenTests#\ testRemoveWindowToken_noAnimateExitWhenSet Test: manual as issue steps: 1. Enter Google chat 2. Start typing a message 3. Swipe home 4. Expect no IME surface flicker happens Merged-In: I044257b5ac0d92dd06dae8eba996ea0ac6f1e659 Change-Id: I044257b5ac0d92dd06dae8eba996ea0ac6f1e659 (cherry picked from commit 0b44f104b3893877960f7a9a41927911469ae563) --- .../InputMethodManagerService.java | 12 ++-- .../com/android/server/wm/DisplayContent.java | 6 +- .../server/wm/WallpaperWindowToken.java | 4 +- .../server/wm/WindowManagerInternal.java | 15 +++- .../server/wm/WindowManagerService.java | 69 ++++++++----------- .../com/android/server/wm/WindowState.java | 13 +++- .../com/android/server/wm/WindowToken.java | 10 +-- .../android/server/wm/WindowTokenTests.java | 29 +++++++- 8 files changed, 98 insertions(+), 60 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index dc955337fdbc5..3e52f5e07e62e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2582,14 +2582,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurToken != null) { - try { - if (DEBUG) { - Slog.v(TAG, "Removing window token: " + mCurToken + " for display: " - + mCurTokenDisplayId); - } - mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId); - } catch (RemoteException e) { + if (DEBUG) { + Slog.v(TAG, "Removing window token: " + mCurToken + " for display: " + + mCurTokenDisplayId); } + mWindowManagerInternal.removeWindowToken(mCurToken, false /* removeWindows */, + false /* animateExit */, mCurTokenDisplayId); // Set IME window status as invisible when unbind current method. mImeWindowVis = 0; mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ca696b71393a5..9335846e7805b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1165,10 +1165,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - WindowToken removeWindowToken(IBinder binder) { + WindowToken removeWindowToken(IBinder binder, boolean animateExit) { final WindowToken token = mTokenMap.remove(binder); if (token != null && token.asActivityRecord() == null) { - token.setExiting(); + token.setExiting(animateExit); } return token; } @@ -1252,7 +1252,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void removeAppToken(IBinder binder) { - final WindowToken token = removeWindowToken(binder); + final WindowToken token = removeWindowToken(binder, true /* animateExit */); if (token == null) { Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder); return; diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 194f48f57cc42..b54e8b7a7b4e9 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -66,8 +66,8 @@ class WallpaperWindowToken extends WindowToken { } @Override - void setExiting() { - super.setExiting(); + void setExiting(boolean animateExit) { + super.setExiting(animateExit); mDisplayContent.mWallpaperController.removeWallpaperToken(this); } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 47087cfbd147b..4fac05c349c58 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -445,8 +445,21 @@ public abstract class WindowManagerInternal { * @param removeWindows Whether to also remove the windows associated with the token. * @param displayId The display to remove the token from. */ + public final void removeWindowToken(android.os.IBinder token, boolean removeWindows, + int displayId) { + removeWindowToken(token, removeWindows, true /* animateExit */, displayId); + } + + /** + * Removes a window token. + * + * @param token The toke to remove. + * @param removeWindows Whether to also remove the windows associated with the token. + * @param animateExit Whether to play the windows exit animation after the token removal. + * @param displayId The display to remove the token from. + */ public abstract void removeWindowToken(android.os.IBinder token, boolean removeWindows, - int displayId); + boolean animateExit, int displayId); /** * Registers a listener to be notified about app transition events. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1ec9187d7a76f..9caef70f6b51a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2816,6 +2816,31 @@ public class WindowManagerService extends IWindowManager.Stub } + void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + + if (dc == null) { + ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" + + " for non-exiting displayId=%d", binder, displayId); + return; + } + final WindowToken token = dc.removeWindowToken(binder, animateExit); + if (token == null) { + ProtoLog.w(WM_ERROR, + "removeWindowToken: Attempted to remove non-existing token: %s", + binder); + return; + } + + if (removeWindows) { + token.removeAllWindowsIfPossible(); + } + dc.getInputMonitor().updateInputWindowsLw(true /* force */); + } + } + @Override public void removeWindowToken(IBinder binder, int displayId) { if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { @@ -2823,23 +2848,7 @@ public class WindowManagerService extends IWindowManager.Stub } final long origId = Binder.clearCallingIdentity(); try { - synchronized (mGlobalLock) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - - if (dc == null) { - ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" - + " for non-exiting displayId=%d", binder, displayId); - return; - } - final WindowToken token = dc.removeWindowToken(binder); - if (token == null) { - ProtoLog.w(WM_ERROR, - "removeWindowToken: Attempted to remove non-existing token: %s", - binder); - return; - } - dc.getInputMonitor().updateInputWindowsLw(true /*force*/); - } + removeWindowToken(binder, false /* removeWindows */, true /* animateExit */, displayId); } finally { Binder.restoreCallingIdentity(origId); } @@ -7536,28 +7545,10 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) { - synchronized (mGlobalLock) { - if (removeWindows) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null) { - ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" - + " for non-exiting displayId=%d", binder, displayId); - return; - } - - final WindowToken token = dc.removeWindowToken(binder); - if (token == null) { - ProtoLog.w(WM_ERROR, - "removeWindowToken: Attempted to remove non-existing token: %s", - binder); - return; - } - - token.removeAllWindowsIfPossible(); - } - WindowManagerService.this.removeWindowToken(binder, displayId); - } + public void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit, + int displayId) { + WindowManagerService.this.removeWindowToken(binder, removeWindows, animateExit, + displayId); } @Override diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c3fc99554bcc8..5e042efa2f112 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2180,11 +2180,18 @@ class WindowState extends WindowContainer implements WindowManagerP } } - boolean onSetAppExiting() { + boolean onSetAppExiting(boolean animateExit) { final DisplayContent displayContent = getDisplayContent(); boolean changed = false; - if (isVisibleNow()) { + if (!animateExit) { + // Hide the window permanently if no window exist animation is performed, so we can + // avoid the window surface becoming visible again unexpectedly during the next + // relayout. + mPermanentlyHidden = true; + hide(false /* doAnimation */, false /* requestAnim */); + } + if (isVisibleNow() && animateExit) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); if (mWmService.mAccessibilityController != null) { mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT); @@ -2197,7 +2204,7 @@ class WindowState extends WindowContainer implements WindowManagerP for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowState c = mChildren.get(i); - changed |= c.onSetAppExiting(); + changed |= c.onSetAppExiting(animateExit); } return changed; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index fbfa400ba8520..3cbc67c004cd9 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -24,6 +24,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -232,7 +233,7 @@ class WindowToken extends WindowContainer { } } - void setExiting() { + void setExiting(boolean animateExit) { if (isEmpty()) { super.removeImmediately(); return; @@ -247,11 +248,12 @@ class WindowToken extends WindowContainer { final int count = mChildren.size(); boolean changed = false; - final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN); + final boolean delayed = isAnimating(TRANSITION | PARENTS) + || (isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION) && animateExit); for (int i = 0; i < count; i++) { final WindowState win = mChildren.get(i); - changed |= win.onSetAppExiting(); + changed |= win.onSetAppExiting(animateExit); } final ActivityRecord app = asActivityRecord(); @@ -353,7 +355,7 @@ class WindowToken extends WindowContainer { @Override void removeImmediately() { if (mDisplayContent != null) { - mDisplayContent.removeWindowToken(token); + mDisplayContent.removeWindowToken(token, true /* animateExit */); } // Needs to occur after the token is removed from the display above to avoid attempt at // duplicate removal of this window container from it's parent. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index d048f1842aa3e..589f9134f2279 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -44,6 +45,7 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import java.util.function.BiFunction; @@ -126,7 +128,7 @@ public class WindowTokenTests extends WindowTestsBase { final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1"); final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2"); - mDisplayContent.removeWindowToken(token.token); + mDisplayContent.removeWindowToken(token.token, true /* animateExit */); // Verify that the token is no longer mapped on the display assertNull(mDisplayContent.getWindowToken(token.token)); // Verify that the token is still attached to its parent @@ -261,4 +263,29 @@ public class WindowTokenTests extends WindowTestsBase { assertNotNull(app.getFrozenInsetsState()); assertNull(mDisplayContent.mInputMethodWindow.getFrozenInsetsState()); } + + @Test + public void testRemoveWindowToken_noAnimateExitWhenSet() { + final TestWindowToken token = createTestWindowToken(0, mDisplayContent); + final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win"); + makeWindowVisible(win); + assertTrue(win.isOnScreen()); + spyOn(win); + spyOn(win.mWinAnimator); + spyOn(win.mToken); + + // Invoking removeWindowToken with setting no window exit animation and not remove window + // immediately. verify the window will hide without applying exit animation. + mWm.removeWindowToken(win.mToken.token, false /* removeWindows */, false /* animateExit */, + mDisplayContent.mDisplayId); + verify(win).onSetAppExiting(Mockito.eq(false) /* animateExit */); + verify(win).hide(false /* doAnimation */, false /* requestAnim */); + assertFalse(win.isOnScreen()); + verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false); + assertTrue(win.mToken.hasChild()); + + // Even though the window is being removed afterwards, it won't apply exit animation. + win.removeIfPossible(); + verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false); + } } From 9a4e52fa566169d5dd78d672fa3be048de9b0041 Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Tue, 24 Aug 2021 11:42:58 -0700 Subject: [PATCH 04/12] Use ArrayMap instead of SparseArray to manage the ProviderKey As the ArrayMap handles the hash code collision properly. Bug: 197647956 Test: atest CtsContentTestCases:android.content.cts Change-Id: Ibb57a22c08b0fa5462505d586d5fb0aaf908c80f (cherry picked from commit 119dd01b497b5e6e25009785ec36c9877675deef) --- core/java/android/app/ActivityThread.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0c64c86daf3ee..de0fef0e74a22 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -464,11 +464,7 @@ public final class ActivityThread extends ClientTransactionHandler @Override public int hashCode() { - return hashCode(authority, userId); - } - - public static int hashCode(final String auth, final int userIdent) { - return ((auth != null) ? auth.hashCode() : 0) ^ userIdent; + return ((authority != null) ? authority.hashCode() : 0) ^ userId; } } @@ -490,7 +486,7 @@ public final class ActivityThread extends ClientTransactionHandler // Note we never removes items from this map but that's okay because there are only so many // users and so many authorities. @GuardedBy("mGetProviderKeys") - final SparseArray mGetProviderKeys = new SparseArray<>(); + final ArrayMap mGetProviderKeys = new ArrayMap<>(); final ArrayMap> mOnPauseListeners = new ArrayMap>(); @@ -7015,11 +7011,11 @@ public final class ActivityThread extends ClientTransactionHandler } private ProviderKey getGetProviderKey(String auth, int userId) { - final int key = ProviderKey.hashCode(auth, userId); + final ProviderKey key = new ProviderKey(auth, userId); synchronized (mGetProviderKeys) { ProviderKey lock = mGetProviderKeys.get(key); if (lock == null) { - lock = new ProviderKey(auth, userId); + lock = key; mGetProviderKeys.put(key, lock); } return lock; From afa5f3c37aea6dd0e14576c035d12fa84c95f2cb Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 18 Aug 2021 16:35:00 -0700 Subject: [PATCH 05/12] Bluetooth: Fix formatting in getAlias() Bug: 180747689 Test: manual Change-Id: Ic309f4aad116fd424d5d0d0e2016d61be8826b78 (cherry picked from commit 3c4a917cb9481f4b70c26b6a06e798e65e5895ce) --- core/java/android/bluetooth/BluetoothDevice.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index bbb550fd6343e..5a5fac4a76645 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1329,7 +1329,10 @@ public final class BluetoothDevice implements Parcelable, Attributable { if (alias == null) { return getName(); } - return alias; + return alias + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } catch (RemoteException e) { Log.e(TAG, "", e); } From 57f6ccc10b0a2f4f819b7eab38ad3a76cfe14fee Mon Sep 17 00:00:00 2001 From: Aseem Kumar Date: Mon, 17 May 2021 09:25:03 +0000 Subject: [PATCH 06/12] Prevent apps from spamming addAccountExplicitly. See comment here for the discussion on solution https://b.corp.google.com/issues/169762606#comment14 Change-Id: If212df3a3b7be1de0fb26b8e88b2fcbb8077c253 Bug: 169762606 (cherry picked from commit 11053c17b397db67b20e96ce769508766cef7db9) Change-Id: Ia048d20c377ed2bde0efb0506ec61ae9d8356bf0 Merged-In: If212df3a3b7be1de0fb26b8e88b2fcbb8077c253 (cherry picked from commit 1877d8ec29b19a3997cc3dfb884059bfb53b05f9) --- core/java/android/accounts/Account.java | 7 +++++++ .../com/android/server/accounts/AccountManagerService.java | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index 0d6a07938e959..e6cdcc0ee7420 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -31,6 +31,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; +import java.util.Objects; import java.util.Set; /** @@ -86,6 +87,12 @@ public class Account implements Parcelable { if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("the type must not be empty: " + type); } + if (name.length() > 200) { + throw new IllegalArgumentException("account name is longer than 200 characters"); + } + if (type.length() > 200) { + throw new IllegalArgumentException("account type is longer than 200 characters"); + } this.name = name; this.type = type; this.accessId = accessId; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index a6a8cf018eefb..400b084ee9667 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1833,6 +1833,11 @@ public class AccountManagerService + ", skipping since the account already exists"); return false; } + if (accounts.accountsDb.findAllDeAccounts().size() > 100) { + Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() + + ", skipping since more than 50 accounts on device exist"); + return false; + } long accountId = accounts.accountsDb.insertCeAccount(account, password); if (accountId < 0) { Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() From 63777c0ca8e194ab3efc51905e83b07ea0d351a9 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Wed, 18 Aug 2021 21:41:48 +0000 Subject: [PATCH 07/12] Revoke internal permissions upon owner change. Similar to runtime permissions. Bug: 195338390 Test: manual Change-Id: I4f35ef021629810ea8854de2582c57b899b46db0 (cherry picked from commit ce576f8f6831d46d71fb74df4e95fa36dc433e87) --- .../server/pm/permission/Permission.java | 7 +++--- .../permission/PermissionManagerService.java | 25 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java index cda48063e9149..94e551a11daeb 100644 --- a/services/core/java/com/android/server/pm/permission/Permission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -480,9 +480,10 @@ public final class Permission { r.append("DUP:"); r.append(permissionInfo.name); } - if (permission.isRuntime() && (ownerChanged || wasNonRuntime)) { - // If this is a runtime permission and the owner has changed, or this wasn't a runtime - // permission, then permission state should be cleaned up + if ((permission.isInternal() && ownerChanged) + || (permission.isRuntime() && (ownerChanged || wasNonRuntime))) { + // If this is an internal/runtime permission and the owner has changed, or this wasn't a + // runtime permission, then permission state should be cleaned up. permission.mDefinitionChanged = true; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { 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 1133faabcf691..7b12709e4efd3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1643,7 +1643,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { isRolePermission = permission.isRole(); } final boolean mayRevokeRolePermission = isRolePermission - && mayManageRolePermission(callingUid); + // Allow ourselves to revoke role permissions due to definition changes. + && (callingUid == Process.myUid() || mayManageRolePermission(callingUid)); final boolean isRuntimePermission; synchronized (mLock) { @@ -2321,11 +2322,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int permNum = 0; permNum < numPermissions; permNum++) { final String permName = permissionsToRevoke.get(permNum); + final boolean isInternalPermission; synchronized (mLock) { final Permission bp = mRegistry.getPermission(permName); - if (bp == null || !bp.isRuntime()) { + if (bp == null || !(bp.isInternal() || bp.isRuntime())) { continue; } + isInternalPermission = bp.isInternal(); } mPackageManagerInt.forEachPackage(pkg -> { final String packageName = pkg.getPackageName(); @@ -2345,12 +2348,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (permissionState == PackageManager.PERMISSION_GRANTED && (flags & flagMask) == 0) { final int uid = UserHandle.getUid(userId, appId); - EventLog.writeEvent(0x534e4554, "154505240", uid, - "Revoking permission " + permName + " from package " - + packageName + " due to definition change"); - EventLog.writeEvent(0x534e4554, "168319670", uid, - "Revoking permission " + permName + " from package " - + packageName + " due to definition change"); + if (isInternalPermission) { + EventLog.writeEvent(0x534e4554, "195338390", uid, + "Revoking permission " + permName + " from package " + + packageName + " due to definition change"); + } else { + EventLog.writeEvent(0x534e4554, "154505240", uid, + "Revoking permission " + permName + " from package " + + packageName + " due to definition change"); + EventLog.writeEvent(0x534e4554, "168319670", uid, + "Revoking permission " + permName + " from package " + + packageName + " due to definition change"); + } Slog.e(TAG, "Revoking permission " + permName + " from package " + packageName + " due to definition change"); try { From 20552b60ee15e6cbc64add4b29aade7166c8374e Mon Sep 17 00:00:00 2001 From: shubang Date: Fri, 13 Aug 2021 15:42:15 -0700 Subject: [PATCH 08/12] TIF: fix issue of using caller-aware methods after clearCallingIdentity() Bug: 189824175 Test: atest android.media.tv.cts.TvInputManagerTest Change-Id: Iced6c2245c0099bc4bcdaceb51f8cce4dbc0a392 (cherry picked from commit a2ab2772d70984b1c24d5d4509b9c7a5abea71f9) --- .../java/com/android/server/tv/TvInputManagerService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 36a854e5374c2..28947083854bd 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -2304,10 +2304,9 @@ public final class TvInputManagerService extends SystemService { public void requestChannelBrowsable(Uri channelUri, int userId) throws RemoteException { final String callingPackageName = getCallingPackageName(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), + Binder.getCallingUid(), userId, "requestChannelBrowsable"); final long identity = Binder.clearCallingIdentity(); - final int callingUid = Binder.getCallingUid(); - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, - userId, "requestChannelBrowsable"); try { Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED); List list = getContext().getPackageManager() From 3f7e5e4464945a80819a737ae1c31d9f26074be4 Mon Sep 17 00:00:00 2001 From: Jonathan Scott Date: Thu, 26 Aug 2021 16:22:06 +0100 Subject: [PATCH 09/12] Mark RECOGNIZE_SPEECH Filter as ONLY_IF_NO_MATCH_FOUND Test: Manual Bug: 197495312 Change-Id: I5e65a183a907d0f16649ad84a1833e25e6065db9 (cherry picked from commit b7c2dbaa3d2b4b0aabfd4a72dfa0bd6aa356cda5) --- .../server/pm/DefaultCrossProfileIntentFiltersUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java index 3019439a430b3..5643873bef4de 100644 --- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java @@ -215,7 +215,7 @@ public class DefaultCrossProfileIntentFiltersUtils { private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH = new DefaultCrossProfileIntentFilter.Builder( DefaultCrossProfileIntentFilter.Direction.TO_PARENT, - /* flags= */0, + /* flags= */ ONLY_IF_NO_MATCH_FOUND, /* letsPersonalDataIntoProfile= */ false) .addAction(ACTION_RECOGNIZE_SPEECH) .addCategory(Intent.CATEGORY_DEFAULT) From cb890336d6a8056cc46ed01a908ad8f70e3880b6 Mon Sep 17 00:00:00 2001 From: Evan Chen Date: Fri, 13 Aug 2021 19:40:04 +0000 Subject: [PATCH 10/12] Fix Security issue for Html.fromHtml without escape Add Html.escapeHtml() for the string and also remove profileName for the confirmation_title to make sure the confirmation_title always show the device's name. Bug: 172251622 Bug: 197035186 Test: Manual Change-Id: I7e2f95fd85e18127dc481f188ace8ad6effc4831 (cherry picked from commit 8f005ffe4aa380552fb9f739d1b08c6f63f24fef) --- .../CompanionDeviceActivity.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index c1a0a9a92cc2e..b4cafd8548f43 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -93,9 +93,9 @@ public class CompanionDeviceActivity extends Activity { final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0); setTitle(Html.fromHtml(getString( R.string.confirmation_title, - getCallingAppName(), - profileName, - selectedDevice.getDisplayName()), 0)); + Html.escapeHtml(getCallingAppName()), + Html.escapeHtml(selectedDevice.getDisplayName())), 0)); + mPairButton = findViewById(R.id.button_pair); mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice)); getService().mSelectedDevice = selectedDevice; @@ -108,8 +108,8 @@ public class CompanionDeviceActivity extends Activity { mPairButton = findViewById(R.id.button_pair); mPairButton.setVisibility(View.GONE); setTitle(Html.fromHtml(getString(R.string.chooser_title, - profileName, - getCallingAppName()), 0)); + Html.escapeHtml(profileName), + Html.escapeHtml(getCallingAppName())), 0)); mDeviceListView = findViewById(R.id.device_list); mDevicesAdapter = new DevicesAdapter(); mDeviceListView.setAdapter(mDevicesAdapter); From 5aaf7d8559014c038d931e26526b45510faaaa4f Mon Sep 17 00:00:00 2001 From: Jeff DeCew Date: Fri, 13 Aug 2021 16:15:37 -0400 Subject: [PATCH 11/12] FooterView color logic cleanup Bug reports showed that when night mode eneded due to schedule, the text color was updated but not the background color. Just from analyzing the code, this appears to be due to onConfigurationChanged not being called on the footer view, but NSSL.onUiModeChanged was calling into FooterView.setTextColor. So logically, this seems like a likely fix, even though I can't repro the bug. Fixes: 197449800 Test: manual theme change continues to update footer colors. Test: manual language change updates footer text. Change-Id: If6fdb527eee244561dcd3975d859de93609662a5 (cherry picked from commit 70a328921b8fd6e243e082fe668e8856d15a8146) (cherry picked from commit 7846db0d3289651b3618160d68537a6a532e25f9) --- .../notification/row/FooterView.java | 31 ++++++++----------- .../stack/NotificationStackScrollLayout.java | 2 +- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 86c90c7bcb2eb..9eb95c4090091 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.row; -import android.annotation.ColorInt; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -28,15 +27,12 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.ViewState; public class FooterView extends StackScrollerDecorView { - private final int mClearAllTopPadding; private FooterViewButton mDismissButton; private FooterViewButton mManageButton; private boolean mShowHistory; public FooterView(Context context, AttributeSet attrs) { super(context, attrs); - mClearAllTopPadding = context.getResources().getDimensionPixelSize( - R.dimen.clear_all_padding_top); } @Override @@ -55,11 +51,6 @@ public class FooterView extends StackScrollerDecorView { mManageButton = findViewById(R.id.manage_text); } - public void setTextColor(@ColorInt int color) { - mManageButton.setTextColor(color); - mDismissButton.setTextColor(color); - } - public void setManageButtonClickListener(OnClickListener listener) { mManageButton.setOnClickListener(listener); } @@ -95,21 +86,25 @@ public class FooterView extends StackScrollerDecorView { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - int textColor = getResources().getColor(R.color.notif_pill_text); - Resources.Theme theme = getContext().getTheme(); - mDismissButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); - mDismissButton.setTextColor(textColor); - mManageButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); - mManageButton = findViewById(R.id.manage_text); + updateColors(); mDismissButton.setText(R.string.clear_all_notifications_text); - mManageButton.setTextColor(textColor); mDismissButton.setContentDescription( mContext.getString(R.string.accessibility_clear_all)); showHistory(mShowHistory); } + /** + * Update the text and background colors for the current color palette and night mode setting. + */ + public void updateColors() { + Resources.Theme theme = mContext.getTheme(); + int textColor = getResources().getColor(R.color.notif_pill_text, theme); + mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + mDismissButton.setTextColor(textColor); + mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + mManageButton.setTextColor(textColor); + } + @Override public ExpandableViewState createExpandableViewState() { return new FooterViewState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 289c32f17b31e..0660daab3720c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -4231,7 +4231,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable final @ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); mSectionsManager.setHeaderForegroundColor(textColor); - mFooterView.setTextColor(textColor); + mFooterView.updateColors(); mEmptyShadeView.setTextColor(textColor); } From 39627225836a334d785e2176ca517effe806ec3a Mon Sep 17 00:00:00 2001 From: yinxu Date: Thu, 9 Sep 2021 13:13:13 -0700 Subject: [PATCH 12/12] [DO NOT MERGE] Make sure WiFi and Cell tiles are not created in Provider Model Bug: 199176067 Test: Manual tests, unit tests Change-Id: Ic1b286a31c429b781652dcb24a1351b85f81edeb (cherry picked from commit bc6f74dbc32ca4f382d045f285789d9292d144a4) --- .../com/android/systemui/qs/QSTileHost.java | 30 ++++++-- .../qs/customize/TileQueryHelper.java | 10 ++- .../android/systemui/qs/QSFragmentTest.java | 2 +- .../android/systemui/qs/QSTileHostTest.java | 71 ++++++++++++++++--- .../qs/customize/TileQueryHelperTest.java | 7 +- .../qs/external/TileServicesTest.java | 6 +- 6 files changed, 107 insertions(+), 19 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 541ee2c4fe8ff..4a75810f86db2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -51,6 +51,7 @@ import com.android.systemui.qs.external.TileServices; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -95,6 +96,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D private final UiEventLogger mUiEventLogger; private final InstanceIdSequence mInstanceIdSequence; private final CustomTileStatePersister mCustomTileStatePersister; + private final FeatureFlags mFeatureFlags; private final List mCallbacks = new ArrayList<>(); private AutoTileManager mAutoTiles; @@ -122,7 +124,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D UiEventLogger uiEventLogger, UserTracker userTracker, SecureSettings secureSettings, - CustomTileStatePersister customTileStatePersister + CustomTileStatePersister customTileStatePersister, + FeatureFlags featureFlags ) { mIconController = iconController; mContext = context; @@ -144,6 +147,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D mUserTracker = userTracker; mSecureSettings = secureSettings; mCustomTileStatePersister = customTileStatePersister; + mFeatureFlags = featureFlags; mainHandler.post(() -> { // This is technically a hack to avoid circular dependency of @@ -265,7 +269,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) { newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode); } - final List tileSpecs = loadTileSpecs(mContext, newValue); + final List tileSpecs = loadTileSpecs(mContext, newValue, mFeatureFlags); int currentUser = mUserTracker.getUserId(); if (currentUser != mCurrentUser) { mUserContext = mUserTracker.getUserContext(); @@ -334,7 +338,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D if (newTiles.isEmpty() && !tileSpecs.isEmpty()) { // If we didn't manage to create any tiles, set it to empty (default) Log.d(TAG, "No valid tiles on tuning changed. Setting to default."); - changeTiles(currentSpecs, loadTileSpecs(mContext, "")); + changeTiles(currentSpecs, loadTileSpecs(mContext, "", mFeatureFlags)); } else { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onTilesChanged(); @@ -389,7 +393,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D private void changeTileSpecs(Predicate> changeFunction) { final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser); - final List tileSpecs = loadTileSpecs(mContext, setting); + final List tileSpecs = loadTileSpecs(mContext, setting, mFeatureFlags); if (changeFunction.test(tileSpecs)) { saveTilesToSettings(tileSpecs); } @@ -478,7 +482,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec()); } - protected static List loadTileSpecs(Context context, String tileList) { + protected static List loadTileSpecs( + Context context, String tileList, FeatureFlags featureFlags) { final Resources res = context.getResources(); if (TextUtils.isEmpty(tileList)) { @@ -511,6 +516,21 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D } } } + if (featureFlags.isProviderModelSettingEnabled()) { + if (!tiles.contains("internet")) { + if (tiles.contains("wifi")) { + // Replace the WiFi with Internet, and remove the Cell + tiles.set(tiles.indexOf("wifi"), "internet"); + tiles.remove("cell"); + } else if (tiles.contains("cell")) { + // Replace the Cell with Internet + tiles.set(tiles.indexOf("cell"), "internet"); + } + } else { + tiles.remove("wifi"); + tiles.remove("cell"); + } + } return tiles; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 3c2f35b954ea3..f2832b3d45ff2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -41,6 +41,7 @@ import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.util.leak.GarbageMonitor; import java.util.ArrayList; @@ -62,6 +63,7 @@ public class TileQueryHelper { private final Executor mBgExecutor; private final Context mContext; private final UserTracker mUserTracker; + private final FeatureFlags mFeatureFlags; private TileStateListener mListener; private boolean mFinished; @@ -71,12 +73,14 @@ public class TileQueryHelper { Context context, UserTracker userTracker, @Main Executor mainExecutor, - @Background Executor bgExecutor + @Background Executor bgExecutor, + FeatureFlags featureFlags ) { mContext = context; mMainExecutor = mainExecutor; mBgExecutor = bgExecutor; mUserTracker = userTracker; + mFeatureFlags = featureFlags; } public void setListener(TileStateListener listener) { @@ -117,6 +121,10 @@ public class TileQueryHelper { } final ArrayList tilesToAdd = new ArrayList<>(); + if (mFeatureFlags.isProviderModelSettingEnabled()) { + possibleTiles.remove("cell"); + possibleTiles.remove("wifi"); + } for (String spec : possibleTiles) { // Only add current and stock tiles that can be created from QSFactoryImpl. diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 3ee3e55c749a7..7f89b2629a6a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -139,7 +139,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { () -> mock(AutoTileManager.class), mock(DumpManager.class), mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)), mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class), - mock(SecureSettings.class), mock(CustomTileStatePersister.class)); + mock(SecureSettings.class), mock(CustomTileStatePersister.class), mFeatureFlags); qs.setHost(host); qs.setListening(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 9e97f801be3e2..4cbad5f15c5f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -63,6 +63,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -124,6 +125,8 @@ public class QSTileHostTest extends SysuiTestCase { private SecureSettings mSecureSettings; @Mock private CustomTileStatePersister mCustomTileStatePersister; + @Mock + private FeatureFlags mFeatureFlags; private Handler mHandler; private TestableLooper mLooper; @@ -137,9 +140,9 @@ public class QSTileHostTest extends SysuiTestCase { mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager, mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker, - mSecureSettings, mCustomTileStatePersister); + mSecureSettings, mCustomTileStatePersister, mFeatureFlags); setUpTileFactory(); - + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false); when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt())) .thenReturn(""); } @@ -169,13 +172,13 @@ public class QSTileHostTest extends SysuiTestCase { @Test public void testLoadTileSpecs_emptySetting() { - List tiles = QSTileHost.loadTileSpecs(mContext, ""); + List tiles = QSTileHost.loadTileSpecs(mContext, "", mFeatureFlags); assertFalse(tiles.isEmpty()); } @Test public void testLoadTileSpecs_nullSetting() { - List tiles = QSTileHost.loadTileSpecs(mContext, null); + List tiles = QSTileHost.loadTileSpecs(mContext, null, mFeatureFlags); assertFalse(tiles.isEmpty()); } @@ -188,6 +191,55 @@ public class QSTileHostTest extends SysuiTestCase { assertEquals(2, mQSTileHost.getTiles().size()); } + @Test + public void testRemoveWifiAndCellularWithoutInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2"); + + assertEquals("internet", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec1", mQSTileHost.mTileSpecs.get(1)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testRemoveWifiAndCellularWithInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(1)); + assertEquals("internet", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testRemoveWifiWithoutInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("internet", mQSTileHost.mTileSpecs.get(1)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testRemoveCellWithInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(1)); + assertEquals("internet", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testNoWifiNoCellularNoInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(1)); + } + @Test public void testSpecWithInvalidDoesNotUseDefault() { mContext.getOrCreateTestableResources() @@ -321,7 +373,7 @@ public class QSTileHostTest extends SysuiTestCase { @Test public void testLoadTileSpec_repeated() { - List specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2"); + List specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2", mFeatureFlags); assertEquals(2, specs.size()); assertEquals("spec1", specs.get(0)); @@ -332,7 +384,7 @@ public class QSTileHostTest extends SysuiTestCase { public void testLoadTileSpec_repeatedInDefault() { mContext.getOrCreateTestableResources() .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1"); - List specs = QSTileHost.loadTileSpecs(mContext, "default"); + List specs = QSTileHost.loadTileSpecs(mContext, "default", mFeatureFlags); // Remove spurious tiles, like dbg:mem specs.removeIf(spec -> !"spec1".equals(spec)); @@ -343,7 +395,7 @@ public class QSTileHostTest extends SysuiTestCase { public void testLoadTileSpec_repeatedDefaultAndSetting() { mContext.getOrCreateTestableResources() .addOverride(R.string.quick_settings_tiles_default, "spec1"); - List specs = QSTileHost.loadTileSpecs(mContext, "default,spec1"); + List specs = QSTileHost.loadTileSpecs(mContext, "default,spec1", mFeatureFlags); // Remove spurious tiles, like dbg:mem specs.removeIf(spec -> !"spec1".equals(spec)); @@ -371,11 +423,12 @@ public class QSTileHostTest extends SysuiTestCase { Provider autoTiles, DumpManager dumpManager, BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker, - SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) { + SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, + FeatureFlags featureFlags) { super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager, tunerService, autoTiles, dumpManager, broadcastDispatcher, Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings, - customTileStatePersister); + customTileStatePersister, featureFlags); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java index 4efcc5c3fc734..c5b67091d1973 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java @@ -58,6 +58,7 @@ import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -108,6 +109,8 @@ public class TileQueryHelperTest extends SysuiTestCase { private PackageManager mPackageManager; @Mock private UserTracker mUserTracker; + @Mock + private FeatureFlags mFeatureFlags; @Captor private ArgumentCaptor> mCaptor; @@ -133,12 +136,12 @@ public class TileQueryHelperTest extends SysuiTestCase { } } ).when(mQSTileHost).createTile(anyString()); - + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false); FakeSystemClock clock = new FakeSystemClock(); mMainExecutor = new FakeExecutor(clock); mBgExecutor = new FakeExecutor(clock); mTileQueryHelper = new TileQueryHelper( - mContext, mUserTracker, mMainExecutor, mBgExecutor); + mContext, mUserTracker, mMainExecutor, mBgExecutor, mFeatureFlags); mTileQueryHelper.setListener(mListener); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index 2b18404622912..01fa222896d31 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -48,6 +48,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -98,6 +99,8 @@ public class TileServicesTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private SecureSettings mSecureSettings; + @Mock + private FeatureFlags mFeatureFlags; @Before public void setUp() throws Exception { @@ -119,7 +122,8 @@ public class TileServicesTest extends SysuiTestCase { mUiEventLogger, mUserTracker, mSecureSettings, - mock(CustomTileStatePersister.class)); + mock(CustomTileStatePersister.class), + mFeatureFlags); mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher, mUserTracker); }