Loading services/core/java/com/android/server/notification/NotificationManagerService.java +70 −64 Original line number Diff line number Diff line Loading @@ -242,7 +242,6 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.IPlatformCompat; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.InstanceId; import com.android.internal.logging.InstanceIdSequence; Loading Loading @@ -468,7 +467,6 @@ public class NotificationManagerService extends SystemService { private UriGrantsManagerInternal mUgmInternal; private RoleObserver mRoleObserver; private UserManager mUm; private IPlatformCompat mPlatformCompat; private ShortcutHelper mShortcutHelper; final IBinder mForegroundToken = new Binder(); Loading Loading @@ -1987,8 +1985,6 @@ public class NotificationManagerService extends SystemService { mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class); mDpm = dpm; mUm = userManager; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); String[] extractorNames; Loading Loading @@ -2886,16 +2882,16 @@ public class NotificationManagerService extends SystemService { return userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId; } private ToastRecord getToastRecord(int uid, int pid, String packageName, IBinder token, @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration, Binder windowToken, int displayId, private ToastRecord getToastRecord(int uid, int pid, String packageName, boolean isSystemToast, IBinder token, @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration, Binder windowToken, int displayId, @Nullable ITransientNotificationCallback textCallback) { if (callback == null) { return new TextToastRecord(this, mStatusBar, uid, pid, packageName, token, text, duration, windowToken, displayId, textCallback); return new TextToastRecord(this, mStatusBar, uid, pid, packageName, isSystemToast, token, text, duration, windowToken, displayId, textCallback); } else { return new CustomToastRecord(this, uid, pid, packageName, token, callback, duration, windowToken, displayId); return new CustomToastRecord(this, uid, pid, packageName, isSystemToast, token, callback, duration, windowToken, displayId); } } Loading Loading @@ -2966,32 +2962,11 @@ public class NotificationManagerService extends SystemService { } boolean isAppRenderedToast = (callback != null); if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg, callingUid)) { boolean block; final long id = Binder.clearCallingIdentity(); try { // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts // are not app-rendered, so isAppRenderedToast == true means it's a custom // toast. block = mPlatformCompat.isChangeEnabledByPackageName( CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg, callingUser.getIdentifier()); } catch (RemoteException e) { // Shouldn't happen have since it's a local local Slog.e(TAG, "Unexpected exception while checking block background custom toasts" + " change", e); block = false; } finally { Binder.restoreCallingIdentity(id); } if (block) { if (blockToast(callingUid, isSystemToast, isAppRenderedToast)) { Slog.w(TAG, "Blocking custom toast from package " + pkg + " due to package not in the foreground"); + " due to package not in the foreground at time the toast was posted"); return; } } synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); Loading Loading @@ -3023,8 +2998,8 @@ public class NotificationManagerService extends SystemService { Binder windowToken = new Binder(); mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId); record = getToastRecord(callingUid, callingPid, pkg, token, text, callback, duration, windowToken, displayId, textCallback); record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token, text, callback, duration, windowToken, displayId, textCallback); mToastQueue.add(record); index = mToastQueue.size() - 1; keepProcessAliveForToastIfNeededLocked(callingPid); Loading @@ -3042,28 +3017,6 @@ public class NotificationManagerService extends SystemService { } } /** * Implementation note: Our definition of foreground for toasts is an implementation matter * and should strike a balance between functionality and anti-abuse effectiveness. We * currently worry about the following cases: * <ol> * <li>App with fullscreen activity: Allow toasts * <li>App behind translucent activity from other app: Block toasts * <li>App in multi-window: Allow toasts * <li>App with expanded bubble: Allow toasts * <li>App posting toasts on onCreate(), onStart(), onResume(): Allow toasts * <li>App posting toasts on onPause(), onStop(), onDestroy(): Block toasts * </ol> * Checking if the UID has any resumed activities satisfy use-cases above. * * <p>Checking if {@code mActivityManager.getUidImportance(callingUid) == * IMPORTANCE_FOREGROUND} does not work because it considers the app in foreground if it has * any visible activities, failing case 2 in list above. */ private boolean isPackageInForegroundForToast(String pkg, int callingUid) { return mAtm.hasResumedActivity(callingUid); } @Override public void cancelToast(String pkg, IBinder token) { Slog.i(TAG, "cancelToast pkg=" + pkg + " token=" + token); Loading Loading @@ -7388,17 +7341,16 @@ public class NotificationManagerService extends SystemService { CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid); boolean isWithinQuota = mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG); if ((!rateLimitingEnabled || isWithinQuota) && record.show()) { if (tryShowToast(record, rateLimitingEnabled, isWithinQuota)) { scheduleDurationReachedLocked(record); mIsCurrentToastShown = true; if (rateLimitingEnabled) { mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); } return; } else if (rateLimitingEnabled && !isWithinQuota) { Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + "following toast was blocked and discarded: " + record); } int index = mToastQueue.indexOf(record); if (index >= 0) { mToastQueue.remove(index); Loading @@ -7407,6 +7359,22 @@ public class NotificationManagerService extends SystemService { } } /** Returns true if it successfully showed the toast. */ private boolean tryShowToast(ToastRecord record, boolean rateLimitingEnabled, boolean isWithinQuota) { if (rateLimitingEnabled && !isWithinQuota) { Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + "following toast was blocked and discarded: " + record); return false; } if (blockToast(record.uid, record.isSystemToast, record.isAppRendered())) { Slog.w(TAG, "Blocking custom toast from package " + record.pkg + " due to package not in the foreground at the time of showing the toast"); return false; } return record.show(); } @GuardedBy("mToastQueue") void cancelToastLocked(int index) { ToastRecord record = mToastQueue.get(index); Loading Loading @@ -7526,6 +7494,44 @@ public class NotificationManagerService extends SystemService { } } /** * Implementation note: Our definition of foreground for toasts is an implementation matter * and should strike a balance between functionality and anti-abuse effectiveness. We * currently worry about the following cases: * <ol> * <li>App with fullscreen activity: Allow toasts * <li>App behind translucent activity from other app: Block toasts * <li>App in multi-window: Allow toasts * <li>App with expanded bubble: Allow toasts * <li>App posting toasts on onCreate(), onStart(), onResume(): Allow toasts * <li>App posting toasts on onPause(), onStop(), onDestroy(): Block toasts * </ol> * Checking if the UID has any resumed activities satisfy use-cases above. * * <p>Checking if {@code mActivityManager.getUidImportance(callingUid) == * IMPORTANCE_FOREGROUND} does not work because it considers the app in foreground if it has * any visible activities, failing case 2 in list above. */ private boolean isPackageInForegroundForToast(int callingUid) { return mAtm.hasResumedActivity(callingUid); } /** * True if the toast should be blocked. It will return true if all of the following conditions * apply: it's a custom toast, it's not a system toast, the package that sent the toast is in * the background and CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is enabled. * * CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so it will return false for apps * with targetSdk < R. For apps with targetSdk R+, text toasts are not app-rendered, so * isAppRenderedToast == true means it's a custom toast. */ private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast) { return isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(uid) && CompatChanges.isChangeEnabled(CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, uid); } private void handleRankingReconsideration(Message message) { if (!(message.obj instanceof RankingReconsideration)) return; RankingReconsideration recon = (RankingReconsideration) message.obj; Loading services/core/java/com/android/server/notification/toast/CustomToastRecord.java +10 −3 Original line number Diff line number Diff line Loading @@ -37,9 +37,10 @@ public class CustomToastRecord extends ToastRecord { public final ITransientNotification callback; public CustomToastRecord(NotificationManagerService notificationManager, int uid, int pid, String packageName, IBinder token, ITransientNotification callback, int duration, Binder windowToken, int displayId) { super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId); String packageName, boolean isSystemToast, IBinder token, ITransientNotification callback, int duration, Binder windowToken, int displayId) { super(notificationManager, uid, pid, packageName, isSystemToast, token, duration, windowToken, displayId); this.callback = checkNotNull(callback); } Loading Loading @@ -77,11 +78,17 @@ public class CustomToastRecord extends ToastRecord { return true; } @Override public boolean isAppRendered() { return true; } @Override public String toString() { return "CustomToastRecord{" + Integer.toHexString(System.identityHashCode(this)) + " " + pid + ":" + pkg + "/" + UserHandle.formatUid(uid) + " isSystemToast=" + isSystemToast + " token=" + token + " callback=" + callback + " duration=" + getDuration() Loading services/core/java/com/android/server/notification/toast/TextToastRecord.java +11 −3 Original line number Diff line number Diff line Loading @@ -43,9 +43,11 @@ public class TextToastRecord extends ToastRecord { public TextToastRecord(NotificationManagerService notificationManager, @Nullable StatusBarManagerInternal statusBarManager, int uid, int pid, String packageName, IBinder token, CharSequence text, int duration, Binder windowToken, int displayId, @Nullable ITransientNotificationCallback callback) { super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId); String packageName, boolean isSystemToast, IBinder token, CharSequence text, int duration, Binder windowToken, int displayId, @Nullable ITransientNotificationCallback callback) { super(notificationManager, uid, pid, packageName, isSystemToast, token, duration, windowToken, displayId); mStatusBar = statusBarManager; mCallback = callback; this.text = checkNotNull(text); Loading @@ -72,11 +74,17 @@ public class TextToastRecord extends ToastRecord { mStatusBar.hideToast(pkg, token); } @Override public boolean isAppRendered() { return false; } @Override public String toString() { return "TextToastRecord{" + Integer.toHexString(System.identityHashCode(this)) + " " + pid + ":" + pkg + "/" + UserHandle.formatUid(uid) + " isSystemToast=" + isSystemToast + " token=" + token + " text=" + text + " duration=" + getDuration() Loading services/core/java/com/android/server/notification/toast/ToastRecord.java +10 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ public abstract class ToastRecord { public final int uid; public final int pid; public final String pkg; public final boolean isSystemToast; public final IBinder token; public final int displayId; public final Binder windowToken; Loading @@ -38,11 +39,13 @@ public abstract class ToastRecord { private int mDuration; protected ToastRecord(NotificationManagerService notificationManager, int uid, int pid, String pkg, IBinder token, int duration, Binder windowToken, int displayId) { String pkg, boolean isSystemToast, IBinder token, int duration, Binder windowToken, int displayId) { this.mNotificationManager = notificationManager; this.uid = uid; this.pid = pid; this.pkg = pkg; this.isSystemToast = isSystemToast; this.token = token; this.windowToken = windowToken; this.displayId = displayId; Loading Loading @@ -95,4 +98,10 @@ public abstract class ToastRecord { // should override this method. return false; } /** * Returns true if the app is responsible for rendering the toast, false otherwise (for example, * if it's rendered by the system). */ public abstract boolean isAppRendered(); } services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -4979,6 +4979,40 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(callback, times(0)).show(any()); } @Test public void testCustomToastPostedWhileInForeground_blockedIfAppGoesToBackground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); setAppInForegroundForToasts(mUid, true); Binder token1 = new Binder(); Binder token2 = new Binder(); ITransientNotification callback1 = mock(ITransientNotification.class); ITransientNotification callback2 = mock(ITransientNotification.class); INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueToast(testPackage, token1, callback1, 2000, 0); nmService.enqueueToast(testPackage, token2, callback2, 2000, 0); assertEquals(2, mService.mToastQueue.size()); // Both toasts enqueued. verify(callback1, times(1)).show(any()); // First toast shown. setAppInForegroundForToasts(mUid, false); mService.cancelToastLocked(0); // Remove the first toast, and show next. assertEquals(0, mService.mToastQueue.size()); // Both toasts processed. verify(callback2, never()).show(any()); // Second toast was never shown. } @Test public void testAllowForegroundTextToasts() throws Exception { final String testPackage = "testPackageName"; Loading Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +70 −64 Original line number Diff line number Diff line Loading @@ -242,7 +242,6 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.IPlatformCompat; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.InstanceId; import com.android.internal.logging.InstanceIdSequence; Loading Loading @@ -468,7 +467,6 @@ public class NotificationManagerService extends SystemService { private UriGrantsManagerInternal mUgmInternal; private RoleObserver mRoleObserver; private UserManager mUm; private IPlatformCompat mPlatformCompat; private ShortcutHelper mShortcutHelper; final IBinder mForegroundToken = new Binder(); Loading Loading @@ -1987,8 +1985,6 @@ public class NotificationManagerService extends SystemService { mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class); mDpm = dpm; mUm = userManager; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); String[] extractorNames; Loading Loading @@ -2886,16 +2882,16 @@ public class NotificationManagerService extends SystemService { return userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId; } private ToastRecord getToastRecord(int uid, int pid, String packageName, IBinder token, @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration, Binder windowToken, int displayId, private ToastRecord getToastRecord(int uid, int pid, String packageName, boolean isSystemToast, IBinder token, @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration, Binder windowToken, int displayId, @Nullable ITransientNotificationCallback textCallback) { if (callback == null) { return new TextToastRecord(this, mStatusBar, uid, pid, packageName, token, text, duration, windowToken, displayId, textCallback); return new TextToastRecord(this, mStatusBar, uid, pid, packageName, isSystemToast, token, text, duration, windowToken, displayId, textCallback); } else { return new CustomToastRecord(this, uid, pid, packageName, token, callback, duration, windowToken, displayId); return new CustomToastRecord(this, uid, pid, packageName, isSystemToast, token, callback, duration, windowToken, displayId); } } Loading Loading @@ -2966,32 +2962,11 @@ public class NotificationManagerService extends SystemService { } boolean isAppRenderedToast = (callback != null); if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg, callingUid)) { boolean block; final long id = Binder.clearCallingIdentity(); try { // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts // are not app-rendered, so isAppRenderedToast == true means it's a custom // toast. block = mPlatformCompat.isChangeEnabledByPackageName( CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg, callingUser.getIdentifier()); } catch (RemoteException e) { // Shouldn't happen have since it's a local local Slog.e(TAG, "Unexpected exception while checking block background custom toasts" + " change", e); block = false; } finally { Binder.restoreCallingIdentity(id); } if (block) { if (blockToast(callingUid, isSystemToast, isAppRenderedToast)) { Slog.w(TAG, "Blocking custom toast from package " + pkg + " due to package not in the foreground"); + " due to package not in the foreground at time the toast was posted"); return; } } synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); Loading Loading @@ -3023,8 +2998,8 @@ public class NotificationManagerService extends SystemService { Binder windowToken = new Binder(); mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId); record = getToastRecord(callingUid, callingPid, pkg, token, text, callback, duration, windowToken, displayId, textCallback); record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token, text, callback, duration, windowToken, displayId, textCallback); mToastQueue.add(record); index = mToastQueue.size() - 1; keepProcessAliveForToastIfNeededLocked(callingPid); Loading @@ -3042,28 +3017,6 @@ public class NotificationManagerService extends SystemService { } } /** * Implementation note: Our definition of foreground for toasts is an implementation matter * and should strike a balance between functionality and anti-abuse effectiveness. We * currently worry about the following cases: * <ol> * <li>App with fullscreen activity: Allow toasts * <li>App behind translucent activity from other app: Block toasts * <li>App in multi-window: Allow toasts * <li>App with expanded bubble: Allow toasts * <li>App posting toasts on onCreate(), onStart(), onResume(): Allow toasts * <li>App posting toasts on onPause(), onStop(), onDestroy(): Block toasts * </ol> * Checking if the UID has any resumed activities satisfy use-cases above. * * <p>Checking if {@code mActivityManager.getUidImportance(callingUid) == * IMPORTANCE_FOREGROUND} does not work because it considers the app in foreground if it has * any visible activities, failing case 2 in list above. */ private boolean isPackageInForegroundForToast(String pkg, int callingUid) { return mAtm.hasResumedActivity(callingUid); } @Override public void cancelToast(String pkg, IBinder token) { Slog.i(TAG, "cancelToast pkg=" + pkg + " token=" + token); Loading Loading @@ -7388,17 +7341,16 @@ public class NotificationManagerService extends SystemService { CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid); boolean isWithinQuota = mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG); if ((!rateLimitingEnabled || isWithinQuota) && record.show()) { if (tryShowToast(record, rateLimitingEnabled, isWithinQuota)) { scheduleDurationReachedLocked(record); mIsCurrentToastShown = true; if (rateLimitingEnabled) { mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); } return; } else if (rateLimitingEnabled && !isWithinQuota) { Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + "following toast was blocked and discarded: " + record); } int index = mToastQueue.indexOf(record); if (index >= 0) { mToastQueue.remove(index); Loading @@ -7407,6 +7359,22 @@ public class NotificationManagerService extends SystemService { } } /** Returns true if it successfully showed the toast. */ private boolean tryShowToast(ToastRecord record, boolean rateLimitingEnabled, boolean isWithinQuota) { if (rateLimitingEnabled && !isWithinQuota) { Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + "following toast was blocked and discarded: " + record); return false; } if (blockToast(record.uid, record.isSystemToast, record.isAppRendered())) { Slog.w(TAG, "Blocking custom toast from package " + record.pkg + " due to package not in the foreground at the time of showing the toast"); return false; } return record.show(); } @GuardedBy("mToastQueue") void cancelToastLocked(int index) { ToastRecord record = mToastQueue.get(index); Loading Loading @@ -7526,6 +7494,44 @@ public class NotificationManagerService extends SystemService { } } /** * Implementation note: Our definition of foreground for toasts is an implementation matter * and should strike a balance between functionality and anti-abuse effectiveness. We * currently worry about the following cases: * <ol> * <li>App with fullscreen activity: Allow toasts * <li>App behind translucent activity from other app: Block toasts * <li>App in multi-window: Allow toasts * <li>App with expanded bubble: Allow toasts * <li>App posting toasts on onCreate(), onStart(), onResume(): Allow toasts * <li>App posting toasts on onPause(), onStop(), onDestroy(): Block toasts * </ol> * Checking if the UID has any resumed activities satisfy use-cases above. * * <p>Checking if {@code mActivityManager.getUidImportance(callingUid) == * IMPORTANCE_FOREGROUND} does not work because it considers the app in foreground if it has * any visible activities, failing case 2 in list above. */ private boolean isPackageInForegroundForToast(int callingUid) { return mAtm.hasResumedActivity(callingUid); } /** * True if the toast should be blocked. It will return true if all of the following conditions * apply: it's a custom toast, it's not a system toast, the package that sent the toast is in * the background and CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is enabled. * * CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so it will return false for apps * with targetSdk < R. For apps with targetSdk R+, text toasts are not app-rendered, so * isAppRenderedToast == true means it's a custom toast. */ private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast) { return isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(uid) && CompatChanges.isChangeEnabled(CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, uid); } private void handleRankingReconsideration(Message message) { if (!(message.obj instanceof RankingReconsideration)) return; RankingReconsideration recon = (RankingReconsideration) message.obj; Loading
services/core/java/com/android/server/notification/toast/CustomToastRecord.java +10 −3 Original line number Diff line number Diff line Loading @@ -37,9 +37,10 @@ public class CustomToastRecord extends ToastRecord { public final ITransientNotification callback; public CustomToastRecord(NotificationManagerService notificationManager, int uid, int pid, String packageName, IBinder token, ITransientNotification callback, int duration, Binder windowToken, int displayId) { super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId); String packageName, boolean isSystemToast, IBinder token, ITransientNotification callback, int duration, Binder windowToken, int displayId) { super(notificationManager, uid, pid, packageName, isSystemToast, token, duration, windowToken, displayId); this.callback = checkNotNull(callback); } Loading Loading @@ -77,11 +78,17 @@ public class CustomToastRecord extends ToastRecord { return true; } @Override public boolean isAppRendered() { return true; } @Override public String toString() { return "CustomToastRecord{" + Integer.toHexString(System.identityHashCode(this)) + " " + pid + ":" + pkg + "/" + UserHandle.formatUid(uid) + " isSystemToast=" + isSystemToast + " token=" + token + " callback=" + callback + " duration=" + getDuration() Loading
services/core/java/com/android/server/notification/toast/TextToastRecord.java +11 −3 Original line number Diff line number Diff line Loading @@ -43,9 +43,11 @@ public class TextToastRecord extends ToastRecord { public TextToastRecord(NotificationManagerService notificationManager, @Nullable StatusBarManagerInternal statusBarManager, int uid, int pid, String packageName, IBinder token, CharSequence text, int duration, Binder windowToken, int displayId, @Nullable ITransientNotificationCallback callback) { super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId); String packageName, boolean isSystemToast, IBinder token, CharSequence text, int duration, Binder windowToken, int displayId, @Nullable ITransientNotificationCallback callback) { super(notificationManager, uid, pid, packageName, isSystemToast, token, duration, windowToken, displayId); mStatusBar = statusBarManager; mCallback = callback; this.text = checkNotNull(text); Loading @@ -72,11 +74,17 @@ public class TextToastRecord extends ToastRecord { mStatusBar.hideToast(pkg, token); } @Override public boolean isAppRendered() { return false; } @Override public String toString() { return "TextToastRecord{" + Integer.toHexString(System.identityHashCode(this)) + " " + pid + ":" + pkg + "/" + UserHandle.formatUid(uid) + " isSystemToast=" + isSystemToast + " token=" + token + " text=" + text + " duration=" + getDuration() Loading
services/core/java/com/android/server/notification/toast/ToastRecord.java +10 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ public abstract class ToastRecord { public final int uid; public final int pid; public final String pkg; public final boolean isSystemToast; public final IBinder token; public final int displayId; public final Binder windowToken; Loading @@ -38,11 +39,13 @@ public abstract class ToastRecord { private int mDuration; protected ToastRecord(NotificationManagerService notificationManager, int uid, int pid, String pkg, IBinder token, int duration, Binder windowToken, int displayId) { String pkg, boolean isSystemToast, IBinder token, int duration, Binder windowToken, int displayId) { this.mNotificationManager = notificationManager; this.uid = uid; this.pid = pid; this.pkg = pkg; this.isSystemToast = isSystemToast; this.token = token; this.windowToken = windowToken; this.displayId = displayId; Loading Loading @@ -95,4 +98,10 @@ public abstract class ToastRecord { // should override this method. return false; } /** * Returns true if the app is responsible for rendering the toast, false otherwise (for example, * if it's rendered by the system). */ public abstract boolean isAppRendered(); }
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -4979,6 +4979,40 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(callback, times(0)).show(any()); } @Test public void testCustomToastPostedWhileInForeground_blockedIfAppGoesToBackground() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) .thenReturn(false); setAppInForegroundForToasts(mUid, true); Binder token1 = new Binder(); Binder token2 = new Binder(); ITransientNotification callback1 = mock(ITransientNotification.class); ITransientNotification callback2 = mock(ITransientNotification.class); INotificationManager nmService = (INotificationManager) mService.mService; nmService.enqueueToast(testPackage, token1, callback1, 2000, 0); nmService.enqueueToast(testPackage, token2, callback2, 2000, 0); assertEquals(2, mService.mToastQueue.size()); // Both toasts enqueued. verify(callback1, times(1)).show(any()); // First toast shown. setAppInForegroundForToasts(mUid, false); mService.cancelToastLocked(0); // Remove the first toast, and show next. assertEquals(0, mService.mToastQueue.size()); // Both toasts processed. verify(callback2, never()).show(any()); // Second toast was never shown. } @Test public void testAllowForegroundTextToasts() throws Exception { final String testPackage = "testPackageName"; Loading