diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 436d6836d11e8..7d97a91b74358 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -651,7 +651,7 @@ public class Toast { } }; - presenter.startLayoutParams(mParams); + presenter.startLayoutParams(mParams, packageName); } private List getCallbacks() { diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java index 654ab50a9ce56..0447b6bb9f11e 100644 --- a/core/java/android/widget/ToastPresenter.java +++ b/core/java/android/widget/ToastPresenter.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.PixelFormat; import android.os.IBinder; import android.view.Gravity; @@ -28,6 +29,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import com.android.internal.R; +import com.android.internal.util.ArrayUtils; /** * Class responsible for toast presentation inside app's process and in system UI. @@ -39,17 +41,19 @@ public class ToastPresenter { private static final long LONG_DURATION_TIMEOUT = 7000; private final Context mContext; + private final Resources mResources; private final AccessibilityManager mAccessibilityManager; public ToastPresenter(Context context, AccessibilityManager accessibilityManager) { mContext = context; + mResources = context.getResources(); mAccessibilityManager = accessibilityManager; } /** * Initializes {@code params} with default values for toasts. */ - public void startLayoutParams(WindowManager.LayoutParams params) { + public void startLayoutParams(WindowManager.LayoutParams params, String packageName) { params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; @@ -60,6 +64,7 @@ public class ToastPresenter { params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + setShowForAllUsersIfApplicable(params, packageName); } /** @@ -69,7 +74,7 @@ public class ToastPresenter { public void adjustLayoutParams(WindowManager.LayoutParams params, IBinder windowToken, int duration, int gravity, int xOffset, int yOffset, float horizontalMargin, float verticalMargin) { - Configuration config = mContext.getResources().getConfiguration(); + Configuration config = mResources.getConfiguration(); int absGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection()); params.gravity = absGravity; if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { @@ -88,6 +93,32 @@ public class ToastPresenter { params.token = windowToken; } + /** + * Sets {@link WindowManager.LayoutParams#SYSTEM_FLAG_SHOW_FOR_ALL_USERS} flag if {@code + * packageName} is a cross-user package. + * + * Implementation note: + * This code is safe to be executed in SystemUI and the app's process: + *
  • SystemUI: It's running on a trusted domain so apps can't tamper with it. SystemUI + * has the permission INTERNAL_SYSTEM_WINDOW needed by the flag, so SystemUI can add + * the flag on behalf of those packages, which all contain INTERNAL_SYSTEM_WINDOW + * permission. + *
  • App: The flag being added is protected behind INTERNAL_SYSTEM_WINDOW permission + * and any app can already add that flag via getWindowParams() if it has that + * permission, so we are just doing this automatically for cross-user packages. + */ + private void setShowForAllUsersIfApplicable(WindowManager.LayoutParams params, + String packageName) { + if (isCrossUserPackage(packageName)) { + params.privateFlags = WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + } + } + + private boolean isCrossUserPackage(String packageName) { + String[] packages = mResources.getStringArray(R.array.config_toastCrossUserPackages); + return ArrayUtils.contains(packages, packageName); + } + /** * Returns the default text toast view for message {@code text}. */ diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8023990937541..eae28a0733320 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4410,4 +4410,13 @@ false + + + + android + com.android.systemui + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3ed3a64e2cb49..3032838698950 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3904,4 +3904,6 @@ + + diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 34a2520085009..0242e8349364e 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -95,7 +95,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { hideCurrentToast(); } View view = mPresenter.getTextToastView(text); - LayoutParams params = getLayoutParams(windowToken, duration); + LayoutParams params = getLayoutParams(packageName, windowToken, duration); mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback); try { mWindowManager.addView(view, params); @@ -145,9 +145,9 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { mCurrentToast = null; } - private LayoutParams getLayoutParams(IBinder windowToken, int duration) { + private LayoutParams getLayoutParams(String packageName, IBinder windowToken, int duration) { WindowManager.LayoutParams params = new WindowManager.LayoutParams(); - mPresenter.startLayoutParams(params); + mPresenter.startLayoutParams(params, packageName); int gravity = mContext.getResources().getInteger( com.android.internal.R.integer.config_toastDefaultGravity); int yOffset = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset); diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index d58f2c9c17917..bc3a5b193c8a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -105,6 +105,33 @@ public class ToastUITest extends SysuiTestCase { assertThat(windowParams.packageName).isEqualTo(mContext.getPackageName()); assertThat(windowParams.getTitle()).isEqualTo("Toast"); assertThat(windowParams.token).isEqualTo(WINDOW_TOKEN_1); + assertThat(windowParams.privateFlags + & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isEqualTo(0); + } + + @Test + public void testShowToast_forAndroidPackage_addsAllUserFlag() throws Exception { + mToastUI.showToast("android", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); + + verify(mWindowManager).addView(any(), mParamsCaptor.capture()); + ViewGroup.LayoutParams params = mParamsCaptor.getValue(); + assertThat(params).isInstanceOf(WindowManager.LayoutParams.class); + WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; + assertThat(windowParams.privateFlags + & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isNotEqualTo(0); + } + + @Test + public void testShowToast_forSystemUiPackage_addsAllUserFlag() throws Exception { + mToastUI.showToast("com.android.systemui", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + null); + + verify(mWindowManager).addView(any(), mParamsCaptor.capture()); + ViewGroup.LayoutParams params = mParamsCaptor.getValue(); + assertThat(params).isInstanceOf(WindowManager.LayoutParams.class); + WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; + assertThat(windowParams.privateFlags + & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isNotEqualTo(0); } @Test