Initial stab at background check.
Actually, this implementation is more what we want for ephemeral
apps. I am realizing the two are not really the same thing. :(
For this implementation, we now keep track of how long a uid has
been in the background, and after a certain amount of time
(currently 1 minute) we mark it as "idle". Any packages associated
with that uid are then no longer allowed to run in the background.
This means, until the app next goes in the foreground:
- No manifest broadcast receivers in the app will execute.
- No services can be started (binding services is still okay,
as this is outside dependencies on the app that should still
be represented).
- All alarms for the app are cancelled and no more can be set.
- All jobs for the app are cancelled and no more can be scheduled.
- All syncs for the app are cancelled and no more can be requested.
Change-Id: If53714ca4beed35faf2e89f916ce9eaaabd9290d
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 65de4ca..00bba2d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -327,12 +327,39 @@
/** @hide Process is being cached for later use and is empty. */
public static final int PROCESS_STATE_CACHED_EMPTY = 16;
+ /** @hide Should this process state be considered a background state? */
+ public static final boolean isProcStateBackground(int procState) {
+ return procState >= PROCESS_STATE_BACKUP;
+ }
+
/** @hide requestType for assist context: only basic information. */
public static final int ASSIST_CONTEXT_BASIC = 0;
/** @hide requestType for assist context: generate full AssistStructure. */
public static final int ASSIST_CONTEXT_FULL = 1;
+ /** @hide Flag for registerUidObserver: report changes in process state. */
+ public static final int UID_OBSERVER_PROCSTATE = 1<<0;
+
+ /** @hide Flag for registerUidObserver: report uid gone. */
+ public static final int UID_OBSERVER_GONE = 1<<1;
+
+ /** @hide Flag for registerUidObserver: report uid has become idle. */
+ public static final int UID_OBSERVER_IDLE = 1<<2;
+
+ /** @hide Flag for registerUidObserver: report uid has become active. */
+ public static final int UID_OBSERVER_ACTIVE = 1<<3;
+
+ /** @hide Mode for {@link IActivityManager#getAppStartMode}: normal free-to-run operation. */
+ public static final int APP_START_MODE_NORMAL = 0;
+
+ /** @hide Mode for {@link IActivityManager#getAppStartMode}: delay running until later. */
+ public static final int APP_START_MODE_DELAYED = 1;
+
+ /** @hide Mode for {@link IActivityManager#getAppStartMode}: disable/cancel pending
+ * launches. */
+ public static final int APP_START_MODE_DISABLED = 2;
+
/**
* Lock task mode is not active.
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4449e4f..e246e620 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -756,7 +756,7 @@
return true;
}
- case MOVE_TOP_ACTIVITY_TO_PINNED_STACK: {
+ case MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int stackId = data.readInt();
final Rect r = Rect.CREATOR.createFromParcel(data);
@@ -2029,7 +2029,8 @@
data.enforceInterface(IActivityManager.descriptor);
IUidObserver observer = IUidObserver.Stub.asInterface(
data.readStrongBinder());
- registerUidObserver(observer);
+ int which = data.readInt();
+ registerUidObserver(observer, which);
return true;
}
@@ -2698,13 +2699,22 @@
reply.writeNoException();
return true;
}
- case REMOVE_STACK: {
+ case REMOVE_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int stackId = data.readInt();
removeStack(stackId);
reply.writeNoException();
return true;
}
+ case GET_APP_START_MODE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int uid = data.readInt();
+ final String pkg = data.readString();
+ int res = getAppStartMode(uid, pkg);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -3579,7 +3589,7 @@
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
r.writeToParcel(data, 0);
- mRemote.transact(MOVE_TOP_ACTIVITY_TO_PINNED_STACK, data, reply, 0);
+ mRemote.transact(MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION, data, reply, 0);
reply.readException();
final boolean res = reply.readInt() != 0;
data.recycle();
@@ -5326,11 +5336,12 @@
reply.recycle();
}
- public void registerUidObserver(IUidObserver observer) throws RemoteException {
+ public void registerUidObserver(IUidObserver observer, int which) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ data.writeInt(which);
mRemote.transact(REGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -6292,11 +6303,26 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
- mRemote.transact(REMOVE_STACK, data, reply, 0);
+ mRemote.transact(REMOVE_STACK_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
+ @Override
+ public int getAppStartMode(int uid, String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ data.writeString(packageName);
+ mRemote.transact(GET_APP_START_MODE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 77a9795..f0453e9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -237,8 +237,10 @@
public static final int OP_TURN_SCREEN_ON = 61;
/** @hide Get device accounts. */
public static final int OP_GET_ACCOUNTS = 62;
+ /** @hide Control whether an application is allowed to run in the background. */
+ public static final int OP_RUN_IN_BACKGROUND = 63;
/** @hide */
- public static final int _NUM_OP = 63;
+ public static final int _NUM_OP = 64;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -409,6 +411,7 @@
OP_WRITE_EXTERNAL_STORAGE,
OP_TURN_SCREEN_ON,
OP_GET_ACCOUNTS,
+ OP_RUN_IN_BACKGROUND,
};
/**
@@ -478,7 +481,8 @@
OPSTR_READ_EXTERNAL_STORAGE,
OPSTR_WRITE_EXTERNAL_STORAGE,
null,
- OPSTR_GET_ACCOUNTS
+ OPSTR_GET_ACCOUNTS,
+ null,
};
/**
@@ -549,6 +553,7 @@
"WRITE_EXTERNAL_STORAGE",
"TURN_ON_SCREEN",
"GET_ACCOUNTS",
+ "RUN_IN_BACKGROUND",
};
/**
@@ -618,7 +623,8 @@
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
null, // no permission for turning the screen on
- Manifest.permission.GET_ACCOUNTS
+ Manifest.permission.GET_ACCOUNTS,
+ null, // no permission for running in background
};
/**
@@ -690,6 +696,7 @@
null, // WRITE_EXTERNAL_STORAGE
null, // TURN_ON_SCREEN
null, // GET_ACCOUNTS
+ null, // RUN_IN_BACKGROUND
};
/**
@@ -760,6 +767,7 @@
false, // WRITE_EXTERNAL_STORAGE
false, // TURN_ON_SCREEN
false, // GET_ACCOUNTS
+ false, // RUN_IN_BACKGROUND
};
/**
@@ -829,6 +837,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED, // OP_RUN_IN_BACKGROUND
};
/**
@@ -901,7 +910,8 @@
false,
false,
false,
- false
+ false,
+ false,
};
/**
@@ -1329,7 +1339,7 @@
IAppOpsCallback cb = mModeWatchers.get(callback);
if (cb == null) {
cb = new IAppOpsCallback.Stub() {
- public void opChanged(int op, String packageName) {
+ public void opChanged(int op, int uid, String packageName) {
if (callback instanceof OnOpChangedInternalListener) {
((OnOpChangedInternalListener)callback).onOpChanged(op, packageName);
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index b69a480..3d0fc92 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -400,7 +400,7 @@
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
- public void registerUidObserver(IUidObserver observer) throws RemoteException;
+ public void registerUidObserver(IUidObserver observer, int which) throws RemoteException;
public void unregisterUidObserver(IUidObserver observer) throws RemoteException;
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
@@ -542,6 +542,8 @@
public void removeStack(int stackId) throws RemoteException;
+ public int getAppStartMode(int uid, String packageName) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -899,6 +901,7 @@
int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
- int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
- int MOVE_TOP_ACTIVITY_TO_PINNED_STACK = IBinder.FIRST_CALL_TRANSACTION + 349;
+ int REMOVE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 348;
+ int MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 349;
+ int GET_APP_START_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 350;
}
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index 308cb94..fa8d0c9 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -18,6 +18,24 @@
/** {@hide} */
oneway interface IUidObserver {
+ /**
+ * General report of a state change of an uid.
+ */
void onUidStateChanged(int uid, int procState);
+
+ /**
+ * Report that there are no longer any processes running for a uid.
+ */
void onUidGone(int uid);
+
+ /**
+ * Report that a uid is now active (no longer idle).
+ */
+ void onUidActive(int uid);
+
+ /**
+ * Report that a uid is idle -- it has either been running in the background for
+ * a sufficient period of time, or all of its processes have gone away.
+ */
+ void onUidIdle(int uid);
}
diff --git a/core/java/com/android/internal/app/IAppOpsCallback.aidl b/core/java/com/android/internal/app/IAppOpsCallback.aidl
index afbc609..5fdc920 100644
--- a/core/java/com/android/internal/app/IAppOpsCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsCallback.aidl
@@ -19,5 +19,5 @@
// This interface is also used by native code, so must
// be kept in sync with frameworks/native/include/binder/IAppOpsCallback.h
oneway interface IAppOpsCallback {
- void opChanged(int op, String packageName);
+ void opChanged(int op, int uid, String packageName);
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 1355635..3164930 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -166,7 +166,7 @@
updateAppOpsPlayAudio();
// register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
mAppOpsCallback = new IAppOpsCallback.Stub() {
- public void opChanged(int op, String packageName) {
+ public void opChanged(int op, int uid, String packageName) {
synchronized (mLock) {
if (op == AppOpsManager.OP_PLAY_AUDIO) {
updateAppOpsPlayAudio();
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index f9d9950..504a7ef 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -25,6 +25,7 @@
import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
import android.app.IAlarmManager;
+import android.app.IUidObserver;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -41,6 +42,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -459,7 +461,7 @@
long newStart = 0; // recalculate endpoints as we go
long newEnd = Long.MAX_VALUE;
int newFlags = 0;
- for (int i = 0; i < alarms.size(); ) {
+ for (int i = alarms.size()-1; i >= 0; i--) {
Alarm alarm = alarms.get(i);
if (alarm.matches(packageName)) {
alarms.remove(i);
@@ -475,7 +477,42 @@
newEnd = alarm.maxWhenElapsed;
}
newFlags |= alarm.flags;
- i++;
+ }
+ }
+ if (didRemove) {
+ // commit the new batch bounds
+ start = newStart;
+ end = newEnd;
+ flags = newFlags;
+ }
+ return didRemove;
+ }
+
+ boolean removeForStopped(final int uid) {
+ boolean didRemove = false;
+ long newStart = 0; // recalculate endpoints as we go
+ long newEnd = Long.MAX_VALUE;
+ int newFlags = 0;
+ for (int i = alarms.size()-1; i >= 0; i--) {
+ Alarm alarm = alarms.get(i);
+ try {
+ if (alarm.uid == uid && ActivityManagerNative.getDefault().getAppStartMode(
+ uid, alarm.packageName) == ActivityManager.APP_START_MODE_DISABLED) {
+ alarms.remove(i);
+ didRemove = true;
+ if (alarm.alarmClock != null) {
+ mNextAlarmClockMayChange = true;
+ }
+ } else {
+ if (alarm.whenElapsed > newStart) {
+ newStart = alarm.whenElapsed;
+ }
+ if (alarm.maxWhenElapsed < newEnd) {
+ newEnd = alarm.maxWhenElapsed;
+ }
+ newFlags |= alarm.flags;
+ }
+ } catch (RemoteException e) {
}
}
if (didRemove) {
@@ -890,6 +927,13 @@
Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
}
+ try {
+ ActivityManagerNative.getDefault().registerUidObserver(new UidObserver(),
+ ActivityManager.UID_OBSERVER_IDLE);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
publishBinderService(Context.ALARM_SERVICE, mService);
}
@@ -1035,6 +1079,15 @@
Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
callingUid, callingPackage);
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(callingUid, callingPackage)
+ == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
+ + " -- package not allowed to start");
+ return;
+ }
+ } catch (RemoteException e) {
+ }
removeLocked(operation, directReceiver);
setImplLocked(a, false, doValidate);
}
@@ -1841,6 +1894,37 @@
}
}
+ void removeForStoppedLocked(int uid) {
+ boolean didRemove = false;
+ for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+ Batch b = mAlarmBatches.get(i);
+ didRemove |= b.removeForStopped(uid);
+ if (b.size() == 0) {
+ mAlarmBatches.remove(i);
+ }
+ }
+ for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingWhileIdleAlarms.get(i);
+ try {
+ if (a.uid == uid && ActivityManagerNative.getDefault().getAppStartMode(
+ uid, a.packageName) == ActivityManager.APP_START_MODE_DISABLED) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingWhileIdleAlarms.remove(i);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ if (didRemove) {
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "remove(package) changed bounds; rebatching");
+ }
+ rebatchAllAlarmsLocked(true);
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
+ }
+
void removeUserLocked(int userHandle) {
boolean didRemove = false;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
@@ -2673,7 +2757,22 @@
}
}
}
-
+
+ final class UidObserver extends IUidObserver.Stub {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ }
+
+ @Override public void onUidGone(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ removeForStoppedLocked(uid);
+ }
+ };
+
private final BroadcastStats getStatsLocked(PendingIntent pi) {
String pkg = pi.getCreatorPackage();
int uid = pi.getCreatorUid();
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 96c1e2a..c131628 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -54,7 +54,6 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -557,12 +556,12 @@
ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
try {
if (reportedPackageNames == null) {
- callback.mCallback.opChanged(code, null);
+ callback.mCallback.opChanged(code, uid, null);
} else {
final int reportedPackageCount = reportedPackageNames.size();
for (int j = 0; j < reportedPackageCount; j++) {
String reportedPackageName = reportedPackageNames.valueAt(j);
- callback.mCallback.opChanged(code, reportedPackageName);
+ callback.mCallback.opChanged(code, uid, reportedPackageName);
}
}
} catch (RemoteException e) {
@@ -620,7 +619,7 @@
try {
for (int i = 0; i < repCbs.size(); i++) {
try {
- repCbs.get(i).mCallback.opChanged(code, packageName);
+ repCbs.get(i).mCallback.opChanged(code, uid, packageName);
} catch (RemoteException e) {
}
}
@@ -630,39 +629,51 @@
}
}
- private static HashMap<Callback, ArrayList<Pair<String, Integer>>> addCallbacks(
- HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks,
- String packageName, int op, ArrayList<Callback> cbs) {
+ private static HashMap<Callback, ArrayList<ChangeRec>> addCallbacks(
+ HashMap<Callback, ArrayList<ChangeRec>> callbacks,
+ int op, int uid, String packageName, ArrayList<Callback> cbs) {
if (cbs == null) {
return callbacks;
}
if (callbacks == null) {
- callbacks = new HashMap<Callback, ArrayList<Pair<String, Integer>>>();
+ callbacks = new HashMap<>();
}
boolean duplicate = false;
for (int i=0; i<cbs.size(); i++) {
Callback cb = cbs.get(i);
- ArrayList<Pair<String, Integer>> reports = callbacks.get(cb);
+ ArrayList<ChangeRec> reports = callbacks.get(cb);
if (reports == null) {
- reports = new ArrayList<Pair<String, Integer>>();
+ reports = new ArrayList<>();
callbacks.put(cb, reports);
} else {
final int reportCount = reports.size();
for (int j = 0; j < reportCount; j++) {
- Pair<String, Integer> report = reports.get(j);
- if (report.second == op && report.first.equals(packageName)) {
+ ChangeRec report = reports.get(j);
+ if (report.op == op && report.pkg.equals(packageName)) {
duplicate = true;
break;
}
}
}
if (!duplicate) {
- reports.add(new Pair<>(packageName, op));
+ reports.add(new ChangeRec(op, uid, packageName));
}
}
return callbacks;
}
+ static final class ChangeRec {
+ final int op;
+ final int uid;
+ final String pkg;
+
+ ChangeRec(int _op, int _uid, String _pkg) {
+ op = _op;
+ uid = _uid;
+ pkg = _pkg;
+ }
+ }
+
@Override
public void resetAllModes(int reqUserId, String reqPackageName) {
final int callingPid = Binder.getCallingPid();
@@ -682,7 +693,7 @@
}
}
- HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
+ HashMap<Callback, ArrayList<ChangeRec>> callbacks = null;
synchronized (this) {
boolean changed = false;
for (int i = mUidStates.size() - 1; i >= 0; i--) {
@@ -699,9 +710,9 @@
uidState.opModes = null;
}
for (String packageName : getPackagesForUid(uidState.uid)) {
- callbacks = addCallbacks(callbacks, packageName, code,
+ callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
mOpModeWatchers.get(code));
- callbacks = addCallbacks(callbacks, packageName, code,
+ callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
mPackageModeWatchers.get(packageName));
}
}
@@ -734,9 +745,9 @@
&& curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
changed = true;
- callbacks = addCallbacks(callbacks, packageName, curOp.op,
+ callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
mOpModeWatchers.get(curOp.op));
- callbacks = addCallbacks(callbacks, packageName, curOp.op,
+ callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
mPackageModeWatchers.get(packageName));
if (curOp.time == 0 && curOp.rejectTime == 0) {
pkgOps.removeAt(j);
@@ -757,13 +768,13 @@
}
}
if (callbacks != null) {
- for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
+ for (Map.Entry<Callback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) {
Callback cb = ent.getKey();
- ArrayList<Pair<String, Integer>> reports = ent.getValue();
+ ArrayList<ChangeRec> reports = ent.getValue();
for (int i=0; i<reports.size(); i++) {
- Pair<String, Integer> rep = reports.get(i);
+ ChangeRec rep = reports.get(i);
try {
- cb.mCallback.opChanged(rep.second, rep.first);
+ cb.mCallback.opChanged(rep.op, rep.uid, rep.pkg);
} catch (RemoteException e) {
}
}
@@ -1163,8 +1174,10 @@
if (pkgUid != uid) {
// Oops! The package name is not valid for the uid they are calling
// under. Abort.
+ RuntimeException ex = new RuntimeException("here");
+ ex.fillInStackTrace();
Slog.w(TAG, "Bad call: specified package " + packageName
- + " under uid " + uid + " but it is really " + pkgUid);
+ + " under uid " + uid + " but it is really " + pkgUid, ex);
return null;
}
} finally {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 30565c6..4d05f9a 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -168,13 +168,10 @@
*/
class ServiceMap extends Handler {
final int mUserId;
- final ArrayMap<ComponentName, ServiceRecord> mServicesByName
- = new ArrayMap<ComponentName, ServiceRecord>();
- final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent
- = new ArrayMap<Intent.FilterComparison, ServiceRecord>();
+ final ArrayMap<ComponentName, ServiceRecord> mServicesByName = new ArrayMap<>();
+ final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent = new ArrayMap<>();
- final ArrayList<ServiceRecord> mDelayedStartList
- = new ArrayList<ServiceRecord>();
+ final ArrayList<ServiceRecord> mDelayedStartList = new ArrayList<>();
/* XXX eventually I'd like to have this based on processes instead of services.
* That is, if we try to start two services in a row both running in the same
* process, this should be one entry in mStartingBackground for that one process
@@ -185,8 +182,7 @@
= new ArrayList<DelayingProcess>();
*/
- final ArrayList<ServiceRecord> mStartingBackground
- = new ArrayList<ServiceRecord>();
+ final ArrayList<ServiceRecord> mStartingBackground = new ArrayList<>();
static final int MSG_BG_START_TIMEOUT = 1;
@@ -338,7 +334,7 @@
ServiceRecord r = res.record;
if (!mAm.mUserController.exists(r.userId)) {
- Slog.d(TAG, "Trying to start service with non-existent user! " + r.userId);
+ Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
return null;
}
@@ -510,6 +506,35 @@
return 0;
}
+ void stopInBackgroundLocked(int uid) {
+ // Stop all services associated with this uid due to it going to the background
+ // stopped state.
+ ServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));
+ ArrayList<ServiceRecord> stopping = null;
+ if (services != null) {
+ for (int i=services.mServicesByName.size()-1; i>=0; i--) {
+ ServiceRecord service = services.mServicesByName.valueAt(i);
+ if (service.appInfo.uid == uid && service.startRequested) {
+ if (mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
+ uid, service.packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (stopping == null) {
+ stopping = new ArrayList<>();
+ stopping.add(service);
+ }
+ }
+ }
+ }
+ if (stopping != null) {
+ for (int i=stopping.size()-1; i>=0; i--) {
+ ServiceRecord service = stopping.get(i);
+ service.delayed = false;
+ services.ensureNotStartingBackground(service);
+ stopServiceLocked(service);
+ }
+ }
+ }
+ }
+
IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(),
@@ -1069,6 +1094,17 @@
}
r = smap.mServicesByName.get(name);
if (r == null && createIfNeeded) {
+ // Before going further -- if this app is not allowed to run in the background,
+ // then at this point we aren't going to let it period.
+ if (!mAm.checkAllowBackgroundLocked(sInfo.applicationInfo.uid,
+ sInfo.packageName, callingPid)) {
+ Slog.w(TAG, "Background execution not allowed: service "
+ + r.intent + " to " + name.flattenToShortString()
+ + " from pid=" + callingPid + " uid=" + callingUid
+ + " pkg=" + callingPackage);
+ return null;
+ }
+
Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
ServiceRestarter res = new ServiceRestarter();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 566065c..f0bcf0c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -60,7 +60,6 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
-import android.content.pm.AppsQueryHelper;
import android.content.pm.PermissionInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -88,6 +87,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.DumpHeapActivity;
+import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
@@ -109,15 +109,12 @@
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
-import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.ActivityStackSupervisor.ActivityDisplay;
import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.Installer;
-import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
@@ -388,6 +385,10 @@
// Maximum number of users we allow to be running at a time.
static final int MAX_RUNNING_USERS = 3;
+ // This is the amount of time we allow an app to settle after it goes into the background,
+ // before we start restricting what it can do.
+ static final int BACKGROUND_SETTLE_TIME = 1*60*1000;
+
// How long to wait in getAssistContextExtras for the activity and foreground services
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
@@ -716,6 +717,12 @@
final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
/**
+ * This is for verifying the UID report flow.
+ */
+ static final boolean VALIDATE_UID_STATES = true;
+ final SparseArray<UidRecord> mValidateUids = new SparseArray<>();
+
+ /**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
@@ -1310,29 +1317,29 @@
}
}
- static final int SHOW_ERROR_MSG = 1;
- static final int SHOW_NOT_RESPONDING_MSG = 2;
- static final int SHOW_FACTORY_ERROR_MSG = 3;
+ static final int SHOW_ERROR_UI_MSG = 1;
+ static final int SHOW_NOT_RESPONDING_UI_MSG = 2;
+ static final int SHOW_FACTORY_ERROR_UI_MSG = 3;
static final int UPDATE_CONFIGURATION_MSG = 4;
static final int GC_BACKGROUND_PROCESSES_MSG = 5;
- static final int WAIT_FOR_DEBUGGER_MSG = 6;
+ static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
- static final int SHOW_UID_ERROR_MSG = 14;
- static final int SHOW_FINGERPRINT_ERROR_MSG = 15;
+ static final int SHOW_UID_ERROR_UI_MSG = 14;
+ static final int SHOW_FINGERPRINT_ERROR_UI_MSG = 15;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
static final int POST_HEAVY_NOTIFICATION_MSG = 24;
static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
- static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+ static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26;
static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
static final int CLEAR_DNS_CACHE_MSG = 28;
static final int UPDATE_HTTP_PROXY_MSG = 29;
- static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30;
- static final int DISPATCH_PROCESSES_CHANGED = 31;
- static final int DISPATCH_PROCESS_DIED = 32;
+ static final int SHOW_COMPAT_MODE_DIALOG_UI_MSG = 30;
+ static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
+ static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
static final int REPORT_USER_SWITCH_MSG = 34;
static final int CONTINUE_USER_SWITCH_MSG = 35;
@@ -1346,20 +1353,21 @@
static final int SYSTEM_USER_CURRENT_MSG = 43;
static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
static final int FINISH_BOOTING_MSG = 45;
- static final int START_USER_SWITCH_MSG = 46;
+ static final int START_USER_SWITCH_UI_MSG = 46;
static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
- static final int DISMISS_DIALOG_MSG = 48;
+ static final int DISMISS_DIALOG_UI_MSG = 48;
static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 49;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 50;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 51;
static final int DELETE_DUMPHEAP_MSG = 52;
static final int FOREGROUND_PROFILE_CHANGED_MSG = 53;
- static final int DISPATCH_UIDS_CHANGED_MSG = 54;
+ static final int DISPATCH_UIDS_CHANGED_UI_MSG = 54;
static final int REPORT_TIME_TRACKER_MSG = 55;
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
static final int APP_BOOST_DEACTIVATE_MSG = 58;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59;
+ static final int IDLE_UIDS_MSG = 60;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1394,7 +1402,7 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case SHOW_ERROR_MSG: {
+ case SHOW_ERROR_UI_MSG: {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
@@ -1439,7 +1447,7 @@
ensureBootCompleted();
} break;
- case SHOW_NOT_RESPONDING_MSG: {
+ case SHOW_NOT_RESPONDING_UI_MSG: {
synchronized (ActivityManagerService.this) {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
ProcessRecord proc = (ProcessRecord)data.get("app");
@@ -1471,7 +1479,7 @@
ensureBootCompleted();
} break;
- case SHOW_STRICT_MODE_VIOLATION_MSG: {
+ case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
synchronized (ActivityManagerService.this) {
ProcessRecord proc = (ProcessRecord) data.get("app");
@@ -1497,13 +1505,13 @@
}
ensureBootCompleted();
} break;
- case SHOW_FACTORY_ERROR_MSG: {
+ case SHOW_FACTORY_ERROR_UI_MSG: {
Dialog d = new FactoryErrorDialog(
mContext, msg.getData().getCharSequence("msg"));
d.show();
ensureBootCompleted();
} break;
- case WAIT_FOR_DEBUGGER_MSG: {
+ case WAIT_FOR_DEBUGGER_UI_MSG: {
synchronized (ActivityManagerService.this) {
ProcessRecord app = (ProcessRecord)msg.obj;
if (msg.arg1 != 0) {
@@ -1523,7 +1531,7 @@
}
}
} break;
- case SHOW_UID_ERROR_MSG: {
+ case SHOW_UID_ERROR_UI_MSG: {
if (mShowDialogs) {
AlertDialog d = new BaseErrorDialog(mContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
@@ -1531,11 +1539,11 @@
d.setTitle(mContext.getText(R.string.android_system_label));
d.setMessage(mContext.getText(R.string.system_error_wipe_data));
d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok),
- obtainMessage(DISMISS_DIALOG_MSG, d));
+ obtainMessage(DISMISS_DIALOG_UI_MSG, d));
d.show();
}
} break;
- case SHOW_FINGERPRINT_ERROR_MSG: {
+ case SHOW_FINGERPRINT_ERROR_UI_MSG: {
if (mShowDialogs) {
AlertDialog d = new BaseErrorDialog(mContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
@@ -1543,11 +1551,11 @@
d.setTitle(mContext.getText(R.string.android_system_label));
d.setMessage(mContext.getText(R.string.system_error_manufacturer));
d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok),
- obtainMessage(DISMISS_DIALOG_MSG, d));
+ obtainMessage(DISMISS_DIALOG_UI_MSG, d));
d.show();
}
} break;
- case SHOW_COMPAT_MODE_DIALOG_MSG: {
+ case SHOW_COMPAT_MODE_DIALOG_UI_MSG: {
synchronized (ActivityManagerService.this) {
ActivityRecord ar = (ActivityRecord) msg.obj;
if (mCompatModeDialog != null) {
@@ -1575,26 +1583,26 @@
}
break;
}
- case START_USER_SWITCH_MSG: {
+ case START_USER_SWITCH_UI_MSG: {
mUserController.showUserSwitchDialog(msg.arg1, (String) msg.obj);
break;
}
- case DISMISS_DIALOG_MSG: {
+ case DISMISS_DIALOG_UI_MSG: {
final Dialog d = (Dialog) msg.obj;
d.dismiss();
break;
}
- case DISPATCH_PROCESSES_CHANGED: {
+ case DISPATCH_PROCESSES_CHANGED_UI_MSG: {
dispatchProcessesChanged();
break;
}
- case DISPATCH_PROCESS_DIED: {
+ case DISPATCH_PROCESS_DIED_UI_MSG: {
final int pid = msg.arg1;
final int uid = msg.arg2;
dispatchProcessDied(pid, uid);
break;
}
- case DISPATCH_UIDS_CHANGED_MSG: {
+ case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
}
@@ -2046,7 +2054,7 @@
// it is finished we make sure it is reset to its default.
mUserIsMonkey = false;
} break;
- case APP_BOOST_DEACTIVATE_MSG : {
+ case APP_BOOST_DEACTIVATE_MSG: {
synchronized(ActivityManagerService.this) {
if (mIsBoosted) {
if (mBoostStartTime < (SystemClock.uptimeMillis() - APP_BOOST_TIMEOUT)) {
@@ -2060,6 +2068,9 @@
}
}
} break;
+ case IDLE_UIDS_MSG: {
+ idleUids();
+ } break;
}
}
};
@@ -2352,6 +2363,17 @@
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"), mHandler);
+ mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null,
+ new IAppOpsCallback.Stub() {
+ @Override public void opChanged(int op, int uid, String packageName) {
+ if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
+ if (mAppOpsService.checkOperation(op, uid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ runInBackgroundDisabled(uid);
+ }
+ }
+ }
+ });
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
@@ -2766,7 +2788,7 @@
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
- msg.what = SHOW_COMPAT_MODE_DIALOG_MSG;
+ msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
msg.obj = r.task.askedCompatMode ? null : r;
mUiHandler.sendMessage(msg);
}
@@ -3797,8 +3819,10 @@
for (int i=0; i<N; i++) {
final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
mActiveUidChanges[i] = change;
- change.uidRecord.pendingChange = null;
- change.uidRecord = null;
+ if (change.uidRecord != null) {
+ change.uidRecord.pendingChange = null;
+ change.uidRecord = null;
+ }
}
mPendingUidChanges.clear();
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
@@ -3808,7 +3832,8 @@
if (mLocalPowerManager != null) {
for (int j=0; j<N; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
- if (item.gone) {
+ if (item.change == UidRecord.CHANGE_GONE
+ || item.change == UidRecord.CHANGE_GONE_IDLE) {
mLocalPowerManager.uidGone(item.uid);
} else {
mLocalPowerManager.updateUidProcState(item.uid, item.processState);
@@ -3820,19 +3845,66 @@
while (i > 0) {
i--;
final IUidObserver observer = mUidObservers.getBroadcastItem(i);
+ final int which = (Integer)mUidObservers.getBroadcastCookie(i);
if (observer != null) {
try {
for (int j=0; j<N; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
- if (item.gone) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID gone uid=" + item.uid);
- observer.onUidGone(item.uid);
+ final int change = item.change;
+ UidRecord validateUid = null;
+ if (VALIDATE_UID_STATES && i == 0) {
+ validateUid = mValidateUids.get(item.uid);
+ if (validateUid == null && change != UidRecord.CHANGE_GONE
+ && change != UidRecord.CHANGE_GONE_IDLE) {
+ validateUid = new UidRecord(item.uid);
+ mValidateUids.put(item.uid, validateUid);
+ }
+ }
+ if (change == UidRecord.CHANGE_IDLE
+ || change == UidRecord.CHANGE_GONE_IDLE) {
+ if ((which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID idle uid=" + item.uid);
+ observer.onUidIdle(item.uid);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ if (validateUid != null) {
+ validateUid.idle = true;
+ }
+ }
+ } else if (change == UidRecord.CHANGE_ACTIVE) {
+ if ((which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID active uid=" + item.uid);
+ observer.onUidActive(item.uid);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ validateUid.idle = false;
+ }
+ }
+ if (change == UidRecord.CHANGE_GONE
+ || change == UidRecord.CHANGE_GONE_IDLE) {
+ if ((which & ActivityManager.UID_OBSERVER_GONE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID gone uid=" + item.uid);
+ observer.onUidGone(item.uid);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ if (validateUid != null) {
+ mValidateUids.remove(item.uid);
+ }
+ }
} else {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID CHANGED uid=" + item.uid
- + ": " + item.processState);
- observer.onUidStateChanged(item.uid, item.processState);
+ if ((which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID CHANGED uid=" + item.uid
+ + ": " + item.processState);
+ observer.onUidStateChanged(item.uid, item.processState);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ validateUid.curProcState = validateUid.setProcState
+ = item.processState;
+ }
}
}
} catch (RemoteException e) {
@@ -5102,7 +5174,7 @@
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
- msg.what = SHOW_NOT_RESPONDING_MSG;
+ msg.what = SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
@@ -5881,7 +5953,7 @@
// No more processes using this uid, tell clients it is gone.
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"No more processes in " + old.uidRecord);
- enqueueUidChangeLocked(old.uidRecord, true);
+ enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
mActiveUids.remove(uid);
}
old.uidRecord = null;
@@ -5907,7 +5979,7 @@
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
mActiveUids.put(proc.uid, uidRec);
- enqueueUidChangeLocked(uidRec, false);
+ enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
proc.uidRecord = uidRec;
uidRec.numProcs++;
@@ -6715,9 +6787,11 @@
if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
+ Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
return null;
}
if (activity.finishing) {
+ Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
return null;
}
}
@@ -7269,6 +7343,36 @@
return readMet && writeMet;
}
+ public int getAppStartMode(int uid, String packageName) {
+ synchronized (this) {
+ boolean bg = checkAllowBackgroundLocked(uid, packageName, -1);
+ return bg ? ActivityManager.APP_START_MODE_NORMAL
+ : ActivityManager.APP_START_MODE_DISABLED;
+ }
+ }
+
+ boolean checkAllowBackgroundLocked(int uid, String packageName, int callingPid) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec == null || uidRec.idle) {
+ if (callingPid >= 0) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(callingPid);
+ }
+ if (proc != null && proc.curProcState < ActivityManager.PROCESS_STATE_RECEIVER) {
+ // Whoever is instigating this is in the foreground, so we will allow it
+ // to go through.
+ return true;
+ }
+ }
+ if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private ProviderInfo getProviderInfoLocked(String authority, int userHandle) {
ProviderInfo pi = null;
ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
@@ -8268,7 +8372,7 @@
if (app == null) return;
Message msg = Message.obtain();
- msg.what = WAIT_FOR_DEBUGGER_MSG;
+ msg.what = WAIT_FOR_DEBUGGER_UI_MSG;
msg.obj = app;
msg.arg1 = waiting ? 1 : 0;
mUiHandler.sendMessage(msg);
@@ -11213,11 +11317,12 @@
}
}
- public void registerUidObserver(IUidObserver observer) {
+ @Override
+ public void registerUidObserver(IUidObserver observer, int which) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerUidObserver()");
synchronized (this) {
- mUidObservers.register(observer);
+ mUidObservers.register(observer, which);
}
}
@@ -12056,7 +12161,7 @@
mTopData = null;
mTopComponent = null;
Message msg = Message.obtain();
- msg.what = SHOW_FACTORY_ERROR_MSG;
+ msg.what = SHOW_FACTORY_ERROR_UI_MSG;
msg.getData().putCharSequence("msg", errorMsg);
mUiHandler.sendMessage(msg);
}
@@ -12110,14 +12215,14 @@
if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
+ " data partition or your device will be unstable.");
- mUiHandler.obtainMessage(SHOW_UID_ERROR_MSG).sendToTarget();
+ mUiHandler.obtainMessage(SHOW_UID_ERROR_UI_MSG).sendToTarget();
}
} catch (RemoteException e) {
}
if (!Build.isBuildConsistent()) {
Slog.e(TAG, "Build fingerprint is not consistent, warning user");
- mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_MSG).sendToTarget();
+ mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_UI_MSG).sendToTarget();
}
long ident = Binder.clearCallingIdentity();
@@ -12398,7 +12503,7 @@
final long origId = Binder.clearCallingIdentity();
Message msg = Message.obtain();
- msg.what = SHOW_STRICT_MODE_VIOLATION_MSG;
+ msg.what = SHOW_STRICT_MODE_VIOLATION_UI_MSG;
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("result", result);
data.put("app", r);
@@ -12855,7 +12960,7 @@
}
Message msg = Message.obtain();
- msg.what = SHOW_ERROR_MSG;
+ msg.what = SHOW_ERROR_UI_MSG;
HashMap data = new HashMap();
data.put("result", result);
data.put("app", r);
@@ -13484,6 +13589,39 @@
}
}
+ boolean dumpUids(PrintWriter pw, String dumpPackage, SparseArray<UidRecord> uids,
+ String header, boolean needSep) {
+ boolean printed = false;
+ int whichAppId = -1;
+ if (dumpPackage != null) {
+ try {
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
+ dumpPackage, 0);
+ whichAppId = UserHandle.getAppId(info.uid);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ for (int i=0; i<uids.size(); i++) {
+ UidRecord uidRec = uids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
+ continue;
+ }
+ if (!printed) {
+ printed = true;
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" ");
+ pw.println(header);
+ needSep = true;
+ }
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(": "); pw.println(uidRec);
+ }
+ return printed;
+ }
+
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -13540,33 +13678,13 @@
}
if (mActiveUids.size() > 0) {
- boolean printed = false;
- int whichAppId = -1;
- if (dumpPackage != null) {
- try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
- dumpPackage, 0);
- whichAppId = UserHandle.getAppId(info.uid);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- }
+ if (dumpUids(pw, dumpPackage, mActiveUids, "UID states:", needSep)) {
+ printedAnything = needSep = true;
}
- for (int i=0; i<mActiveUids.size(); i++) {
- UidRecord uidRec = mActiveUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
- continue;
- }
- if (!printed) {
- printed = true;
- if (needSep) {
- pw.println();
- }
- pw.println(" UID states:");
- needSep = true;
- printedAnything = true;
- }
- pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
- pw.print(": "); pw.println(uidRec);
+ }
+ if (mValidateUids.size() > 0) {
+ if (dumpUids(pw, dumpPackage, mValidateUids, "UID validation:", needSep)) {
+ printedAnything = needSep = true;
}
}
@@ -14457,6 +14575,9 @@
if (object1.first.setAdj != object2.first.setAdj) {
return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
}
+ if (object1.first.setProcState != object2.first.setProcState) {
+ return object1.first.setProcState > object2.first.setProcState ? -1 : 1;
+ }
if (object1.second.intValue() != object2.second.intValue()) {
return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
}
@@ -15829,7 +15950,8 @@
mAvailProcessChanges.add(item);
}
}
- mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid,
+ null).sendToTarget();
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
@@ -19020,7 +19142,7 @@
if (mPendingProcessChanges.size() == 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Enqueueing dispatch processes changed!");
- mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG).sendToTarget();
}
mPendingProcessChanges.add(item);
}
@@ -19039,29 +19161,46 @@
return success;
}
- private final void enqueueUidChangeLocked(UidRecord uidRec, boolean gone) {
- if (uidRec.pendingChange == null) {
+ private final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
+ final UidRecord.ChangeItem pendingChange;
+ if (uidRec == null || uidRec.pendingChange == null) {
if (mPendingUidChanges.size() == 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"*** Enqueueing dispatch uid changed!");
- mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_MSG).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_UI_MSG).sendToTarget();
}
final int NA = mAvailUidChanges.size();
if (NA > 0) {
- uidRec.pendingChange = mAvailUidChanges.remove(NA-1);
+ pendingChange = mAvailUidChanges.remove(NA-1);
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Retrieving available item: " + uidRec.pendingChange);
+ "Retrieving available item: " + pendingChange);
} else {
- uidRec.pendingChange = new UidRecord.ChangeItem();
+ pendingChange = new UidRecord.ChangeItem();
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Allocating new item: " + uidRec.pendingChange);
+ "Allocating new item: " + pendingChange);
}
- uidRec.pendingChange.uidRecord = uidRec;
- uidRec.pendingChange.uid = uidRec.uid;
- mPendingUidChanges.add(uidRec.pendingChange);
+ if (uidRec != null) {
+ uidRec.pendingChange = pendingChange;
+ if (change == UidRecord.CHANGE_GONE && !uidRec.idle) {
+ // If this uid is going away, and we haven't yet reported it is gone,
+ // then do so now.
+ change = UidRecord.CHANGE_GONE_IDLE;
+ }
+ } else if (uid < 0) {
+ throw new IllegalArgumentException("No UidRecord or uid");
+ }
+ pendingChange.uidRecord = uidRec;
+ pendingChange.uid = uidRec != null ? uidRec.uid : uid;
+ mPendingUidChanges.add(pendingChange);
+ } else {
+ pendingChange = uidRec.pendingChange;
+ if (change == UidRecord.CHANGE_GONE && pendingChange.change == UidRecord.CHANGE_IDLE) {
+ change = UidRecord.CHANGE_GONE_IDLE;
+ }
}
- uidRec.pendingChange.gone = gone;
- uidRec.pendingChange.processState = uidRec.setProcState;
+ pendingChange.change = change;
+ pendingChange.processState = uidRec != null
+ ? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
}
private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
@@ -19608,12 +19747,31 @@
// Update from any uid changes.
for (int i=mActiveUids.size()-1; i>=0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
+ int uidChange = UidRecord.CHANGE_PROCSTATE;
if (uidRec.setProcState != uidRec.curProcState) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Changes in " + uidRec + ": proc state from " + uidRec.setProcState
+ " to " + uidRec.curProcState);
+ if (ActivityManager.isProcStateBackground(uidRec.curProcState)) {
+ if (!ActivityManager.isProcStateBackground(uidRec.setProcState)) {
+ uidRec.lastBackgroundTime = nowElapsed;
+ if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {
+ // Note: the background settle time is in elapsed realtime, while
+ // the handler time base is uptime. All this means is that we may
+ // stop background uids later than we had intended, but that only
+ // happens because the device was sleeping so we are okay anyway.
+ mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, BACKGROUND_SETTLE_TIME);
+ }
+ }
+ } else {
+ if (uidRec.idle) {
+ uidChange = UidRecord.CHANGE_ACTIVE;
+ uidRec.idle = false;
+ }
+ uidRec.lastBackgroundTime = 0;
+ }
uidRec.setProcState = uidRec.curProcState;
- enqueueUidChangeLocked(uidRec, false);
+ enqueueUidChangeLocked(uidRec, -1, uidChange);
}
}
@@ -19638,6 +19796,53 @@
}
}
+ final void idleUids() {
+ synchronized (this) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long maxBgTime = nowElapsed - BACKGROUND_SETTLE_TIME;
+ long nextTime = 0;
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ final long bgTime = uidRec.lastBackgroundTime;
+ if (bgTime > 0 && !uidRec.idle) {
+ if (bgTime <= maxBgTime) {
+ uidRec.idle = true;
+ doStopUidLocked(uidRec.uid, uidRec);
+ } else {
+ if (nextTime == 0 || nextTime > bgTime) {
+ nextTime = bgTime;
+ }
+ }
+ }
+ }
+ if (nextTime > 0) {
+ mHandler.removeMessages(IDLE_UIDS_MSG);
+ mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+ nextTime + BACKGROUND_SETTLE_TIME - nowElapsed);
+ }
+ }
+ }
+
+ final void runInBackgroundDisabled(int uid) {
+ synchronized (this) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec != null) {
+ // This uid is actually running... should it be considered background now?
+ if (uidRec.idle) {
+ doStopUidLocked(uidRec.uid, uidRec);
+ }
+ } else {
+ // This uid isn't actually running... still send a report about it being "stopped".
+ doStopUidLocked(uid, null);
+ }
+ }
+ }
+
+ final void doStopUidLocked(int uid, final UidRecord uidRec) {
+ mServices.stopInBackgroundLocked(uid);
+ enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
+ }
+
final void trimApplications() {
synchronized (this) {
int i;
@@ -19973,8 +20178,9 @@
userName = userInfo.name;
mUserController.setTargetUserIdLocked(userId);
}
- mUiHandler.removeMessages(START_USER_SWITCH_MSG);
- mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));
+ mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
+ mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_UI_MSG, userId, 0,
+ userName));
return true;
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index d317791..fb37eda 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -556,6 +556,17 @@
+ " (uid " + r.callingUid + ")");
skip = true;
}
+ if (!skip) {
+ if (!mService.checkAllowBackgroundLocked(filter.receiverList.uid, filter.packageName,
+ -1)) {
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")");
+ skip = true;
+ }
+ }
if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, filter.receiverList.uid)) {
@@ -938,6 +949,15 @@
skip = true;
}
if (!skip) {
+ if (!mService.checkAllowBackgroundLocked(info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName, -1)) {
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ skip = true;
+ }
+ }
+ if (!skip) {
skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index b4efbf0..d24c3a5 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -17,7 +17,9 @@
package com.android.server.am;
import android.app.ActivityManager;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.util.TimeUtils;
/**
* Overall information about a uid that has actively running processes.
@@ -26,12 +28,20 @@
final int uid;
int curProcState;
int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ long lastBackgroundTime;
+ boolean idle;
int numProcs;
+ static final int CHANGE_PROCSTATE = 0;
+ static final int CHANGE_GONE = 1;
+ static final int CHANGE_GONE_IDLE = 2;
+ static final int CHANGE_IDLE = 3;
+ static final int CHANGE_ACTIVE = 4;
+
static final class ChangeItem {
UidRecord uidRecord;
int uid;
- boolean gone;
+ int change;
int processState;
}
@@ -54,9 +64,16 @@
UserHandle.formatUid(sb, uid);
sb.append(' ');
sb.append(ProcessList.makeProcStateString(curProcState));
- sb.append(" / ");
+ if (lastBackgroundTime > 0) {
+ sb.append(" bg:");
+ TimeUtils.formatDuration(SystemClock.elapsedRealtime()-lastBackgroundTime, sb);
+ }
+ if (idle) {
+ sb.append(" idle");
+ }
+ sb.append(" procs:");
sb.append(numProcs);
- sb.append(" procs}");
+ sb.append("}");
return sb.toString();
}
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index b766894..75a74c0 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -466,7 +466,7 @@
if (cname == null) {
info = new SyncStorageEngine.EndPoint(account, authority, userId);
} else {
- info = new SyncStorageEngine.EndPoint(cname, userId);
+ info = new SyncStorageEngine.EndPoint(cname, userId, -1);
}
syncManager.clearScheduledSyncOperations(info);
syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e2a0f82..4f53882 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -21,8 +21,10 @@
import android.accounts.AccountManager;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppGlobals;
+import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -79,6 +81,7 @@
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
@@ -237,7 +240,7 @@
private final AppIdleMonitor mAppIdleMonitor;
- private BroadcastReceiver mStorageIntentReceiver =
+ private final BroadcastReceiver mStorageIntentReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -260,7 +263,7 @@
}
};
- private BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
boolean idle = mPowerManager.isDeviceIdleMode()
|| mPowerManager.isLightDeviceIdleMode();
@@ -281,7 +284,7 @@
}
};
- private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mBootCompleted = true;
@@ -289,7 +292,7 @@
}
};
- private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateRunningAccounts();
@@ -300,6 +303,21 @@
}
};
+ private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ }
+
+ @Override public void onUidGone(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ cancelSyncsForUid(uid);
+ }
+ };
+
private final PowerManager mPowerManager;
DeviceIdleController.LocalService mLocalDeviceIdleController;
@@ -504,6 +522,13 @@
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ try {
+ ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_IDLE);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -661,6 +686,26 @@
Log.d(TAG, "one off sync for: " + cname + " " + extras.toString());
}
+ final android.content.pm.ServiceInfo sinfo;
+ try {
+ sinfo = mContext.getPackageManager().getServiceInfo(cname, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Not scheduling sync " + cname
+ + " -- can't find service for user " + userId);
+ return;
+ }
+ final int sUid = sinfo.applicationInfo.uid;
+
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(sUid, cname.getPackageName())
+ == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not scheduling sync " + sUid + ":" + cname
+ + " -- package not allowed to start");
+ return;
+ }
+ } catch (RemoteException e) {
+ }
+
Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
if (expedited) {
runtimeMillis = -1; // this means schedule at the front of the queue
@@ -688,7 +733,7 @@
}
return;
}
- SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId);
+ SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId, sUid);
Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
long delayUntil = mSyncStorageEngine.getDelayUntilTime(info);
final long backoffTime = backoff != null ? backoff.first : 0;
@@ -702,7 +747,7 @@
+ ", extras " + extras);
}
scheduleSyncOperation(
- new SyncOperation(cname, userId, uid, source, extras,
+ new SyncOperation(cname, userId, sUid, cname.getPackageName(), uid, source, extras,
runtimeMillis /* runtime */,
beforeRunTimeMillis /* flextime */,
backoffTime,
@@ -837,6 +882,18 @@
if (syncAdapterInfo == null) {
continue;
}
+ final int owningUid = syncAdapterInfo.uid;
+ final String owningPackage = syncAdapterInfo.componentName.getPackageName();
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(owningUid,
+ owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
+ + syncAdapterInfo.componentName
+ + " -- package not allowed to start");
+ return;
+ }
+ } catch (RemoteException e) {
+ }
final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
if (isSyncable < 0 && isAlwaysSyncable) {
@@ -886,7 +943,8 @@
+ ", extras " + newExtras);
}
scheduleSyncOperation(
- new SyncOperation(account.account, account.userId, reason, source,
+ new SyncOperation(account.account, account.userId,
+ owningUid, owningPackage, reason, source,
authority, newExtras, 0 /* immediate */, 0 /* No flex time*/,
backoffTime, delayUntil, allowParallelSyncs));
}
@@ -902,7 +960,8 @@
+ ", extras " + extras);
}
scheduleSyncOperation(
- new SyncOperation(account.account, account.userId, reason, source,
+ new SyncOperation(account.account, account.userId,
+ owningUid, owningPackage, reason, source,
authority, extras, runtimeMillis, beforeRuntimeMillis,
backoffTime, delayUntil, allowParallelSyncs));
}
@@ -1309,6 +1368,14 @@
}
}
+ void cancelSyncsForUid(int uid) {
+ synchronized (mSyncQueue) {
+ if (mSyncQueue.removeUidIfNeededLocked(uid)) {
+ sendCheckAlarmsMessage();
+ }
+ }
+ }
+
/**
* @hide
*/
@@ -2166,8 +2233,8 @@
/**
* Stash any messages that come to the handler before boot is complete or before the device
* is properly provisioned (i.e. out of set-up wizard).
- * {@link #onBootCompleted()} and {@link #onDeviceProvisioned(boolean)} both need to come
- * in before we start syncing.
+ * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
+ * need to come in before we start syncing.
* @param msg Message to dispatch at a later point.
* @return true if a message was enqueued, false otherwise. This is to avoid losing the
* message if we manage to acquire the lock but by the time we do boot has completed.
@@ -2503,6 +2570,8 @@
}
scheduleSyncOperation(
new SyncOperation(target.account, target.userId,
+ syncAdapterInfo.uid,
+ syncAdapterInfo.componentName.getPackageName(),
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC,
target.provider, extras,
@@ -2513,6 +2582,7 @@
} else if (target.target_service) {
scheduleSyncOperation(
new SyncOperation(target.service, target.userId,
+ target.serviceUid, target.service.getPackageName(),
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC,
extras,
@@ -3145,6 +3215,7 @@
if (syncResult != null && syncResult.fullSyncRequested) {
scheduleSyncOperation(
new SyncOperation(info.account, info.userId,
+ syncOperation.owningUid, syncOperation.owningPackage,
syncOperation.reason,
syncOperation.syncSource, info.provider, new Bundle(),
0 /* delay */, 0 /* flex */,
@@ -3155,6 +3226,7 @@
if (syncResult != null && syncResult.fullSyncRequested) {
scheduleSyncOperation(
new SyncOperation(info.service, info.userId,
+ syncOperation.owningUid, syncOperation.owningPackage,
syncOperation.reason,
syncOperation.syncSource, new Bundle(),
0 /* delay */, 0 /* flex */,
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 10efe81..ab777ae 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -22,6 +22,7 @@
import android.content.ContentResolver;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
/**
@@ -62,6 +63,8 @@
/** Identifying info for the target for this operation. */
public final SyncStorageEngine.EndPoint target;
+ public final int owningUid;
+ public final String owningPackage;
/** Why this sync was kicked off. {@link #REASON_NAMES} */
public final int reason;
/** Where this sync was initiated. */
@@ -93,25 +96,28 @@
/** Whether this sync op was recently skipped due to the app being idle */
public boolean appIdle;
- public SyncOperation(Account account, int userId, int reason, int source, String provider,
- Bundle extras, long runTimeFromNow, long flexTime, long backoff,
- long delayUntil, boolean allowParallelSyncs) {
- this(new SyncStorageEngine.EndPoint(account, provider, userId),
+ public SyncOperation(Account account, int userId, int owningUid, String owningPackage,
+ int reason, int source, String provider, Bundle extras, long runTimeFromNow,
+ long flexTime, long backoff, long delayUntil, boolean allowParallelSyncs) {
+ this(new SyncStorageEngine.EndPoint(account, provider, userId), owningUid, owningPackage,
reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
allowParallelSyncs);
}
- public SyncOperation(ComponentName service, int userId, int reason, int source,
- Bundle extras, long runTimeFromNow, long flexTime, long backoff,
+ public SyncOperation(ComponentName service, int userId, int owningUid, String owningPackage,
+ int reason, int source, Bundle extras, long runTimeFromNow, long flexTime, long backoff,
long delayUntil) {
- this(new SyncStorageEngine.EndPoint(service, userId), reason, source, extras,
- runTimeFromNow, flexTime, backoff, delayUntil, true /* allowParallelSyncs */);
+ this(new SyncStorageEngine.EndPoint(service, userId, owningUid), owningUid, owningPackage,
+ reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
+ true /* allowParallelSyncs */);
}
- private SyncOperation(SyncStorageEngine.EndPoint info, int reason, int source, Bundle extras,
- long runTimeFromNow, long flexTime, long backoff, long delayUntil,
- boolean allowParallelSyncs) {
+ private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
+ int reason, int source, Bundle extras, long runTimeFromNow, long flexTime,
+ long backoff, long delayUntil, boolean allowParallelSyncs) {
this.target = info;
+ this.owningUid = owningUid;
+ this.owningPackage = owningPackage;
this.reason = reason;
this.syncSource = source;
this.extras = new Bundle(extras);
@@ -142,7 +148,8 @@
/** Used to reschedule a sync at a new point in time. */
public SyncOperation(SyncOperation other, long newRunTimeFromNow) {
- this(other.target, other.reason, other.syncSource, new Bundle(other.extras),
+ this(other.target, other.owningUid, other.owningPackage, other.reason, other.syncSource,
+ new Bundle(other.extras),
newRunTimeFromNow,
0L /* In back-off so no flex */,
other.backoff,
@@ -228,6 +235,13 @@
}
sb.append(", reason: ");
sb.append(reasonToString(pm, reason));
+ if (!useOneLine) {
+ sb.append("\n ");
+ sb.append("owningUid=");
+ UserHandle.formatUid(sb, owningUid);
+ sb.append(" owningPackage=");
+ sb.append(owningPackage);
+ }
if (!useOneLine && !extras.keySet().isEmpty()) {
sb.append("\n ");
extrasToStringBuilder(extras, sb);
diff --git a/services/core/java/com/android/server/content/SyncQueue.java b/services/core/java/com/android/server/content/SyncQueue.java
index 587de1c..b15d0d8 100644
--- a/services/core/java/com/android/server/content/SyncQueue.java
+++ b/services/core/java/com/android/server/content/SyncQueue.java
@@ -16,16 +16,20 @@
package com.android.server.content;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.content.pm.PackageManager;
import android.content.SyncAdapterType;
import android.content.SyncAdaptersCache;
import android.content.pm.RegisteredServicesCache.ServiceInfo;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import com.google.android.collect.Maps;
import java.util.ArrayList;
@@ -74,8 +78,9 @@
continue;
}
operationToAdd = new SyncOperation(
- info.account, info.userId, op.reason, op.syncSource, info.provider,
- op.extras,
+ info.account, info.userId, syncAdapterInfo.uid,
+ syncAdapterInfo.componentName.getPackageName(), op.reason,
+ op.syncSource, info.provider, op.extras,
op.expedited ? -1 : 0 /* delay */,
0 /* flex */,
backoff != null ? backoff.first : 0L,
@@ -84,16 +89,24 @@
operationToAdd.pendingOperation = op;
add(operationToAdd, op);
} else if (info.target_service) {
+ android.content.pm.ServiceInfo sinfo;
try {
- mPackageManager.getServiceInfo(info.service, 0);
+ sinfo = mPackageManager.getServiceInfo(info.service, info.userId);
} catch (PackageManager.NameNotFoundException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.w(TAG, "Missing sync service for authority " + op.target);
}
continue;
}
+ if (sinfo == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.w(TAG, "Missing sync service for authority " + op.target);
+ }
+ continue;
+ }
operationToAdd = new SyncOperation(
- info.service, info.userId, op.reason, op.syncSource,
+ info.service, info.userId, sinfo.applicationInfo.uid,
+ info.service.getPackageName(), op.reason, op.syncSource,
op.extras,
op.expedited ? -1 : 0 /* delay */,
0 /* flex */,
@@ -161,9 +174,38 @@
opsToRemove.add(op);
}
}
- for (SyncOperation op : opsToRemove) {
- remove(op);
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ }
+
+ public boolean removeUidIfNeededLocked(int uid) {
+ ArrayList<SyncOperation> opsToRemove = null;
+ for (SyncOperation op : mOperationsMap.values()) {
+ if (op.owningUid != uid) {
+ continue;
}
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(op.owningUid,
+ op.owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Removing sync " + op.owningUid + ":" + op
+ + " -- package not allowed to start");
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
+ if (opsToRemove == null) {
+ opsToRemove = new ArrayList<SyncOperation>();
+ }
+ opsToRemove.add(op);
+ }
+ if (opsToRemove == null) {
+ return false;
+ }
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index cca0c16..8266c08 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -27,6 +27,7 @@
import android.content.SyncInfo;
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -42,6 +43,7 @@
import android.util.AtomicFile;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.ArrayMap;
import android.util.Xml;
@@ -227,14 +229,16 @@
public final static EndPoint USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL =
new EndPoint(null, null, UserHandle.USER_ALL);
final ComponentName service;
+ final int serviceUid; // -1 for "any"
final Account account;
final int userId;
final String provider;
final boolean target_service;
final boolean target_provider;
- public EndPoint(ComponentName service, int userId) {
+ public EndPoint(ComponentName service, int userId, int uid) {
this.service = service;
+ this.serviceUid = uid;
this.userId = userId;
this.account = null;
this.provider = null;
@@ -247,6 +251,7 @@
this.provider = provider;
this.userId = userId;
this.service = null;
+ this.serviceUid = -1;
this.target_service = false;
this.target_provider = true;
}
@@ -264,6 +269,11 @@
return false;
}
if (target_service && spec.target_service) {
+ if (serviceUid != spec.serviceUid
+ && serviceUid >= 0
+ && spec.serviceUid >= 0) {
+ return false;
+ }
return service.equals(spec.service);
} else if (target_provider && spec.target_provider) {
boolean accountsMatch;
@@ -290,8 +300,9 @@
.append("/")
.append(provider == null ? "ALL PDRS" : provider);
} else if (target_service) {
- sb.append(service.getPackageName() + "/")
- .append(service.getClassName());
+ service.appendShortString(sb);
+ sb.append(":");
+ UserHandle.formatUid(sb,serviceUid);
} else {
sb.append("invalid target");
}
@@ -737,7 +748,7 @@
synchronized (mAuthorities) {
if (cname != null) {
AuthorityInfo authority = getAuthorityLocked(
- new EndPoint(cname, userId),
+ new EndPoint(cname, userId, -1),
"get service active");
if (authority == null) {
return false;
@@ -749,7 +760,7 @@
}
public void setIsTargetServiceActive(ComponentName cname, int userId, boolean active) {
- setSyncableStateForEndPoint(new EndPoint(cname, userId), active ?
+ setSyncableStateForEndPoint(new EndPoint(cname, userId, -1), active ?
AuthorityInfo.SYNCABLE : AuthorityInfo.NOT_SYNCABLE);
}
@@ -2064,18 +2075,30 @@
new Account(accountName, accountType),
authorityName, userId);
} else {
- info = new EndPoint(
- new ComponentName(packageName, className),
- userId);
+ final ComponentName cname = new ComponentName(packageName, className);
+ android.content.pm.ServiceInfo sinfo = null;
+ try {
+ sinfo = mContext.getPackageManager().getServiceInfo(cname, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Not restoring sync " + cname
+ + " -- can't find service for user " + userId);
+ }
+ if (sinfo != null) {
+ info = new EndPoint(cname, userId, sinfo.applicationInfo.uid);
+ } else {
+ info = null;
+ }
}
- authority = getOrCreateAuthorityLocked(info, id, false);
- // If the version is 0 then we are upgrading from a file format that did not
- // know about periodic syncs. In that case don't clear the list since we
- // want the default, which is a daily periodic sync.
- // Otherwise clear out this default list since we will populate it later with
- // the periodic sync descriptions that are read from the configuration file.
- if (version > 0) {
- authority.periodicSyncs.clear();
+ if (info != null) {
+ authority = getOrCreateAuthorityLocked(info, id, false);
+ // If the version is 0 then we are upgrading from a file format that did not
+ // know about periodic syncs. In that case don't clear the list since we
+ // want the default, which is a daily periodic sync.
+ // Otherwise clear out this default list since we will populate it later with
+ // the periodic sync descriptions that are read from the configuration file.
+ if (version > 0) {
+ authority.periodicSyncs.clear();
+ }
}
}
if (authority != null) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 759199c..4d7df9c 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -23,7 +23,9 @@
import java.util.List;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.IUidObserver;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.app.job.JobService;
@@ -85,6 +87,7 @@
static final int MSG_JOB_EXPIRED = 0;
static final int MSG_CHECK_JOB = 1;
+ static final int MSG_STOP_JOB = 2;
// Policy constants
/**
@@ -162,7 +165,7 @@
if (DEBUG) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
- cancelJobsForUid(uidRemoved);
+ cancelJobsForUid(uidRemoved, true);
}
} else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -180,6 +183,21 @@
}
};
+ final private IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ }
+
+ @Override public void onUidGone(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ cancelJobsForUid(uid, false);
+ }
+ };
+
@Override
public void onStartUser(int userHandle) {
mStartedUsers.add(userHandle);
@@ -202,6 +220,15 @@
public int schedule(JobInfo job, int uId) {
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(uId,
+ job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
+ + " -- package not allowed to start");
+ return JobScheduler.RESULT_FAILURE;
+ }
+ } catch (RemoteException e) {
+ }
startTrackingJob(jobStatus);
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
@@ -237,14 +264,26 @@
* This will remove the job from the master list, and cancel the job if it was staged for
* execution or being executed.
* @param uid Uid to check against for removal of a job.
+ * @param forceAll If true, all jobs for the uid will be canceled; if false, only those
+ * whose apps are stopped.
*/
- public void cancelJobsForUid(int uid) {
+ public void cancelJobsForUid(int uid, boolean forceAll) {
List<JobStatus> jobsForUid;
synchronized (mJobs) {
jobsForUid = mJobs.getJobsByUid(uid);
}
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
+ if (!forceAll) {
+ String packageName = toRemove.getServiceComponent().getPackageName();
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
+ != ActivityManager.APP_START_MODE_DISABLED) {
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
+ }
cancelJobImpl(toRemove);
}
}
@@ -384,6 +423,12 @@
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
+ try {
+ ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_IDLE);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mJobs) {
// Let's go!
@@ -635,6 +680,9 @@
maybeQueueReadyJobsForExecutionLockedH();
}
break;
+ case MSG_STOP_JOB:
+ cancelJobImpl((JobStatus)message.obj);
+ break;
}
maybeRunPendingJobsH();
// Don't remove JOB_EXPIRED in case one came along while processing the queue.
@@ -686,11 +734,22 @@
int idleCount = 0;
int backoffCount = 0;
int connectivityCount = 0;
- List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
+ List<JobStatus> runnableJobs = null;
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
if (isReadyToBeExecutedLocked(job)) {
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
+ job.getJob().getService().getPackageName())
+ == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Aborting job " + job.getUid() + ":"
+ + job.getJob().toString() + " -- package not allowed to start");
+ mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
if (job.getNumFailures() > 0) {
backoffCount++;
}
@@ -703,6 +762,9 @@
if (job.hasChargingConstraint()) {
chargingCount++;
}
+ if (runnableJobs == null) {
+ runnableJobs = new ArrayList<>();
+ }
runnableJobs.add(job);
} else if (isReadyToBeCancelledLocked(job)) {
stopJobOnServiceContextLocked(job);
@@ -712,7 +774,7 @@
idleCount >= MIN_IDLE_COUNT ||
connectivityCount >= MIN_CONNECTIVITY_COUNT ||
chargingCount >= MIN_CHARGING_COUNT ||
- runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
+ (runnableJobs != null && runnableJobs.size() >= MIN_READY_JOBS_COUNT)) {
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
}
@@ -908,7 +970,7 @@
long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJobsForUid(uid);
+ JobSchedulerService.this.cancelJobsForUid(uid, true);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 41aea04..2ac0ba6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -459,7 +459,8 @@
updateScreenOn();
try {
- mActivityManager.registerUidObserver(mUidObserver);
+ mActivityManager.registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE);
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
@@ -541,6 +542,12 @@
removeUidStateLocked(uid);
}
}
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ }
};
final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() {
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index b0296a0..c174a92 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -61,7 +61,7 @@
b2.putBoolean("b2", true);
SyncOperation op1 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
@@ -73,7 +73,7 @@
// Same as op1 but different time infos
SyncOperation op2 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
@@ -85,7 +85,7 @@
// Same as op1 but different authority
SyncOperation op3 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority2",
b1,
@@ -97,7 +97,7 @@
// Same as op1 but different account
SyncOperation op4 = new SyncOperation(account2, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
@@ -109,7 +109,7 @@
// Same as op1 but different bundle
SyncOperation op5 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b2,
@@ -131,21 +131,21 @@
long soonFlex = 50;
long after = 1500;
long afterFlex = 100;
- SyncOperation op1 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ SyncOperation op1 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
"authority1", mEmpty, soon, soonFlex, mUnimportantLong, mUnimportantLong, true);
// Interval disjoint from and after op1.
- SyncOperation op2 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ SyncOperation op2 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
"authority1", mEmpty, after, afterFlex, mUnimportantLong, mUnimportantLong, true);
// Interval equivalent to op1, but expedited.
Bundle b2 = new Bundle();
b2.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- SyncOperation op3 = new SyncOperation(mDummy, 0, 0, 0,
+ SyncOperation op3 = new SyncOperation(mDummy, 0, 0, "foo", 0, 0,
"authority1", b2, -1, soonFlex, mUnimportantLong, mUnimportantLong, true);
// Interval overlaps but not equivalent to op1.
- SyncOperation op4 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ SyncOperation op4 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
"authority1", mEmpty, soon + 100, soonFlex + 100, mUnimportantLong, mUnimportantLong, true);
assertTrue(op1.compareTo(op2) == -1);
@@ -165,7 +165,8 @@
Bundle withExpedited = new Bundle();
withExpedited.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- SyncOperation op = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_USER_START,
+ SyncOperation op = new SyncOperation(mDummy, 0, 0, "foo", 0,
+ SyncOperation.REASON_USER_START,
mAuthority, withExpedited, fiveSecondsFromNow, twoSecondsFlex,
eightSeconds /* backoff */, fourSeconds /* delayUntil */, true);
// Create another sync op to be rerun in 5 minutes.
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index ae1967e4..b22eb53 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -41,8 +41,6 @@
import java.io.FileOutputStream;
import java.util.List;
-import com.android.server.content.SyncStorageEngine.EndPoint;
-
public class SyncStorageEngineTest extends AndroidTestCase {
protected Account account1;
@@ -96,7 +94,7 @@
SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
new TestContext(mockResolver, getContext()));
long time0 = 1000;
- SyncOperation op = new SyncOperation(account, 0,
+ SyncOperation op = new SyncOperation(account, 0, 0, "foo",
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL,
authority,
@@ -112,7 +110,7 @@
@MediumTest
public void testAppendPending() throws Exception {
SyncOperation sop = new SyncOperation(account1,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
0 /* runtime */, 0 /* flex */, 0 /* backoff */, 0 /* delayuntil */,
@@ -140,19 +138,19 @@
*/
public void testWritePendingOperationsLocked() throws Exception {
SyncOperation sop = new SyncOperation(account1,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_IS_SYNCABLE,
SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
1000L /* runtime */, 57L /* flex */, 0 /* backoff */, 0 /* delayuntil */,
true /* expedited */);
SyncOperation sop1 = new SyncOperation(account2,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL, authority1, defaultBundle,
0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
false /* expedited */);
SyncOperation deleted = new SyncOperation(account2,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_SYNC_AUTO,
SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
@@ -456,14 +454,14 @@
// Test service component read
List<PeriodicSync> syncs = engine.getPeriodicSyncs(
- new SyncStorageEngine.EndPoint(syncService1, 0));
+ new SyncStorageEngine.EndPoint(syncService1, 0, 0));
assertEquals(1, syncs.size());
assertEquals(true, engine.getIsTargetServiceActive(syncService1, 0));
}
@SmallTest
public void testComponentSettings() throws Exception {
- EndPoint target1 = new EndPoint(syncService1, 0);
+ EndPoint target1 = new EndPoint(syncService1, 0, 0);
engine.updateOrAddPeriodicSync(target1, dayPoll, dayFuzz, Bundle.EMPTY);
engine.setIsTargetServiceActive(target1.service, 0, true);