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

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

Merge "Allow Intent.ACSD for notification action trampolines for targetSdk < S"

parents 2f639030 515e88cb
Loading
Loading
Loading
Loading
+12 −5
Original line number Original line Diff line number Diff line
@@ -14052,18 +14052,25 @@ public class ActivityManagerService extends IActivityManager.Stub
                callerApp = mPidsSelfLocked.get(pid);
                callerApp = mPidsSelfLocked.get(pid);
            }
            }
        }
        }
        if (callerApp != null) {
            // Check if the instrumentation of the process has the permission. This covers the usual
            // Check if the instrumentation of the process has the permission. This covers the usual
            // test started from the shell (which has the permission) case. This is needed for apps
            // test started from the shell (which has the permission) case. This is needed for apps
            // targeting SDK level < S but we are also allowing for targetSdk S+ as a convenience to
            // targeting SDK level < S but we are also allowing for targetSdk S+ as a convenience to
        // avoid breaking a bunch of existing tests and asking them to adopt shell permissions to do
            // avoid breaking a bunch of existing tests and asking them to adopt shell permissions
        // this.
            // to do this.
        if (callerApp != null) {
            ActiveInstrumentation instrumentation = callerApp.getActiveInstrumentation();
            ActiveInstrumentation instrumentation = callerApp.getActiveInstrumentation();
            if (instrumentation != null && checkPermission(
            if (instrumentation != null && checkPermission(
                    permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, instrumentation.mSourceUid)
                    permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, instrumentation.mSourceUid)
                    == PERMISSION_GRANTED) {
                    == PERMISSION_GRANTED) {
                return true;
                return true;
            }
            }
            // This is the notification trampoline use-case for example, where apps use Intent.ACSD
            // to close the shade prior to starting an activity.
            WindowProcessController wmApp = callerApp.getWindowProcessController();
            if (wmApp.canCloseSystemDialogsByToken()) {
                return true;
            }
        }
        }
        return false;
        return false;
    }
    }
+20 −8
Original line number Original line Diff line number Diff line
@@ -105,6 +105,7 @@ import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
@@ -291,7 +292,6 @@ import libcore.io.IoUtils;


import org.json.JSONException;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserException;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayInputStream;
@@ -308,7 +308,7 @@ import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Duration;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.LinkedList;
@@ -10416,12 +10416,15 @@ public class NotificationManagerService extends SystemService {
        private final Set<String> mPackagesShown = new ArraySet<>();
        private final Set<String> mPackagesShown = new ArraySet<>();


        @Override
        @Override
        public IBinder getToken() {
        public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid,
            return ALLOWLIST_TOKEN;
                String packageName) {
            checkArgument(!tokens.isEmpty());
            for (IBinder token : tokens) {
                if (token != ALLOWLIST_TOKEN) {
                    // We only block or warn if the start is exclusively due to notification
                    return true;
                }
            }
            }

        @Override
        public boolean isActivityStartAllowed(int uid, String packageName) {
            String toastMessage = "Indirect activity start from " + packageName;
            String toastMessage = "Indirect activity start from " + packageName;
            String logcatMessage =
            String logcatMessage =
                    "Indirect notification activity start (trampoline) from " + packageName;
                    "Indirect notification activity start (trampoline) from " + packageName;
@@ -10439,6 +10442,15 @@ public class NotificationManagerService extends SystemService {
            }
            }
        }
        }


        @Override
        public boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid) {
            // If the start is allowed via notification, we allow the app to close system dialogs
            // only if their targetSdk < S, otherwise they have no valid reason to do this since
            // trampolines are blocked.
            return tokens.contains(ALLOWLIST_TOKEN)
                    && !CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
        }

        private void toast(String message) {
        private void toast(String message) {
            mUiHandler.post(() ->
            mUiHandler.post(() ->
                    Toast.makeText(getUiContext(), message + "\nSee go/s-trampolines.",
                    Toast.makeText(getUiContext(), message + "\nSee go/s-trampolines.",
+15 −11
Original line number Original line Diff line number Diff line
@@ -18,25 +18,29 @@ package com.android.server.wm;


import android.os.IBinder;
import android.os.IBinder;


import java.util.Collection;

/**
/**
 * Callback to be called when a background activity start is allowed exclusively because of the
 * Callback to decide activity starts and related operations based on originating tokens.
 * token provided in {@link #getToken()}.
 */
 */
public interface BackgroundActivityStartCallback {
public interface BackgroundActivityStartCallback {
    /**
    /**
     * The token for which this callback is responsible for deciding whether the app can start
     * Returns true if the background activity start originating from {@code tokens} should be
     * background activities or not.
     * allowed or not.
     *
     * Note that if the start was allowed due to a mechanism other than tokens (eg. permission),
     * this won't be called.
     *
     *
     * Ideally this should just return a final variable, don't do anything costly here (don't hold
     * This will be called holding the WM lock, don't do anything costly here.
     * any locks).
     */
     */
    IBinder getToken();
    boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, String packageName);


    /**
    /**
     * Returns true if the background activity start due to originating token in {@link #getToken()}
     * Returns whether {@code uid} can send {@link android.content.Intent
     * should be allowed or not.
     * #ACTION_CLOSE_SYSTEM_DIALOGS}, presumably to start activities, based on the originating
     * tokens {@code tokens} currently associated with potential activity starts.
     *
     *
     * This will be called holding the WM lock, don't do anything costly here.
     * This will be called holding the AM and WM lock, don't do anything costly here.
     */
     */
    boolean isActivityStartAllowed(int uid, String packageName);
    boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid);
}
}
+17 −12
Original line number Original line Diff line number Diff line
@@ -585,9 +585,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
    }
    }


    /**
    /**
     * If there are no tokens, we don't allow *by token*. If there are tokens, we need to check if
     * If there are no tokens, we don't allow *by token*. If there are tokens, we ask the callback
     * the callback handles all the tokens, if so we ask the callback if the activity should be
     * if the start is allowed for these tokens, otherwise if there is no callback we allow.
     * started, otherwise we allow.
     */
     */
    private boolean isBackgroundStartAllowedByToken() {
    private boolean isBackgroundStartAllowedByToken() {
        if (mBackgroundActivityStartTokens.isEmpty()) {
        if (mBackgroundActivityStartTokens.isEmpty()) {
@@ -597,16 +596,22 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
            // We have tokens but no callback to decide => allow
            // We have tokens but no callback to decide => allow
            return true;
            return true;
        }
        }
        IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
        // The callback will decide
        for (IBinder token : mBackgroundActivityStartTokens.values()) {
        return mBackgroundActivityStartCallback.isActivityStartAllowed(
            if (token != callbackToken) {
                mBackgroundActivityStartTokens.values(), mInfo.uid, mInfo.packageName);
                // The callback doesn't handle all the tokens => allow
                return true;
    }
    }

    /**
     * Returns whether this process is allowed to close system dialogs via a background activity
     * start token that allows the close system dialogs operation (eg. notification).
     */
    public boolean canCloseSystemDialogsByToken() {
        synchronized (mAtm.mGlobalLock) {
            return !mBackgroundActivityStartTokens.isEmpty()
                    && mBackgroundActivityStartCallback != null
                    && mBackgroundActivityStartCallback.canCloseSystemDialogs(
                            mBackgroundActivityStartTokens.values(), mInfo.uid);
        }
        }
        // The callback handles all the tokens => callback decides
        return mBackgroundActivityStartCallback.isActivityStartAllowed(mInfo.uid,
                mInfo.packageName);
    }
    }


    private boolean isBoundByForegroundUid() {
    private boolean isBoundByForegroundUid() {