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() {

View File

@@ -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:
* <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}.
*/

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);

View File

@@ -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