Loading services/core/java/com/android/server/am/ActivityManagerService.java +12 −5 Original line number Original line Diff line number Diff line Loading @@ -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; } } services/core/java/com/android/server/notification/NotificationManagerService.java +20 −8 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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.", Loading services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java +15 −11 Original line number Original line Diff line number Diff line Loading @@ -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); } } services/core/java/com/android/server/wm/WindowProcessController.java +17 −12 Original line number Original line Diff line number Diff line Loading @@ -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()) { Loading @@ -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() { Loading Loading
services/core/java/com/android/server/am/ActivityManagerService.java +12 −5 Original line number Original line Diff line number Diff line Loading @@ -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; } }
services/core/java/com/android/server/notification/NotificationManagerService.java +20 −8 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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.", Loading
services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java +15 −11 Original line number Original line Diff line number Diff line Loading @@ -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); } }
services/core/java/com/android/server/wm/WindowProcessController.java +17 −12 Original line number Original line Diff line number Diff line Loading @@ -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()) { Loading @@ -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() { Loading