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

Commit f0ef9bae authored by Bernardo Rufino's avatar Bernardo Rufino Committed by Android (Google) Code Review
Browse files

Merge "Block non-activity notification trampolines for apps w/ targetSdk S+"

parents e0213504 e3874038
Loading
Loading
Loading
Loading
+29 −14
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ import android.app.StatusBarManager;
import android.app.UriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.BackupManager;
import android.app.compat.CompatChanges;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
@@ -408,6 +409,15 @@ public class NotificationManagerService extends SystemService {
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;

    /**
     * Activity starts coming from broadcast receivers or services in response to notification and
     * notification action clicks will be blocked for UX and performance reasons. Instead start the
     * activity directly from the PendingIntent.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;

    private IActivityManager mAm;
    private ActivityTaskManagerInternal mAtm;
    private ActivityManager mActivityManager;
@@ -10013,7 +10023,7 @@ public class NotificationManagerService extends SystemService {
     * TODO(b/161957908): Remove dogfooder toast.
     */
    private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
        private Set<String> mPackagesShown = new ArraySet<>();
        private final Set<String> mPackagesShown = new ArraySet<>();

        @Override
        public IBinder getToken() {
@@ -10021,13 +10031,9 @@ public class NotificationManagerService extends SystemService {
        }

        @Override
        public void onExclusiveTokenActivityStart(String packageName) {
            Slog.w(TAG, "Indirect notification activity start from " + packageName);
            boolean isFirstOccurrence = mPackagesShown.add(packageName);
            if (!isFirstOccurrence) {
                return;
            }

        public boolean isActivityStartAllowed(int uid, String packageName) {
            boolean block = CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
            if (block || mPackagesShown.add(packageName)) {
                mUiHandler.post(() ->
                        Toast.makeText(getUiContext(),
                                "Indirect activity start from "
@@ -10036,5 +10042,14 @@ public class NotificationManagerService extends SystemService {
                                        + "See go/s-trampolines.",
                                Toast.LENGTH_LONG).show());
            }
            String message =
                    "Indirect notification activity start (trampoline) from " + packageName;
            if (block) {
                Slog.e(TAG, message + " blocked");
                return false;
            }
            Slog.w(TAG, message + ", this should be avoided for performance reasons");
            return true;
        }
    }
}
+6 −6
Original line number Diff line number Diff line
@@ -1358,6 +1358,12 @@ class ActivityStarter {
            }
            return false;
        }
        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
            Slog.w(TAG, "Background activity start for " + callingPackage
                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
            return false;
        }
        // If we don't have callerApp at this point, no caller was provided to startActivity().
        // That's the case for PendingIntent-based starts, since the creator's process might not be
        // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
@@ -1394,12 +1400,6 @@ class ActivityStarter {
                }
            }
        }
        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
            Slog.w(TAG, "Background activity start for " + callingPackage
                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
            return false;
        }
        // anything that has fallen through would currently be aborted
        Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                + "; callingUid: " + callingUid
+7 −4
Original line number Diff line number Diff line
@@ -24,8 +24,8 @@ import android.os.IBinder;
 */
public interface BackgroundActivityStartCallback {
    /**
     * The token that allowed the activity start that triggered {@link
     * #onExclusiveTokenActivityStart()}.
     * The token for which this callback is responsible for deciding whether the app can start
     * background activities or not.
     *
     * Ideally this should just return a final variable, don't do anything costly here (don't hold
     * any locks).
@@ -33,7 +33,10 @@ public interface BackgroundActivityStartCallback {
    IBinder getToken();

    /**
     * Called when the background activity start happens.
     * Returns true if the background activity start due to originating token in {@link #getToken()}
     * should be allowed or not.
     *
     * This will be called holding the WM lock, don't do anything costly here.
     */
    void onExclusiveTokenActivityStart(String packageName);
    boolean isActivityStartAllowed(int uid, String packageName);
}
+18 −8
Original line number Diff line number Diff line
@@ -223,7 +223,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
    private boolean mRunningRemoteAnimation;

    @Nullable
    private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
    private final BackgroundActivityStartCallback mBackgroundActivityStartCallback;

    /** The state for oom-adjustment calculation. */
    private final OomScoreReferenceState mOomRefState;
@@ -555,8 +555,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
            return true;
        }
        // allow if the flag was explicitly set
        if (!mBackgroundActivityStartTokens.isEmpty()) {
            onBackgroundStartAllowedByToken();
        if (isBackgroundStartAllowedByToken()) {
            if (DEBUG_ACTIVITY_STARTS) {
                Slog.d(TAG, "[WindowProcessController(" + mPid
                        + ")] Activity start allowed: process allowed by token");
@@ -566,18 +565,29 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
        return false;
    }

    private void onBackgroundStartAllowedByToken() {
    /**
     * If there are no tokens, we don't allow *by token*. If there are tokens, we need to check if
     * the callback handles all the tokens, if so we ask the callback if the activity should be
     * started, otherwise we allow.
     */
    private boolean isBackgroundStartAllowedByToken() {
        if (mBackgroundActivityStartTokens.isEmpty()) {
            return false;
        }
        if (mBackgroundActivityStartCallback == null) {
            return;
            // We have tokens but no callback to decide => allow
            return true;
        }
        IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
        for (IBinder token : mBackgroundActivityStartTokens.values()) {
            if (token != callbackToken) {
                return;
                // The callback doesn't handle all the tokens => allow
                return true;
            }
        }
        mAtm.mH.post(() ->
                mBackgroundActivityStartCallback.onExclusiveTokenActivityStart(mInfo.packageName));
        // The callback handles all the tokens => callback decides
        return mBackgroundActivityStartCallback.isActivityStartAllowed(mInfo.uid,
                mInfo.packageName);
    }

    private boolean isBoundByForegroundUid() {