Update power manager to track uid state like netstats.
To follow the correct semantics for when restricts due to
device idle can be applied, power manager need to know about
uid process states like net policy so that it can allow
wake locks from apps that are in the foreground.
Since this is being added to a second place, I reworked things
so that the activity manager now keeps track of per-uid process
states and allows apps to register to listen to those, rather
than having to track lower-level process states and transform
them into an overall uid state. Both net policy and power
manager use this new facility.
Change-Id: I77359164c40d0f36fe1ef296dd9f9c3062431148
diff --git a/Android.mk b/Android.mk
index eb6e7b5..146afe0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -84,6 +84,7 @@
core/java/android/app/job/IJobScheduler.aidl \
core/java/android/app/job/IJobService.aidl \
core/java/android/app/ITransientNotification.aidl \
+ core/java/android/app/IUidObserver.aidl \
core/java/android/app/IUiAutomationConnection.aidl \
core/java/android/app/IUiModeManager.aidl \
core/java/android/app/IUserSwitchObserver.aidl \
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1e9bc54..c83a45b 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1970,6 +1970,22 @@
return true;
}
+ case REGISTER_UID_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IUidObserver observer = IUidObserver.Stub.asInterface(
+ data.readStrongBinder());
+ registerUidObserver(observer);
+ return true;
+ }
+
+ case UNREGISTER_UID_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IUidObserver observer = IUidObserver.Stub.asInterface(
+ data.readStrongBinder());
+ unregisterUidObserver(observer);
+ return true;
+ }
+
case GET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -5084,6 +5100,28 @@
reply.recycle();
}
+ public void registerUidObserver(IUidObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(REGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void unregisterUidObserver(IUidObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 05a936c..a7c5cb9 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -387,6 +387,9 @@
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
+ public void registerUidObserver(IUidObserver observer) throws RemoteException;
+ public void unregisterUidObserver(IUidObserver observer) throws RemoteException;
+
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteException;
@@ -846,4 +849,6 @@
int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
int UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
+ int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
+ int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+299;
}
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
new file mode 100644
index 0000000..308cb94
--- /dev/null
+++ b/core/java/android/app/IUidObserver.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/** {@hide} */
+oneway interface IUidObserver {
+ void onUidStateChanged(int uid, int procState);
+ void onUidGone(int uid);
+}
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 9a0d0d0..e523285 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -136,4 +136,8 @@
public abstract void setDeviceIdleMode(boolean enabled);
public abstract void setDeviceIdleWhitelist(int[] appids);
+
+ public abstract void updateUidProcState(int uid, int procState);
+
+ public abstract void uidGone(int uid);
}
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 92e874f..dc965ed 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -134,6 +134,24 @@
}
/**
+ * @hide
+ * Removes the mapping from the specified key, if there was any, returning the old value.
+ */
+ public E removeReturnOld(int key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ final E old = (E) mValues[i];
+ mValues[i] = DELETED;
+ mGarbage = true;
+ return old;
+ }
+ }
+ return null;
+ }
+
+ /**
* Alias for {@link #delete(int)}.
*/
public void remove(int key) {
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 6ff0304..23a8bd7 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -39,14 +39,16 @@
return value;
}
- public void remove(String name, int uid) {
+ public E remove(String name, int uid) {
SparseArray<E> uids = mMap.get(name);
if (uids != null) {
- uids.remove(uid);
+ final E old = uids.removeReturnOld(uid);
if (uids.size() == 0) {
mMap.remove(name);
}
+ return old;
}
+ return null;
}
public ArrayMap<String, SparseArray<E>> getMap() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 925fae0a..ec2bd67 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -71,6 +71,7 @@
static final boolean DEBUG_TASKS = DEBUG_ALL || false;
static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false;
static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
+ static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
@@ -104,6 +105,8 @@
static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
static final String POSTFIX_THUMBNAILS = (APPEND_CATEGORY_NAME) ? "_Thumbnails" : "";
static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
+ static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME)
+ ? "_UidObservers" : "";
static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a8ab667..caa4058 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -129,6 +129,7 @@
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
+import android.app.IUidObserver;
import android.app.IUiAutomationConnection;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
@@ -253,7 +254,6 @@
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- private static final String USER_DATA_DIR = "/data/user/";
// File that stores last updated system version and called preboot receivers
static final String CALLED_PRE_BOOTS_FILENAME = "called_pre_boots.dat";
@@ -278,6 +278,7 @@
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
@@ -655,9 +656,14 @@
long mPreviousProcessVisibleTime;
/**
+ * Track all uids that have actively running processes.
+ */
+ final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
+
+ /**
* Which uses have been started, so are allowed to run code.
*/
- final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
+ final SparseArray<UserStartedState> mStartedUsers = new SparseArray<>();
/**
* LRU list of history of current users. Most recently current is at the end.
@@ -1023,6 +1029,11 @@
private IVoiceInteractionSession mRunningVoice;
/**
+ * For some direct access we need to power manager.
+ */
+ PowerManagerInternal mLocalPowerManager;
+
+ /**
* We want to hold a wake lock while running a voice interaction session, since
* this may happen with the screen off and we need to keep the CPU running to
* be able to continue to interact with the user.
@@ -1174,7 +1185,7 @@
final long[] mTmpLong = new long[1];
- static class ProcessChangeItem {
+ static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
static final int CHANGE_PROCESS_STATE = 1<<1;
int changes;
@@ -1184,14 +1195,17 @@
boolean foregroundActivities;
}
- final RemoteCallbackList<IProcessObserver> mProcessObservers
- = new RemoteCallbackList<IProcessObserver>();
+ final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
- final ArrayList<ProcessChangeItem> mPendingProcessChanges
- = new ArrayList<ProcessChangeItem>();
- final ArrayList<ProcessChangeItem> mAvailProcessChanges
- = new ArrayList<ProcessChangeItem>();
+ final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>();
+ final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>();
+
+ final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
+ UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5];
+
+ final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
+ final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
/**
* Runtime CPU use collection thread. This object's lock is used to
@@ -1316,6 +1330,7 @@
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 = 54;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1538,6 +1553,19 @@
d.dismiss();
break;
}
+ case DISPATCH_PROCESSES_CHANGED: {
+ dispatchProcessesChanged();
+ break;
+ }
+ case DISPATCH_PROCESS_DIED: {
+ final int pid = msg.arg1;
+ final int uid = msg.arg2;
+ dispatchProcessDied(pid, uid);
+ break;
+ }
+ case DISPATCH_UIDS_CHANGED: {
+ dispatchUidsChanged();
+ } break;
}
}
}
@@ -1723,16 +1751,6 @@
sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
}
} break;
- case DISPATCH_PROCESSES_CHANGED: {
- dispatchProcessesChanged();
- break;
- }
- case DISPATCH_PROCESS_DIED: {
- final int pid = msg.arg1;
- final int uid = msg.arg2;
- dispatchProcessDied(pid, uid);
- break;
- }
case REPORT_MEM_USAGE_MSG: {
final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj;
Thread thread = new Thread() {
@@ -2091,7 +2109,6 @@
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
- mProcessNames.put(app.processName, app.uid, app);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
@@ -2343,6 +2360,7 @@
public void initPowerManagement() {
mStackSupervisor.initPowerManagement();
mBatteryStatsService.initPowerManagement();
+ mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
mVoiceWakeLock.setReferenceCounted(false);
@@ -3073,10 +3091,6 @@
return null;
}
app.crashHandler = crashHandler;
- mProcessNames.put(processName, app.uid, app);
- if (isolated) {
- mIsolatedProcesses.put(app.uid, app);
- }
checkTime(startTime, "startProcess: done creating new process record");
} else {
// If this is a new package in the process, add the package to the list
@@ -3562,7 +3576,6 @@
mActiveProcessChanges = new ProcessChangeItem[N];
}
mPendingProcessChanges.toArray(mActiveProcessChanges);
- mAvailProcessChanges.addAll(mPendingProcessChanges);
mPendingProcessChanges.clear();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Delivering " + N + " process changes");
@@ -3595,6 +3608,12 @@
}
}
mProcessObservers.finishBroadcast();
+
+ synchronized (this) {
+ for (int j=0; j<N; j++) {
+ mAvailProcessChanges.add(mActiveProcessChanges[j]);
+ }
+ }
}
private void dispatchProcessDied(int pid, int uid) {
@@ -3612,6 +3631,67 @@
mProcessObservers.finishBroadcast();
}
+ private void dispatchUidsChanged() {
+ int N;
+ synchronized (this) {
+ N = mPendingUidChanges.size();
+ if (mActiveUidChanges.length < N) {
+ mActiveUidChanges = new UidRecord.ChangeItem[N];
+ }
+ for (int i=0; i<N; i++) {
+ final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
+ mActiveUidChanges[i] = change;
+ change.uidRecord.pendingChange = null;
+ change.uidRecord = null;
+ }
+ mPendingUidChanges.clear();
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "*** Delivering " + N + " uid changes");
+ }
+
+ if (mLocalPowerManager != null) {
+ for (int j=0; j<N; j++) {
+ UidRecord.ChangeItem item = mActiveUidChanges[j];
+ if (item.gone) {
+ mLocalPowerManager.uidGone(item.uid);
+ } else {
+ mLocalPowerManager.updateUidProcState(item.uid, item.processState);
+ }
+ }
+ }
+
+ int i = mUidObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IUidObserver observer = mUidObservers.getBroadcastItem(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);
+ } else {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID CHANGED uid=" + item.uid
+ + ": " + item.processState);
+ observer.onUidStateChanged(item.uid, item.processState);
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mUidObservers.finishBroadcast();
+
+ synchronized (this) {
+ for (int j=0; j<N; j++) {
+ mAvailUidChanges.add(mActiveUidChanges[j]);
+ }
+ }
+ }
+
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
@@ -5623,6 +5703,47 @@
return didSomething;
}
+ private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+ ProcessRecord old = mProcessNames.remove(name, uid);
+ if (old != null) {
+ old.uidRecord.numProcs--;
+ if (old.uidRecord.numProcs == 0) {
+ // 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);
+ mActiveUids.remove(uid);
+ }
+ old.uidRecord = null;
+ }
+ mIsolatedProcesses.remove(uid);
+ return old;
+ }
+
+ private final void addProcessNameLocked(ProcessRecord proc) {
+ // We shouldn't already have a process under this name, but just in case we
+ // need to clean up whatever may be there now.
+ ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+ if (old != null) {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
+ UidRecord uidRec = mActiveUids.get(proc.uid);
+ if (uidRec == null) {
+ uidRec = new UidRecord(proc.uid);
+ // This is the first appearance of the uid, report it now!
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Creating new process uid: " + uidRec);
+ mActiveUids.put(proc.uid, uidRec);
+ enqueueUidChangeLocked(uidRec, false);
+ }
+ proc.uidRecord = uidRec;
+ uidRec.numProcs++;
+ mProcessNames.put(proc.processName, proc.uid, proc);
+ if (proc.isolated) {
+ mIsolatedProcesses.put(proc.uid, proc);
+ }
+ }
+
private final boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
@@ -5630,8 +5751,7 @@
if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
"Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
- mProcessNames.remove(name, uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(name, uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -5684,8 +5804,7 @@
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
- mProcessNames.remove(app.processName, app.uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -9885,7 +10004,6 @@
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
- BatteryStatsImpl.Uid.Proc ps = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
@@ -9920,6 +10038,7 @@
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
r.persistent = true;
}
+ addProcessNameLocked(r);
return r;
}
@@ -9934,10 +10053,6 @@
if (app == null) {
app = newProcessRecordLocked(info, null, isolated, 0);
- mProcessNames.put(info.processName, app.uid, app);
- if (isolated) {
- mIsolatedProcesses.put(app.uid, app);
- }
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
@@ -10613,6 +10728,21 @@
}
}
+ public void registerUidObserver(IUidObserver observer) {
+ enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ synchronized (this) {
+ mUidObservers.register(observer);
+ }
+ }
+
+ @Override
+ public void unregisterUidObserver(IUidObserver observer) {
+ synchronized (this) {
+ mUidObservers.unregister(observer);
+ }
+ }
+
@Override
public boolean convertFromTranslucent(IBinder token) {
final long origId = Binder.clearCallingIdentity();
@@ -12932,6 +13062,20 @@
}
}
+ if (mActiveUids.size() > 0) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" UID states:");
+ for (int i=0; i<mActiveUids.size(); i++) {
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(": "); pw.println(uidRec);
+ }
+ needSep = true;
+ printedAnything = true;
+ }
+
if (mLruProcesses.size() > 0) {
if (needSep) {
pw.println();
@@ -15174,7 +15318,7 @@
mAvailProcessChanges.add(item);
}
}
- mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, 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.
@@ -15185,8 +15329,7 @@
if (!app.persistent || app.isolated) {
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
- mProcessNames.remove(app.processName, app.uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -15218,7 +15361,7 @@
if (index < 0) {
ProcessList.remove(app.pid);
}
- mProcessNames.put(app.processName, app.uid, app);
+ addProcessNameLocked(app);
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
@@ -18310,7 +18453,7 @@
if (NA > 0) {
item = mAvailProcessChanges.remove(NA-1);
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Retreiving available item: " + item);
+ "Retrieving available item: " + item);
} else {
item = new ProcessChangeItem();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
@@ -18322,7 +18465,7 @@
if (mPendingProcessChanges.size() == 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Enqueueing dispatch processes changed!");
- mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
}
mPendingProcessChanges.add(item);
}
@@ -18341,6 +18484,31 @@
return success;
}
+ private final void enqueueUidChangeLocked(UidRecord uidRec, boolean gone) {
+ if (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).sendToTarget();
+ }
+ final int NA = mAvailUidChanges.size();
+ if (NA > 0) {
+ uidRec.pendingChange = mAvailUidChanges.remove(NA-1);
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Retrieving available item: " + uidRec.pendingChange);
+ } else {
+ uidRec.pendingChange = new UidRecord.ChangeItem();
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Allocating new item: " + uidRec.pendingChange);
+ }
+ uidRec.pendingChange.uidRecord = uidRec;
+ uidRec.pendingChange.uid = uidRec.uid;
+ mPendingUidChanges.add(uidRec.pendingChange);
+ }
+ uidRec.pendingChange.gone = gone;
+ uidRec.pendingChange.processState = uidRec.setProcState;
+ }
+
private void maybeUpdateUsageStats(ProcessRecord app) {
if (DEBUG_USAGE_STATS) {
Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
@@ -18484,6 +18652,14 @@
Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);
}
+ // Reset state in all uid records.
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Starting update of " + uidRec);
+ uidRec.reset();
+ }
+
mAdjSeq++;
mNewNumServiceProcs = 0;
mNewNumAServiceProcs = 0;
@@ -18631,6 +18807,12 @@
// good to avoid having whatever code was running in them
// left sitting around after no longer needed.
app.kill("isolated not needed", true);
+ } else {
+ // Keeping this process, update its uid.
+ final UidRecord uidRec = app.uidRecord;
+ if (uidRec != null && uidRec.curProcState > app.curProcState) {
+ uidRec.curProcState = app.curProcState;
+ }
}
if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
@@ -18826,6 +19008,18 @@
requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());
}
+ // Update from any uid changes.
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ 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);
+ uidRec.setProcState = uidRec.curProcState;
+ enqueueUidChangeLocked(uidRec, false);
+ }
+ }
+
if (mProcessStats.shouldWriteNowLocked(now)) {
mHandler.post(new Runnable() {
@Override public void run() {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 29e14f8..14759c3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -62,8 +62,8 @@
final int userId; // user of process.
final String processName; // name of the process
// List of packages running in the process
- final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList
- = new ArrayMap<String, ProcessStats.ProcessStateHolder>();
+ final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
+ UidRecord uidRecord; // overall state of process's uid.
ArraySet<String> pkgDeps; // additional packages we have a dependency on
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
@@ -142,24 +142,20 @@
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
- // contains HistoryRecord objects
- final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+ // all activities running in the process
+ final ArrayList<ActivityRecord> activities = new ArrayList<>();
// all ServiceRecord running in this process
- final ArraySet<ServiceRecord> services = new ArraySet<ServiceRecord>();
+ final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
- final ArraySet<ServiceRecord> executingServices
- = new ArraySet<ServiceRecord>();
+ final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
// All ConnectionRecord this process holds
- final ArraySet<ConnectionRecord> connections
- = new ArraySet<ConnectionRecord>();
+ final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// all IIntentReceivers that are registered from this process.
- final ArraySet<ReceiverList> receivers = new ArraySet<ReceiverList>();
+ final ArraySet<ReceiverList> receivers = new ArraySet<>();
// class (String) -> ContentProviderRecord
- final ArrayMap<String, ContentProviderRecord> pubProviders
- = new ArrayMap<String, ContentProviderRecord>();
+ final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
- final ArrayList<ContentProviderConnection> conProviders
- = new ArrayList<ContentProviderConnection>();
+ final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
boolean execServicesFg; // do we need to be executing services in the foreground?
boolean persistent; // always keep this application running?
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
new file mode 100644
index 0000000..b4efbf0
--- /dev/null
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.ActivityManager;
+import android.os.UserHandle;
+
+/**
+ * Overall information about a uid that has actively running processes.
+ */
+public final class UidRecord {
+ final int uid;
+ int curProcState;
+ int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ int numProcs;
+
+ static final class ChangeItem {
+ UidRecord uidRecord;
+ int uid;
+ boolean gone;
+ int processState;
+ }
+
+ ChangeItem pendingChange;
+
+ public UidRecord(int _uid) {
+ uid = _uid;
+ reset();
+ }
+
+ public void reset() {
+ curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("UidRecord{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ UserHandle.formatUid(sb, uid);
+ sb.append(' ');
+ sb.append(ProcessList.makeProcStateString(curProcState));
+ sb.append(" / ");
+ sb.append(numProcs);
+ sb.append(" procs}");
+ return sb.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 24ab3b8..1a79568 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -81,6 +81,7 @@
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
+import android.app.IUidObserver;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
@@ -153,10 +154,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.AppOpsService;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import com.google.android.collect.Lists;
import org.xmlpull.v1.XmlPullParser;
@@ -294,9 +293,8 @@
/** Set of currently active {@link Notification} tags. */
private final ArraySet<String> mActiveNotifs = new ArraySet<String>();
- /** Foreground at both UID and PID granularity. */
+ /** Foreground at UID granularity. */
final SparseIntArray mUidState = new SparseIntArray();
- final SparseArray<SparseIntArray> mUidPidState = new SparseArray<>();
/** The current maximum process state that we are considering to be foreground. */
private int mCurForegroundState = ActivityManager.PROCESS_STATE_TOP;
@@ -411,7 +409,7 @@
updateScreenOn();
try {
- mActivityManager.registerProcessObserver(mProcessObserver);
+ mActivityManager.registerUidObserver(mUidObserver);
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
@@ -477,40 +475,16 @@
}
- private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
- @Override
- public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
- }
-
- @Override
- public void onProcessStateChanged(int pid, int uid, int procState) {
+ private IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
synchronized (mRulesLock) {
- // because a uid can have multiple pids running inside, we need to
- // remember all pid states and summarize foreground at uid level.
-
- // record foreground for this specific pid
- SparseIntArray pidState = mUidPidState.get(uid);
- if (pidState == null) {
- pidState = new SparseIntArray(2);
- mUidPidState.put(uid, pidState);
- }
- pidState.put(pid, procState);
- computeUidStateLocked(uid);
+ updateUidStateLocked(uid, procState);
}
}
- @Override
- public void onProcessDied(int pid, int uid) {
+ @Override public void onUidGone(int uid) throws RemoteException {
synchronized (mRulesLock) {
- // clear records and recompute, when they exist
- final SparseIntArray pidState = mUidPidState.get(uid);
- if (pidState != null) {
- pidState.delete(pid);
- if (pidState.size() <= 0) {
- mUidPidState.remove(uid);
- }
- computeUidStateLocked(uid);
- }
+ removeUidStateLocked(uid);
}
}
};
@@ -1919,14 +1893,6 @@
fout.print(state);
fout.print(state <= mCurForegroundState ? " (fg)" : " (bg)");
- fout.print(" pids=");
- final int foregroundIndex = mUidPidState.indexOfKey(uid);
- if (foregroundIndex < 0) {
- fout.print("UNKNOWN");
- } else {
- dumpSparseIntArray(fout, mUidPidState.valueAt(foregroundIndex));
- }
-
fout.print(" rules=");
final int rulesIndex = mUidRules.indexOfKey(uid);
if (rulesIndex < 0) {
@@ -1957,36 +1923,38 @@
}
/**
- * Process state of PID changed; recompute state at UID level. If
- * changed, will trigger {@link #updateRulesForUidLocked(int)}.
+ * Process state of UID changed; if needed, will trigger
+ * {@link #updateRulesForUidLocked(int)}.
*/
- void computeUidStateLocked(int uid) {
- final SparseIntArray pidState = mUidPidState.get(uid);
-
- // current pid is dropping foreground; examine other pids
- int uidState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- if (pidState != null) {
- final int size = pidState.size();
- for (int i = 0; i < size; i++) {
- final int state = pidState.valueAt(i);
- if (state < uidState) {
- uidState = state;
- }
- }
- }
-
+ void updateUidStateLocked(int uid, int uidState) {
final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (oldUidState != uidState) {
// state changed, push updated rules
mUidState.put(uid, uidState);
- final boolean oldForeground = oldUidState <= mCurForegroundState;
- final boolean newForeground = uidState <= mCurForegroundState;
- if (oldForeground != newForeground) {
- updateRulesForUidLocked(uid);
+ updateRulesForUidStateChangeLocked(uid, oldUidState, uidState);
+ }
+ }
+
+ void removeUidStateLocked(int uid) {
+ final int index = mUidState.indexOfKey(uid);
+ if (index >= 0) {
+ final int oldUidState = mUidState.valueAt(index);
+ mUidState.removeAt(index);
+ if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+ updateRulesForUidStateChangeLocked(uid, oldUidState,
+ ActivityManager.PROCESS_STATE_CACHED_EMPTY);
}
}
}
+ void updateRulesForUidStateChangeLocked(int uid, int oldUidState, int newUidState) {
+ final boolean oldForeground = oldUidState <= mCurForegroundState;
+ final boolean newForeground = newUidState <= mCurForegroundState;
+ if (oldForeground != newForeground) {
+ updateRulesForUidLocked(uid);
+ }
+ }
+
private void updateScreenOn() {
synchronized (mRulesLock) {
try {
@@ -2381,16 +2349,6 @@
}
}
- private static void dumpSparseIntArray(PrintWriter fout, SparseIntArray value) {
- fout.print("[");
- final int size = value.size();
- for (int i = 0; i < size; i++) {
- fout.print(value.keyAt(i) + "=" + value.valueAt(i));
- if (i < size - 1) fout.print(",");
- }
- fout.print("]");
- }
-
@Override
public void factoryReset(String subscriber) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 74df0a0..5aea746 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.power;
+import android.app.ActivityManager;
+import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
@@ -436,6 +438,8 @@
// Set of app ids that we will always respect the wake locks for.
int[] mDeviceIdleWhitelist = new int[0];
+ private final SparseIntArray mUidState = new SparseIntArray();
+
// True if theater mode is enabled
private boolean mTheaterModeEnabled;
@@ -2316,6 +2320,24 @@
}
}
+ void updateUidProcStateInternal(int uid, int procState) {
+ synchronized (mLock) {
+ mUidState.put(uid, procState);
+ if (mDeviceIdleMode) {
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
+ void uidGoneInternal(int uid) {
+ synchronized (mLock) {
+ mUidState.delete(uid);
+ if (mDeviceIdleMode) {
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
private void updateWakeLockDisabledStatesLocked() {
boolean changed = false;
final int numWakeLocks = mWakeLocks.size();
@@ -2349,7 +2371,10 @@
// If we are in idle mode, we will ignore all partial wake locks that are
// for application uids that are not whitelisted.
if (appid >= Process.FIRST_APPLICATION_UID &&
- Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0) {
+ Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
+ mUidState.get(wakeLock.mOwnerUid,
+ ActivityManager.PROCESS_STATE_CACHED_EMPTY)
+ > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
disabled = true;
}
}
@@ -2650,6 +2675,13 @@
pw.println("Screen dim duration: " + screenDimDuration + " ms");
pw.println();
+ pw.println("UID states:");
+ for (int i=0; i<mUidState.size(); i++) {
+ pw.print(" UID "); UserHandle.formatUid(pw, mUidState.keyAt(i));
+ pw.print(": "); pw.println(mUidState.valueAt(i));
+ }
+
+ pw.println();
pw.println("Wake Locks: size=" + mWakeLocks.size());
for (WakeLock wl : mWakeLocks) {
pw.println(" " + wl);
@@ -3451,5 +3483,15 @@
public void setDeviceIdleWhitelist(int[] appids) {
setDeviceIdleWhitelistInternal(appids);
}
+
+ @Override
+ public void updateUidProcState(int uid, int procState) {
+ updateUidProcStateInternal(uid, procState);
+ }
+
+ @Override
+ public void uidGone(int uid) {
+ uidGoneInternal(uid);
+ }
}
}