Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60c39b3b authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

[Roll Forward] Block background custom toasts

Previous CL (ag/9699132) broke CTS ToastTest and was reverted (ag/9754500).
This was because the test was posting a background toast and verifying it
wasn't clickable. Test has been updated to use a foreground toast in ag/9754274
and I will also look into having it be executed in presubmit.(b/144810971).

-- Previous CL description (updated "Test:" lines)

To assess app compatibility problems early on, this CL blocks background
custom toasts in a non-secure way. We create a new method in the AIDL called
enqueueTextToast() that now contain the same parameters as
enqueueToast(), receiving the view created inside the app's process. But this
method will, in the future, contain a CharSequence instead of
ITransientNotification, for window creation inside the system.

We use PlatformCompat infrastructure to check if the change is enabled.

I learned with this change that if user blocks notifications for an app, it
won't be able to show background toasts, allowing me to re-use the foreground
check that was already in place.

This change will display a text toast (from the system) in case the app's
custom toast gets blocked, this is temporary to get feedback from
dogfood.

Test: With sample app, verify that:
      1. Posting text toast in bg works
      2. Posting custom toast in fg works
      3. Posting custom toast in bg does NOT work
Test: atest CtsToastTestCases CtsWindowManagerDeviceTestCases:ToastTest
      CtsWidgetTestCases:ToastTest CtsToastLegacyTestCases
Test: Working on CTS
Bug: 128611929

Change-Id: I25c6339c6adeb907878596332f57e2fa229bfda9
parent 0cf10818
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ interface INotificationManager
    void cancelAllNotifications(String pkg, int userId);

    void clearData(String pkg, int uid, boolean fromApp);
    // TODO: Replace parameter (ITransientNotification callback) with (CharSequence text)
    void enqueueTextToast(String pkg, ITransientNotification callback, int duration, int displayId);
    @UnsupportedAppUsage
    void enqueueToast(String pkg, ITransientNotification callback, int duration, int displayId);
    @UnsupportedAppUsage
+9 −1
Original line number Diff line number Diff line
@@ -100,6 +100,8 @@ public class Toast {
    @UnsupportedAppUsage
    int mDuration;
    View mNextView;
    // TODO(b/128611929): Remove this and check for null view when toast creation is in the system
    boolean mIsCustomToast = false;

    /**
     * Construct an empty Toast object.  You must call {@link #setView} before you
@@ -140,7 +142,11 @@ public class Toast {
        final int displayId = mContext.getDisplayId();

        try {
            if (mIsCustomToast) {
                service.enqueueToast(pkg, tn, mDuration, displayId);
            } else {
                service.enqueueTextToast(pkg, tn, mDuration, displayId);
            }
        } catch (RemoteException e) {
            // Empty
        }
@@ -160,6 +166,7 @@ public class Toast {
     * @see #getView
     */
    public void setView(View view) {
        mIsCustomToast = true;
        mNextView = view;
    }

@@ -168,6 +175,7 @@ public class Toast {
     * @see #setView
     */
    public View getView() {
        mIsCustomToast = true;
        return mNextView;
    }

+4 −0
Original line number Diff line number Diff line
@@ -117,6 +117,10 @@ java_library_static {
        "dnsresolver_aidl_interface-V2-java",
        "netd_event_listener_interface-java",
    ],

    plugins: [
        "compat-changeid-annotation-processor",
    ],
}

java_genrule {
+64 −10
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -216,6 +217,7 @@ 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.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -236,6 +238,7 @@ import com.android.server.EventLogTags;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
@@ -355,6 +358,15 @@ public class NotificationManagerService extends SystemService {
    private static final int REQUEST_CODE_TIMEOUT = 1;
    private static final String SCHEME_TIMEOUT = "timeout";
    private static final String EXTRA_KEY = "key";

    /**
     * Apps targeting R+ that post custom toasts in the background will have those blocked. Apps can
     * still post toasts created with {@link Toast#makeText(Context, CharSequence, int)} and its
     * variants while in the background.
     */
    @ChangeId
    private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;

    private IActivityManager mAm;
    private ActivityManager mActivityManager;
    private IPackageManager mPackageManager;
@@ -372,9 +384,11 @@ public class NotificationManagerService extends SystemService {
    private UriGrantsManagerInternal mUgmInternal;
    private RoleObserver mRoleObserver;
    private UserManager mUm;
    private IPlatformCompat mPlatformCompat;

    final IBinder mForegroundToken = new Binder();
    private WorkerHandler mHandler;
    private Handler mUiHandler;
    private final HandlerThread mRankingThread = new HandlerThread("ranker",
            Process.THREAD_PRIORITY_BACKGROUND);

@@ -1773,8 +1787,11 @@ public class NotificationManagerService extends SystemService {
                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
        mDpm = dpm;
        mUm = userManager;
        mPlatformCompat = IPlatformCompat.Stub.asInterface(
                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));

        mHandler = new WorkerHandler(looper);
        mUiHandler = new Handler(UiThread.get().getLooper());
        String[] extractorNames;
        try {
            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
@@ -2433,10 +2450,20 @@ public class NotificationManagerService extends SystemService {
        // Toasts
        // ============================================================================

        @Override
        public void enqueueTextToast(String pkg, ITransientNotification callback, int duration,
                int displayId) {
            enqueueToast(pkg, callback, duration, displayId, false);
        }

        @Override
        public void enqueueToast(String pkg, ITransientNotification callback, int duration,
                int displayId)
        {
                int displayId) {
            enqueueToast(pkg, callback, duration, displayId, true);
        }

        private void enqueueToast(String pkg, ITransientNotification callback, int duration,
                int displayId, boolean isCustomToast) {
            if (DBG) {
                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
                        + " duration=" + duration + " displayId=" + displayId);
@@ -2448,16 +2475,22 @@ public class NotificationManagerService extends SystemService {
            }

            final int callingUid = Binder.getCallingUid();
            final UserHandle callingUser = Binder.getCallingUserHandle();
            final boolean isSystemToast = isCallerSystemOrPhone()
                    || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
            final boolean isPackageSuspended = isPackagePaused(pkg);
            final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
                    callingUid);

            final boolean appIsForeground;
            long callingIdentity = Binder.clearCallingIdentity();
            try {
                final boolean appIsForeground = mActivityManager.getUidImportance(callingUid)
                appIsForeground = mActivityManager.getUidImportance(callingUid)
                        == IMPORTANCE_FOREGROUND;
            } finally {
                Binder.restoreCallingIdentity(callingIdentity);
            }

            if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage
                    && !appIsForeground) || isPackageSuspended)) {
                Slog.e(TAG, "Suppressing toast from package " + pkg
@@ -2465,8 +2498,29 @@ public class NotificationManagerService extends SystemService {
                        : " by user request."));
                return;
            }
            } finally {
                Binder.restoreCallingIdentity(callingIdentity);

            if (isCustomToast && !appIsForeground && !isSystemToast) {
                boolean block;
                try {
                    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;
                }
                if (block) {
                    // TODO(b/144152069): Remove informative toast
                    mUiHandler.post(() -> Toast.makeText(getContext(),
                            "Background custom toast blocked for package " + pkg + ".\n"
                                    + "See go/r-toast-block.",
                            Toast.LENGTH_SHORT).show());
                    Slog.w(TAG, "Blocking custom toast from package " + pkg
                            + " due to package not in the foreground");
                    return;
                }
            }

            synchronized (mToastQueue) {