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 caef507aa9)
This commit is contained in:
Bernardo Rufino
2020-02-19 14:37:58 +00:00
parent f9de0572fb
commit 0a7ced1dd3
6 changed files with 75 additions and 6 deletions

View File

@@ -651,7 +651,7 @@ public class Toast {
} }
}; };
presenter.startLayoutParams(mParams); presenter.startLayoutParams(mParams, packageName);
} }
private List<Callback> getCallbacks() { private List<Callback> getCallbacks() {

View File

@@ -18,6 +18,7 @@ package android.widget;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.os.IBinder; import android.os.IBinder;
import android.view.Gravity; import android.view.Gravity;
@@ -28,6 +29,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import com.android.internal.R; import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
/** /**
* Class responsible for toast presentation inside app's process and in system UI. * 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 static final long LONG_DURATION_TIMEOUT = 7000;
private final Context mContext; private final Context mContext;
private final Resources mResources;
private final AccessibilityManager mAccessibilityManager; private final AccessibilityManager mAccessibilityManager;
public ToastPresenter(Context context, AccessibilityManager accessibilityManager) { public ToastPresenter(Context context, AccessibilityManager accessibilityManager) {
mContext = context; mContext = context;
mResources = context.getResources();
mAccessibilityManager = accessibilityManager; mAccessibilityManager = accessibilityManager;
} }
/** /**
* Initializes {@code params} with default values for toasts. * 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.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT; params.format = PixelFormat.TRANSLUCENT;
@@ -60,6 +64,7 @@ public class ToastPresenter {
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
setShowForAllUsersIfApplicable(params, packageName);
} }
/** /**
@@ -69,7 +74,7 @@ public class ToastPresenter {
public void adjustLayoutParams(WindowManager.LayoutParams params, IBinder windowToken, public void adjustLayoutParams(WindowManager.LayoutParams params, IBinder windowToken,
int duration, int gravity, int xOffset, int yOffset, float horizontalMargin, int duration, int gravity, int xOffset, int yOffset, float horizontalMargin,
float verticalMargin) { float verticalMargin) {
Configuration config = mContext.getResources().getConfiguration(); Configuration config = mResources.getConfiguration();
int absGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection()); int absGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection());
params.gravity = absGravity; params.gravity = absGravity;
if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
@@ -88,6 +93,32 @@ public class ToastPresenter {
params.token = windowToken; 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:
* <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}. * Returns the default text toast view for message {@code text}.
*/ */

View File

@@ -4410,4 +4410,13 @@
<!-- Whether to default to an expanded list of users on the lock screen user switcher. --> <!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
<bool name="config_expandLockScreenUserSwitcher">false</bool> <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> </resources>

View File

@@ -3903,4 +3903,6 @@
<java-symbol type="bool" name="config_expandLockScreenUserSwitcher" /> <java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
<java-symbol type="string" name="loading" /> <java-symbol type="string" name="loading" />
<java-symbol type="array" name="config_toastCrossUserPackages" />
</resources> </resources>

View File

@@ -95,7 +95,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
hideCurrentToast(); hideCurrentToast();
} }
View view = mPresenter.getTextToastView(text); View view = mPresenter.getTextToastView(text);
LayoutParams params = getLayoutParams(windowToken, duration); LayoutParams params = getLayoutParams(packageName, windowToken, duration);
mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback); mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback);
try { try {
mWindowManager.addView(view, params); mWindowManager.addView(view, params);
@@ -145,9 +145,9 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
mCurrentToast = null; mCurrentToast = null;
} }
private LayoutParams getLayoutParams(IBinder windowToken, int duration) { private LayoutParams getLayoutParams(String packageName, IBinder windowToken, int duration) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(); WindowManager.LayoutParams params = new WindowManager.LayoutParams();
mPresenter.startLayoutParams(params); mPresenter.startLayoutParams(params, packageName);
int gravity = mContext.getResources().getInteger( int gravity = mContext.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity); com.android.internal.R.integer.config_toastDefaultGravity);
int yOffset = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset); int yOffset = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);

View File

@@ -105,6 +105,33 @@ public class ToastUITest extends SysuiTestCase {
assertThat(windowParams.packageName).isEqualTo(mContext.getPackageName()); assertThat(windowParams.packageName).isEqualTo(mContext.getPackageName());
assertThat(windowParams.getTitle()).isEqualTo("Toast"); assertThat(windowParams.getTitle()).isEqualTo("Toast");
assertThat(windowParams.token).isEqualTo(WINDOW_TOKEN_1); 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 @Test