private boolean update(Context context, ActivityManager am) { final PackageManager pm = context.getPackageManager(); mSequence++; boolean changed = false; List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(MAX_SERVICES); final int NS = services != null ? services.size() : 0; for (int i = 0; i < NS; i++) { ActivityManager.RunningServiceInfo si = services.get(i); // We are not interested in services that have not been started // and don't have a known client, because // there is nothing the user can do about them. if (!si.started && si.clientLabel == 0) { continue; } // We likewise don't care about services running in a // persistent process like the system or phone. if ((si.flags & ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS) != 0) { continue; } HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid); if (procs == null) { procs = new HashMap<String, ProcessItem>(); mServiceProcessesByName.put(si.uid, procs); } ProcessItem proc = procs.get(si.process); if (proc == null) { changed = true; proc = new ProcessItem(context, si.uid, si.process); procs.put(si.process, proc); } if (proc.mCurSeq != mSequence) { int pid = si.restarting == 0 ? si.pid : 0; if (pid != proc.mPid) { changed = true; if (proc.mPid != pid) { if (proc.mPid != 0) { mServiceProcessesByPid.remove(proc.mPid); } if (pid != 0) { mServiceProcessesByPid.put(pid, proc); } proc.mPid = pid; } } proc.mDependentProcesses.clear(); proc.mCurSeq = mSequence; } changed |= proc.updateService(context, si); } // Now update the map of other processes that are running (but // don't have services actively running inside them). List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses(); final int NP = processes != null ? processes.size() : 0; for (int i = 0; i < NP; i++) { ActivityManager.RunningAppProcessInfo pi = processes.get(i); ProcessItem proc = mServiceProcessesByPid.get(pi.pid); if (proc == null) { // This process is not one that is a direct container // of a service, so look for it in the secondary // running list. proc = mRunningProcesses.get(pi.pid); if (proc == null) { changed = true; proc = new ProcessItem(context, pi.uid, pi.processName); proc.mPid = pi.pid; mRunningProcesses.put(pi.pid, proc); } proc.mDependentProcesses.clear(); } if (isInterestingProcess(pi)) { if (!mInterestingProcesses.contains(proc)) { changed = true; mInterestingProcesses.add(proc); } proc.mCurSeq = mSequence; proc.ensureLabel(pm); } proc.mRunningSeq = mSequence; proc.mRunningProcessInfo = pi; } // Build the chains from client processes to the process they are // dependent on; also remove any old running processes. int NRP = mRunningProcesses.size(); for (int i = 0; i < NRP; i++) { ProcessItem proc = mRunningProcesses.valueAt(i); if (proc.mRunningSeq == mSequence) { int clientPid = proc.mRunningProcessInfo.importanceReasonPid; if (clientPid != 0) { ProcessItem client = mServiceProcessesByPid.get(clientPid); if (client == null) { client = mRunningProcesses.get(clientPid); } if (client != null) { client.mDependentProcesses.put(proc.mPid, proc); } } else { // In this pass the process doesn't have a client. // Clear to make sure that, if it later gets the same one, // we will detect the change. proc.mClient = null; } } else { changed = true; mRunningProcesses.remove(mRunningProcesses.keyAt(i)); } } // Remove any old interesting processes. int NHP = mInterestingProcesses.size(); for (int i = 0; i < NHP; i++) { ProcessItem proc = mInterestingProcesses.get(i); if (mRunningProcesses.get(proc.mPid) == null) { changed = true; mInterestingProcesses.remove(i); i--; NHP--; } } // Follow the tree from all primary service processes to all // processes they are dependent on, marking these processes as // still being active and determining if anything has changed. final int NAP = mServiceProcessesByPid.size(); for (int i = 0; i < NAP; i++) { ProcessItem proc = mServiceProcessesByPid.valueAt(i); if (proc.mCurSeq == mSequence) { changed |= proc.buildDependencyChain(context, pm, mSequence); } } // Look for services and their primary processes that no longer exist... ArrayList<Integer> uidToDelete = null; for (int i = 0; i < mServiceProcessesByName.size(); i++) { HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i); Iterator<ProcessItem> pit = procs.values().iterator(); while (pit.hasNext()) { ProcessItem pi = pit.next(); if (pi.mCurSeq == mSequence) { pi.ensureLabel(pm); if (pi.mPid == 0) { // Sanity: a non-process can't be dependent on // anything. pi.mDependentProcesses.clear(); } } else { changed = true; pit.remove(); if (procs.size() == 0) { if (uidToDelete == null) { uidToDelete = new ArrayList<Integer>(); } uidToDelete.add(mServiceProcessesByName.keyAt(i)); } if (pi.mPid != 0) { mServiceProcessesByPid.remove(pi.mPid); } continue; } Iterator<ServiceItem> sit = pi.mServices.values().iterator(); while (sit.hasNext()) { ServiceItem si = sit.next(); if (si.mCurSeq != mSequence) { changed = true; sit.remove(); } } } } if (uidToDelete != null) { for (int i = 0; i < uidToDelete.size(); i++) { int uid = uidToDelete.get(i); mServiceProcessesByName.remove(uid); } } if (changed) { // First determine an order for the services. ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>(); for (int i = 0; i < mServiceProcessesByName.size(); i++) { for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) { pi.mIsSystem = false; pi.mIsStarted = true; pi.mActiveSince = Long.MAX_VALUE; for (ServiceItem si : pi.mServices.values()) { if (si.mServiceInfo != null && (si.mServiceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { pi.mIsSystem = true; } if (si.mRunningService != null && si.mRunningService.clientLabel != 0) { pi.mIsStarted = false; if (pi.mActiveSince > si.mRunningService.activeSince) { pi.mActiveSince = si.mRunningService.activeSince; } } } sortedProcesses.add(pi); } } Collections.sort(sortedProcesses, mServiceProcessComparator); ArrayList<BaseItem> newItems = new ArrayList<BaseItem>(); ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>(); mProcessItems.clear(); for (int i = 0; i < sortedProcesses.size(); i++) { ProcessItem pi = sortedProcesses.get(i); pi.mNeedDivider = false; int firstProc = mProcessItems.size(); // First add processes we are dependent on. pi.addDependentProcesses(newItems, mProcessItems); // And add the process itself. newItems.add(pi); if (pi.mPid > 0) { mProcessItems.add(pi); } // Now add the services running in it. MergedItem mergedItem = null; boolean haveAllMerged = false; boolean needDivider = false; for (ServiceItem si : pi.mServices.values()) { si.mNeedDivider = needDivider; needDivider = true; newItems.add(si); if (si.mMergedItem != null) { if (mergedItem != null && mergedItem != si.mMergedItem) { haveAllMerged = false; } mergedItem = si.mMergedItem; } else { haveAllMerged = false; } } if (!haveAllMerged || mergedItem == null || mergedItem.mServices.size() != pi.mServices.size()) { // Whoops, we need to build a new MergedItem! mergedItem = new MergedItem(); for (ServiceItem si : pi.mServices.values()) { mergedItem.mServices.add(si); si.mMergedItem = mergedItem; } mergedItem.mProcess = pi; mergedItem.mOtherProcesses.clear(); for (int mpi = firstProc; mpi < (mProcessItems.size() - 1); mpi++) { mergedItem.mOtherProcesses.add(mProcessItems.get(mpi)); } } mergedItem.update(context, false); newMergedItems.add(mergedItem); } // Finally, interesting processes need to be shown and will // go at the top. NHP = mInterestingProcesses.size(); for (int i = 0; i < NHP; i++) { ProcessItem proc = mInterestingProcesses.get(i); if (proc.mClient == null && proc.mServices.size() <= 0) { if (proc.mMergedItem == null) { proc.mMergedItem = new MergedItem(); proc.mMergedItem.mProcess = proc; } proc.mMergedItem.update(context, false); newMergedItems.add(0, proc.mMergedItem); mProcessItems.add(proc); } } synchronized (mLock) { mItems = newItems; mMergedItems = newMergedItems; } } // Count number of interesting other (non-active) processes, and // build a list of all processes we will retrieve memory for. mAllProcessItems.clear(); mAllProcessItems.addAll(mProcessItems); int numBackgroundProcesses = 0; int numForegroundProcesses = 0; int numServiceProcesses = 0; NRP = mRunningProcesses.size(); for (int i = 0; i < NRP; i++) { ProcessItem proc = mRunningProcesses.valueAt(i); if (proc.mCurSeq != mSequence) { // We didn't hit this process as a dependency on one // of our active ones, so add it up if needed. if (proc.mRunningProcessInfo.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { numBackgroundProcesses++; mAllProcessItems.add(proc); } else if (proc.mRunningProcessInfo.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { numForegroundProcesses++; mAllProcessItems.add(proc); } else { Log.i( "RunningState", "Unknown non-service process: " + proc.mProcessName + " #" + proc.mPid); } } else { numServiceProcesses++; } } long backgroundProcessMemory = 0; long foregroundProcessMemory = 0; long serviceProcessMemory = 0; ArrayList<MergedItem> newBackgroundItems = null; try { final int numProc = mAllProcessItems.size(); int[] pids = new int[numProc]; for (int i = 0; i < numProc; i++) { pids[i] = mAllProcessItems.get(i).mPid; } Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault().getProcessMemoryInfo(pids); int bgIndex = 0; for (int i = 0; i < pids.length; i++) { ProcessItem proc = mAllProcessItems.get(i); changed |= proc.updateSize(context, mem[i], mSequence); if (proc.mCurSeq == mSequence) { serviceProcessMemory += proc.mSize; } else if (proc.mRunningProcessInfo.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { backgroundProcessMemory += proc.mSize; MergedItem mergedItem; if (newBackgroundItems != null) { mergedItem = proc.mMergedItem = new MergedItem(); proc.mMergedItem.mProcess = proc; newBackgroundItems.add(mergedItem); } else { if (bgIndex >= mBackgroundItems.size() || mBackgroundItems.get(bgIndex).mProcess != proc) { newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); for (int bgi = 0; bgi < bgIndex; bgi++) { newBackgroundItems.add(mBackgroundItems.get(bgi)); } mergedItem = proc.mMergedItem = new MergedItem(); proc.mMergedItem.mProcess = proc; newBackgroundItems.add(mergedItem); } else { mergedItem = mBackgroundItems.get(bgIndex); } } mergedItem.update(context, true); mergedItem.updateSize(context); bgIndex++; } else if (proc.mRunningProcessInfo.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { foregroundProcessMemory += proc.mSize; } } } catch (RemoteException e) { } if (newBackgroundItems == null) { // One or more at the bottom may no longer exit. if (mBackgroundItems.size() > numBackgroundProcesses) { newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); for (int bgi = 0; bgi < numBackgroundProcesses; bgi++) { newBackgroundItems.add(mBackgroundItems.get(bgi)); } } } for (int i = 0; i < mMergedItems.size(); i++) { mMergedItems.get(i).updateSize(context); } synchronized (mLock) { mNumBackgroundProcesses = numBackgroundProcesses; mNumForegroundProcesses = numForegroundProcesses; mNumServiceProcesses = numServiceProcesses; mBackgroundProcessMemory = backgroundProcessMemory; mForegroundProcessMemory = foregroundProcessMemory; mServiceProcessMemory = serviceProcessMemory; if (newBackgroundItems != null) { mBackgroundItems = newBackgroundItems; if (mWatchingBackgroundItems) { changed = true; } } if (!mHaveData) { mHaveData = true; mLock.notifyAll(); } } return changed; }
boolean updateService(Context context, ActivityManager.RunningServiceInfo service) { final PackageManager pm = context.getPackageManager(); boolean changed = false; ServiceItem si = mServices.get(service.service); if (si == null) { changed = true; si = new ServiceItem(); si.mRunningService = service; try { si.mServiceInfo = pm.getServiceInfo(service.service, 0); } catch (PackageManager.NameNotFoundException e) { } si.mDisplayLabel = makeLabel(pm, si.mRunningService.service.getClassName(), si.mServiceInfo); mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null; si.mPackageInfo = si.mServiceInfo.applicationInfo; mServices.put(service.service, si); } si.mCurSeq = mCurSeq; si.mRunningService = service; long activeSince = service.restarting == 0 ? service.activeSince : -1; if (si.mActiveSince != activeSince) { si.mActiveSince = activeSince; changed = true; } if (service.clientPackage != null && service.clientLabel != 0) { if (si.mShownAsStarted) { si.mShownAsStarted = false; changed = true; } try { Resources clientr = pm.getResourcesForApplication(service.clientPackage); String label = clientr.getString(service.clientLabel); si.mDescription = context.getResources().getString(R.string.service_client_name, label); } catch (PackageManager.NameNotFoundException e) { si.mDescription = null; } } else { if (!si.mShownAsStarted) { si.mShownAsStarted = true; changed = true; } si.mDescription = context.getResources().getString(R.string.service_started_by_app); } return changed; }