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