Merge "Add new BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS binding flag and the mechanism to use it"

This commit is contained in:
Michal Karpinski
2019-03-13 13:44:02 +00:00
committed by Android (Google) Code Review
10 changed files with 162 additions and 52 deletions

View File

@@ -337,6 +337,15 @@ public abstract class Context {
*/
public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x0100;
/**
* Flag for {@link #bindService}: allow background activity starts from the bound service's
* process.
* This flag is only respected if the caller is holding
* {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}.
* @hide
*/
public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000;
/**
* @hide Flag for {@link #bindService}: the service being bound to represents a
* protected system component, so must have association restrictions applied to it.

View File

@@ -11518,6 +11518,7 @@ public final class Settings {
* service_min_restart_time_between (long)
* service_max_inactivity (long)
* service_bg_start_timeout (long)
* service_bg_activity_start_timeout (long)
* process_start_async (boolean)
* </pre>
*

View File

@@ -159,8 +159,8 @@ public class TileLifecycleManager extends BroadcastReceiver implements
mBindTryCount++;
try {
mIsBound = mContext.bindServiceAsUser(mIntent, this,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
mUser);
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, mUser);
} catch (SecurityException e) {
Log.e(TAG, "Failed to bind to service", e);
mIsBound = false;

View File

@@ -91,7 +91,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (userState == null) return;
final long identity = Binder.clearCallingIdentity();
try {
int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE;
int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
if (userState.getBindInstantServiceAllowed()) {
flags |= Context.BIND_ALLOW_INSTANT;
}

View File

@@ -131,9 +131,6 @@ public final class ActiveServices {
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
// For how long after a whitelisted service's start its process can start a background activity
private static final int SERVICE_BG_ACTIVITY_START_TIMEOUT_MS = 10*1000;
final ActivityManagerService mAm;
// Maximum number of services that we allow to start in the background
@@ -333,8 +330,8 @@ public final class ActiveServices {
+ " delayedStop=" + r.delayedStop);
} else {
try {
startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true,
false);
startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false,
true);
} catch (TransactionTooLargeException e) {
// Ignore, nobody upstack cares.
}
@@ -637,26 +634,26 @@ public final class ActiveServices {
}
if (allowBackgroundActivityStarts) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
if (proc != null) {
proc.addAllowBackgroundActivityStartsToken(r);
// schedule removal of the whitelisting token after the timeout
removeAllowBackgroundActivityStartsServiceToken(proc, r,
SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
}
r.hasStartedWhitelistingBgActivityStarts = true;
scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(r);
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting,
allowBackgroundActivityStarts);
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
private void removeAllowBackgroundActivityStartsServiceToken(ProcessRecord proc,
ServiceRecord r, int delayMillis) {
mAm.mHandler.postDelayed(() -> {
if (proc != null) {
proc.removeAllowBackgroundActivityStartsToken(r);
}
}, delayMillis);
private void scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(ServiceRecord r) {
// if there's a request pending from the past, drop it before scheduling a new one
if (r.startedWhitelistingBgActivityStartsCleanUp == null) {
r.startedWhitelistingBgActivityStartsCleanUp = () -> {
synchronized(mAm) {
r.setHasStartedWhitelistingBgActivityStarts(false);
}
};
}
mAm.mHandler.removeCallbacks(r.startedWhitelistingBgActivityStartsCleanUp);
mAm.mHandler.postDelayed(r.startedWhitelistingBgActivityStartsCleanUp,
mAm.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
@@ -705,8 +702,7 @@ public final class ActiveServices {
}
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting, boolean allowBackgroundActivityStarts)
throws TransactionTooLargeException {
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
@@ -717,8 +713,7 @@ public final class ActiveServices {
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false,
allowBackgroundActivityStarts);
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
if (error != null) {
return new ComponentName("!!", error);
}
@@ -765,6 +760,12 @@ public final class ActiveServices {
SystemClock.uptimeMillis());
}
service.callStart = false;
// the service will not necessarily be brought down, so only clear the whitelisting state
// for start-based bg activity starts now, and drop any existing future cleanup callback
service.setHasStartedWhitelistingBgActivityStarts(false);
mAm.mHandler.removeCallbacks(service.startedWhitelistingBgActivityStartsCleanUp);
bringDownServiceIfNeededLocked(service, false, false);
}
@@ -788,9 +789,6 @@ public final class ActiveServices {
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
try {
// immediately remove bg activity whitelisting token if there was one
removeAllowBackgroundActivityStartsServiceToken(callerApp, r.record,
0 /* delayMillis */);
stopServiceLocked(r.record);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -1614,6 +1612,12 @@ public final class ActiveServices {
+ ") set BIND_ALLOW_INSTANT when binding service " + service);
}
if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
mAm.enforceCallingPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
"BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS");
}
final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
@@ -1672,7 +1676,7 @@ public final class ActiveServices {
try {
bringUpServiceLocked(serviceRecord,
serviceIntent.getFlags(),
callerFg, false, false, false);
callerFg, false, false);
} catch (RemoteException e) {
/* ignore - local call */
}
@@ -1762,6 +1766,9 @@ public final class ActiveServices {
if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
s.whitelistManager = true;
}
if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
s.setHasBindingWhitelistingBgActivityStarts(true);
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}
@@ -1775,7 +1782,7 @@ public final class ActiveServices {
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired, false) != null) {
permissionsReviewRequired) != null) {
return 0;
}
}
@@ -2445,8 +2452,7 @@ public final class ActiveServices {
return;
}
try {
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false,
false);
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
} catch (TransactionTooLargeException e) {
// Ignore, it's been logged and nothing upstack cares.
}
@@ -2491,11 +2497,8 @@ public final class ActiveServices {
}
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired,
boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
@@ -2603,13 +2606,6 @@ public final class ActiveServices {
}
}
if (app != null && allowBackgroundActivityStarts) {
app.addAllowBackgroundActivityStartsToken(r);
// schedule removal of the whitelisting token after the timeout
removeAllowBackgroundActivityStartsServiceToken(app, r,
SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
}
if (r.fgRequired) {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
@@ -2646,6 +2642,11 @@ public final class ActiveServices {
}
}
/**
* Note the name of this method should not be confused with the started services concept.
* The "start" here means bring up the instance in the client, and this method is called
* from bindService() as well.
*/
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
if (app.thread == null) {
@@ -3085,6 +3086,10 @@ public final class ActiveServices {
updateWhitelistManagerLocked(s.app);
}
}
// And do the same for bg activity starts whitelisting.
if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
s.updateHasBindingWhitelistingBgActivityStarts();
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}

View File

@@ -69,6 +69,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_SERVICE_MIN_RESTART_TIME_BETWEEN = "service_min_restart_time_between";
static final String KEY_MAX_SERVICE_INACTIVITY = "service_max_inactivity";
static final String KEY_BG_START_TIMEOUT = "service_bg_start_timeout";
static final String KEY_SERVICE_BG_ACTIVITY_START_TIMEOUT = "service_bg_activity_start_timeout";
static final String KEY_BOUND_SERVICE_CRASH_RESTART_DURATION = "service_crash_restart_duration";
static final String KEY_BOUND_SERVICE_CRASH_MAX_RETRY = "service_crash_max_retry";
static final String KEY_PROCESS_START_ASYNC = "process_start_async";
@@ -99,6 +100,7 @@ final class ActivityManagerConstants extends ContentObserver {
private static final long DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN = 10*1000;
private static final long DEFAULT_MAX_SERVICE_INACTIVITY = 30*60*1000;
private static final long DEFAULT_BG_START_TIMEOUT = 15*1000;
private static final long DEFAULT_SERVICE_BG_ACTIVITY_START_TIMEOUT = 10_000;
private static final long DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION = 30*60_000;
private static final int DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY = 16;
private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
@@ -212,6 +214,9 @@ final class ActivityManagerConstants extends ContentObserver {
// allowing the next pending start to run.
public long BG_START_TIMEOUT = DEFAULT_BG_START_TIMEOUT;
// For how long after a whitelisted service's start its process can start a background activity
public long SERVICE_BG_ACTIVITY_START_TIMEOUT = DEFAULT_SERVICE_BG_ACTIVITY_START_TIMEOUT;
// Initial backoff delay for retrying bound foreground services
public long BOUND_SERVICE_CRASH_RESTART_DURATION = DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION;
@@ -398,6 +403,9 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_MAX_SERVICE_INACTIVITY);
BG_START_TIMEOUT = mParser.getLong(KEY_BG_START_TIMEOUT,
DEFAULT_BG_START_TIMEOUT);
SERVICE_BG_ACTIVITY_START_TIMEOUT = mParser.getLong(
KEY_SERVICE_BG_ACTIVITY_START_TIMEOUT,
DEFAULT_SERVICE_BG_ACTIVITY_START_TIMEOUT);
BOUND_SERVICE_CRASH_RESTART_DURATION = mParser.getLong(
KEY_BOUND_SERVICE_CRASH_RESTART_DURATION,
DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION);
@@ -518,6 +526,8 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println(MAX_SERVICE_INACTIVITY);
pw.print(" "); pw.print(KEY_BG_START_TIMEOUT); pw.print("=");
pw.println(BG_START_TIMEOUT);
pw.print(" "); pw.print(KEY_SERVICE_BG_ACTIVITY_START_TIMEOUT); pw.print("=");
pw.println(SERVICE_BG_ACTIVITY_START_TIMEOUT);
pw.print(" "); pw.print(KEY_BOUND_SERVICE_CRASH_RESTART_DURATION); pw.print("=");
pw.println(BOUND_SERVICE_CRASH_RESTART_DURATION);
pw.print(" "); pw.print(KEY_BOUND_SERVICE_CRASH_MAX_RETRY); pw.print("=");

View File

@@ -125,6 +125,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
int pendingConnectionGroup; // To be filled in to ProcessRecord once it connects
int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects
// any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag?
private boolean hasBindingWhitelistingBgActivityStarts;
// is this service currently whitelisted to start activities from background by providing
// allowBackgroundActivityStarts=true to startServiceLocked()?
boolean hasStartedWhitelistingBgActivityStarts;
// used to clean up the state of hasStartedWhitelistingBgActivityStarts after a timeout
Runnable startedWhitelistingBgActivityStartsCleanUp;
String stringName; // caching of toString
private int lastStartId; // identifier of most recent start request.
@@ -371,6 +379,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
if (whitelistManager) {
pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
}
if (hasBindingWhitelistingBgActivityStarts) {
pw.print(prefix); pw.print("hasBindingWhitelistingBgActivityStarts=");
pw.println(hasBindingWhitelistingBgActivityStarts);
}
if (hasStartedWhitelistingBgActivityStarts) {
pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts=");
pw.println(hasStartedWhitelistingBgActivityStarts);
}
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
}
@@ -518,6 +534,15 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
public void setProcess(ProcessRecord _proc) {
if (_proc != null) {
if (hasStartedWhitelistingBgActivityStarts || hasBindingWhitelistingBgActivityStarts) {
_proc.addAllowBackgroundActivityStartsToken(this);
} else {
_proc.removeAllowBackgroundActivityStartsToken(this);
}
} else if (app != null) {
app.removeAllowBackgroundActivityStartsToken(this);
}
app = _proc;
if (pendingConnectionGroup > 0 && _proc != null) {
_proc.connectionService = this;
@@ -540,6 +565,62 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
}
void updateHasBindingWhitelistingBgActivityStarts() {
boolean hasWhitelistingBinding = false;
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
for (int i = 0; i < cr.size(); i++) {
if ((cr.get(i).flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
hasWhitelistingBinding = true;
break;
}
}
if (hasWhitelistingBinding) {
break;
}
}
if (hasBindingWhitelistingBgActivityStarts != hasWhitelistingBinding) {
hasBindingWhitelistingBgActivityStarts = hasWhitelistingBinding;
updateParentProcessBgActivityStartsWhitelistingToken();
}
}
void setHasBindingWhitelistingBgActivityStarts(boolean newValue) {
if (hasBindingWhitelistingBgActivityStarts != newValue) {
hasBindingWhitelistingBgActivityStarts = newValue;
updateParentProcessBgActivityStartsWhitelistingToken();
}
}
void setHasStartedWhitelistingBgActivityStarts(boolean newValue) {
if (hasStartedWhitelistingBgActivityStarts != newValue) {
hasStartedWhitelistingBgActivityStarts = newValue;
updateParentProcessBgActivityStartsWhitelistingToken();
}
}
/**
* Whether the process this service runs in should be temporarily whitelisted to start
* activities from background depends on the current state of both
* {@code hasStartedWhitelistingBgActivityStarts} and
* {@code hasBindingWhitelistingBgActivityStarts}. If either is true, this ServiceRecord
* should be contributing as a token in parent ProcessRecord.
*
* @see com.android.server.am.ProcessRecord#mAllowBackgroundActivityStartsTokens
*/
private void updateParentProcessBgActivityStartsWhitelistingToken() {
if (app == null) {
return;
}
if (hasStartedWhitelistingBgActivityStarts || hasBindingWhitelistingBgActivityStarts) {
// if the token is already there it's safe to "re-add it" - we're deadling with
// a set of Binder objects
app.addAllowBackgroundActivityStartsToken(this);
} else {
app.removeAllowBackgroundActivityStartsToken(this);
}
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);

View File

@@ -99,8 +99,8 @@ public class AccessibilityServiceConnectionTest {
public void bind_requestsContextToBindService() {
mConnection.bindLocked();
verify(mMockContext).bindServiceAsUser(any(Intent.class), eq(mConnection),
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE),
any(UserHandle.class));
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS), any(UserHandle.class));
}
@Test

View File

@@ -357,7 +357,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
intent.setComponent(mComponent);
mBound = mContext.bindServiceAsUser(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUser));
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
if (!mBound) {
Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
}

View File

@@ -159,7 +159,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
mBindIntent.setComponent(mSessionComponentName);
mBound = mContext.bindServiceAsUser(mBindIntent, this,
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
| Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
| Context.BIND_ALLOW_OOM_MANAGEMENT
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
if (mBound) {
try {
mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY);
@@ -191,7 +192,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY
| Context.BIND_FOREGROUND_SERVICE,
| Context.BIND_FOREGROUND_SERVICE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
new UserHandle(mUser));
}