Show toasts from system/sysUI to all users
The package name test is safe:
* If it's used by Toast class in an app, apps have access to layout
params anyway via getWindowParams() and applied flag is already
protected by INTERNAL_SYSTEM_WINDOW permission.
* If it's used by ToastUI class in sys UI on behalf of another app,
sys UI is trusted domain and can't be messed up by another app. It
also has INTERNAL_SYSTEM_WINDOW permission.
Bug: 149408635
Test: atest ToastUITest android.widget.cts.ToastTest
android.widget.cts29.ToastTest android.server.wm.ToastTest
Test: Change phone to vibration/silent in secondary user and observe
toast is displayed.
Change-Id: I227e9f74166300fcb3ba8f3871b464afe6ec6a28
Merged-In: I227e9f74166300fcb3ba8f3871b464afe6ec6a28
(cherry picked from commit caef507aa935b3724bd19096b31d557aa0fc787a)
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 436d683..7d97a91 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -651,7 +651,7 @@
}
};
- presenter.startLayoutParams(mParams);
+ presenter.startLayoutParams(mParams, packageName);
}
private List<Callback> getCallbacks() {
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index 654ab50..0447b6b 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -18,6 +18,7 @@
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.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 @@
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 @@
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 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) {
@@ -89,6 +94,32 @@
}
/**
+ * 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:
+ * <li>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.
+ * <li>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}.
*/
public View getTextToastView(CharSequence text) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8023990..eae28a0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4410,4 +4410,13 @@
<!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
<bool name="config_expandLockScreenUserSwitcher">false</bool>
+
+ <!-- Toasts posted from these packages will be shown to the current user, regardless of the user
+ the process belongs to. This is useful for packages that run under a single user but serve
+ multiple users, e.g. the system.
+ These packages MUST be able to add flag SYSTEM_FLAG_SHOW_FOR_ALL_USERS to a window. -->
+ <string-array name="config_toastCrossUserPackages" translatable="false">
+ <item>android</item>
+ <item>com.android.systemui</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 797d03f..e1d2618 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3903,4 +3903,6 @@
<java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
<java-symbol type="string" name="loading" />
+
+ <java-symbol type="array" name="config_toastCrossUserPackages" />
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 34a2520..0242e83 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 @@
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 @@
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 d58f2c9..bc3a5b1 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 @@
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