1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.applications;
18 
19 import android.app.ActivityManager;
20 import android.app.ActivityThread;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageInfo;
28 import android.content.pm.PackageItemInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ServiceInfo;
31 import android.content.pm.UserInfo;
32 import android.content.res.Resources;
33 import android.graphics.drawable.Drawable;
34 import android.graphics.drawable.Drawable.ConstantState;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.RemoteException;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.text.format.Formatter;
43 import android.util.Log;
44 import android.util.SparseArray;
45 
46 import com.android.settings.R;
47 import com.android.settingslib.Utils;
48 import com.android.settingslib.applications.InterestingConfigChanges;
49 
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.Comparator;
53 import java.util.HashMap;
54 import java.util.Iterator;
55 import java.util.List;
56 
57 /**
58  * Singleton for retrieving and monitoring the state about all running
59  * applications/processes/services.
60  */
61 public class RunningState {
62     static final String TAG = "RunningState";
63     static final boolean DEBUG_COMPARE = false;
64 
65     static Object sGlobalLock = new Object();
66     static RunningState sInstance;
67 
68     static final int MSG_RESET_CONTENTS = 1;
69     static final int MSG_UPDATE_CONTENTS = 2;
70     static final int MSG_REFRESH_UI = 3;
71     static final int MSG_UPDATE_TIME = 4;
72 
73     static final long TIME_UPDATE_DELAY = 1000;
74     static final long CONTENTS_UPDATE_DELAY = 2000;
75 
76     static final int MAX_SERVICES = 100;
77 
78     final Context mApplicationContext;
79     final ActivityManager mAm;
80     final PackageManager mPm;
81     final UserManager mUm;
82     final int mMyUserId;
83     final boolean mHideManagedProfiles;
84 
85     OnRefreshUiListener mRefreshUiListener;
86 
87     final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
88 
89     // Processes that are hosting a service we are interested in, organized
90     // by uid and name.  Note that this mapping does not change even across
91     // service restarts, and during a restart there will still be a process
92     // entry.
93     final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName
94             = new SparseArray<HashMap<String, ProcessItem>>();
95 
96     // Processes that are hosting a service we are interested in, organized
97     // by their pid.  These disappear and re-appear as services are restarted.
98     final SparseArray<ProcessItem> mServiceProcessesByPid
99             = new SparseArray<ProcessItem>();
100 
101     // Used to sort the interesting processes.
102     final ServiceProcessComparator mServiceProcessComparator
103             = new ServiceProcessComparator();
104 
105     // Additional interesting processes to be shown to the user, even if
106     // there is no service running in them.
107     final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>();
108 
109     // All currently running processes, for finding dependencies etc.
110     final SparseArray<ProcessItem> mRunningProcesses
111             = new SparseArray<ProcessItem>();
112 
113     // The processes associated with services, in sorted order.
114     final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
115 
116     // All processes, used for retrieving memory information.
117     final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
118 
119     // If there are other users on the device, these are the merged items
120     // representing all items that would be put in mMergedItems for that user.
121     final SparseArray<MergedItem> mOtherUserMergedItems = new SparseArray<MergedItem>();
122 
123     // If there are other users on the device, these are the merged items
124     // representing all items that would be put in mUserBackgroundItems for that user.
125     final SparseArray<MergedItem> mOtherUserBackgroundItems = new SparseArray<MergedItem>();
126 
127     static class AppProcessInfo {
128         final ActivityManager.RunningAppProcessInfo info;
129         boolean hasServices;
130         boolean hasForegroundServices;
131 
AppProcessInfo(ActivityManager.RunningAppProcessInfo _info)132         AppProcessInfo(ActivityManager.RunningAppProcessInfo _info) {
133             info = _info;
134         }
135     }
136 
137     // Temporary structure used when updating above information.
138     final SparseArray<AppProcessInfo> mTmpAppProcesses = new SparseArray<AppProcessInfo>();
139 
140     int mSequence = 0;
141 
142     final Comparator<RunningState.MergedItem> mBackgroundComparator
143         = new Comparator<RunningState.MergedItem>() {
144             @Override
145             public int compare(MergedItem lhs, MergedItem rhs) {
146                 if (DEBUG_COMPARE) {
147                     Log.i(TAG, "Comparing " + lhs + " with " + rhs);
148                     Log.i(TAG, "     Proc " + lhs.mProcess + " with " + rhs.mProcess);
149                     Log.i(TAG, "   UserId " + lhs.mUserId + " with " + rhs.mUserId);
150                 }
151                 if (lhs.mUserId != rhs.mUserId) {
152                     if (lhs.mUserId == mMyUserId) return -1;
153                     if (rhs.mUserId == mMyUserId) return 1;
154                     return lhs.mUserId < rhs.mUserId ? -1 : 1;
155                 }
156                 if (lhs.mProcess == rhs.mProcess) {
157                     if (lhs.mLabel == rhs.mLabel) {
158                         return 0;
159                     }
160                     return lhs.mLabel != null ? lhs.mLabel.compareTo(rhs.mLabel) : -1;
161                 }
162                 if (lhs.mProcess == null) return -1;
163                 if (rhs.mProcess == null) return 1;
164                 if (DEBUG_COMPARE) Log.i(TAG, "    Label " + lhs.mProcess.mLabel
165                         + " with " + rhs.mProcess.mLabel);
166                 final ActivityManager.RunningAppProcessInfo lhsInfo
167                         = lhs.mProcess.mRunningProcessInfo;
168                 final ActivityManager.RunningAppProcessInfo rhsInfo
169                         = rhs.mProcess.mRunningProcessInfo;
170                 final boolean lhsBg = lhsInfo.importance
171                         >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
172                 final boolean rhsBg = rhsInfo.importance
173                         >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
174                         if (DEBUG_COMPARE) Log.i(TAG, "       Bg " + lhsBg + " with " + rhsBg);
175                 if (lhsBg != rhsBg) {
176                     return lhsBg ? 1 : -1;
177                 }
178                 final boolean lhsA = (lhsInfo.flags
179                         & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
180                 final boolean rhsA = (rhsInfo.flags
181                         & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
182                 if (DEBUG_COMPARE) Log.i(TAG, "      Act " + lhsA + " with " + rhsA);
183                 if (lhsA != rhsA) {
184                     return lhsA ? -1 : 1;
185                 }
186                 if (DEBUG_COMPARE) Log.i(TAG, "      Lru " + lhsInfo.lru + " with " + rhsInfo.lru);
187                 if (lhsInfo.lru != rhsInfo.lru) {
188                     return lhsInfo.lru < rhsInfo.lru ? -1 : 1;
189                 }
190                 if (lhs.mProcess.mLabel == rhs.mProcess.mLabel) {
191                     return 0;
192                 }
193                 if (lhs.mProcess.mLabel == null) return 1;
194                 if (rhs.mProcess.mLabel == null) return -1;
195                 return lhs.mProcess.mLabel.compareTo(rhs.mProcess.mLabel);
196             }
197     };
198 
199     // ----- following protected by mLock -----
200 
201     // Lock for protecting the state that will be shared between the
202     // background update thread and the UI thread.
203     final Object mLock = new Object();
204 
205     boolean mResumed;
206     boolean mHaveData;
207     boolean mWatchingBackgroundItems;
208 
209     ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
210     ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
211     ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>();
212     ArrayList<MergedItem> mUserBackgroundItems = new ArrayList<MergedItem>();
213 
214     int mNumBackgroundProcesses;
215     long mBackgroundProcessMemory;
216     int mNumForegroundProcesses;
217     long mForegroundProcessMemory;
218     int mNumServiceProcesses;
219     long mServiceProcessMemory;
220 
221     // ----- BACKGROUND MONITORING THREAD -----
222 
223     final HandlerThread mBackgroundThread;
224     final class BackgroundHandler extends Handler {
BackgroundHandler(Looper looper)225         public BackgroundHandler(Looper looper) {
226             super(looper);
227         }
228 
229         @Override
handleMessage(Message msg)230         public void handleMessage(Message msg) {
231             switch (msg.what) {
232                 case MSG_RESET_CONTENTS:
233                     reset();
234                     break;
235                 case MSG_UPDATE_CONTENTS:
236                     synchronized (mLock) {
237                         if (!mResumed) {
238                             return;
239                         }
240                     }
241                     Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
242                     cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0;
243                     mHandler.sendMessage(cmd);
244                     removeMessages(MSG_UPDATE_CONTENTS);
245                     msg = obtainMessage(MSG_UPDATE_CONTENTS);
246                     sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
247                     break;
248             }
249         }
250     };
251 
252     final BackgroundHandler mBackgroundHandler;
253 
254     final Handler mHandler = new Handler() {
255         int mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
256 
257         @Override
258         public void handleMessage(Message msg) {
259             switch (msg.what) {
260                 case MSG_REFRESH_UI:
261                     mNextUpdate = msg.arg1 != 0
262                             ? OnRefreshUiListener.REFRESH_STRUCTURE
263                             : OnRefreshUiListener.REFRESH_DATA;
264                     break;
265                 case MSG_UPDATE_TIME:
266                     synchronized (mLock) {
267                         if (!mResumed) {
268                             return;
269                         }
270                     }
271                     removeMessages(MSG_UPDATE_TIME);
272                     Message m = obtainMessage(MSG_UPDATE_TIME);
273                     sendMessageDelayed(m, TIME_UPDATE_DELAY);
274 
275                     if (mRefreshUiListener != null) {
276                         //Log.i("foo", "Refresh UI: " + mNextUpdate
277                         //        + " @ " + SystemClock.uptimeMillis());
278                         mRefreshUiListener.onRefreshUi(mNextUpdate);
279                         mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
280                     }
281                     break;
282             }
283         }
284     };
285 
286     private final class UserManagerBroadcastReceiver extends BroadcastReceiver {
287         private volatile boolean usersChanged;
288 
289         @Override
onReceive(Context context, Intent intent)290         public void onReceive(Context context, Intent intent) {
291             synchronized (mLock) {
292                 if (mResumed) {
293                     mHaveData = false;
294                     mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
295                     mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
296                     mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
297                     mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
298                 } else {
299                     usersChanged = true;
300                 }
301             }
302         }
303 
checkUsersChangedLocked()304         public boolean checkUsersChangedLocked() {
305             boolean oldValue = usersChanged;
306             usersChanged = false;
307             return oldValue;
308         }
309 
register(Context context)310         void register(Context context) {
311             IntentFilter filter = new IntentFilter();
312             filter.addAction(Intent.ACTION_USER_STOPPED);
313             filter.addAction(Intent.ACTION_USER_STARTED);
314             filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
315             context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
316         }
317     }
318 
319     private final UserManagerBroadcastReceiver mUmBroadcastReceiver =
320             new UserManagerBroadcastReceiver();
321 
322     // ----- DATA STRUCTURES -----
323 
324     static interface OnRefreshUiListener {
325         public static final int REFRESH_TIME = 0;
326         public static final int REFRESH_DATA = 1;
327         public static final int REFRESH_STRUCTURE = 2;
328 
onRefreshUi(int what)329         public void onRefreshUi(int what);
330     }
331 
332     static class UserState {
333         UserInfo mInfo;
334         String mLabel;
335         Drawable mIcon;
336     }
337 
338     static class BaseItem {
339         final boolean mIsProcess;
340         final int mUserId;
341 
342         PackageItemInfo mPackageInfo;
343         CharSequence mDisplayLabel;
344         String mLabel;
345         String mDescription;
346 
347         int mCurSeq;
348 
349         long mActiveSince;
350         long mSize;
351         String mSizeStr;
352         String mCurSizeStr;
353         boolean mNeedDivider;
354         boolean mBackground;
355 
BaseItem(boolean isProcess, int userId)356         public BaseItem(boolean isProcess, int userId) {
357             mIsProcess = isProcess;
358             mUserId = userId;
359         }
360 
loadIcon(Context context, RunningState state)361         public Drawable loadIcon(Context context, RunningState state) {
362             if (mPackageInfo != null) {
363                 Drawable unbadgedIcon = mPackageInfo.loadUnbadgedIcon(state.mPm);
364                 Drawable icon = state.mPm.getUserBadgedIcon(unbadgedIcon, new UserHandle(mUserId));
365                 return icon;
366             }
367             return null;
368         }
369     }
370 
371     static class ServiceItem extends BaseItem {
372         ActivityManager.RunningServiceInfo mRunningService;
373         ServiceInfo mServiceInfo;
374         boolean mShownAsStarted;
375 
376         MergedItem mMergedItem;
377 
ServiceItem(int userId)378         public ServiceItem(int userId) {
379             super(false, userId);
380         }
381     }
382 
383     static class ProcessItem extends BaseItem {
384         final HashMap<ComponentName, ServiceItem> mServices
385                 = new HashMap<ComponentName, ServiceItem>();
386         final SparseArray<ProcessItem> mDependentProcesses
387                 = new SparseArray<ProcessItem>();
388 
389         final int mUid;
390         final String mProcessName;
391         int mPid;
392 
393         ProcessItem mClient;
394         int mLastNumDependentProcesses;
395 
396         int mRunningSeq;
397         ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
398 
399         MergedItem mMergedItem;
400 
401         boolean mInteresting;
402 
403         // Purely for sorting.
404         boolean mIsSystem;
405         boolean mIsStarted;
406         long mActiveSince;
407 
ProcessItem(Context context, int uid, String processName)408         public ProcessItem(Context context, int uid, String processName) {
409             super(true, UserHandle.getUserId(uid));
410             mDescription = context.getResources().getString(
411                     R.string.service_process_name, processName);
412             mUid = uid;
413             mProcessName = processName;
414         }
415 
ensureLabel(PackageManager pm)416         void ensureLabel(PackageManager pm) {
417             if (mLabel != null) {
418                 return;
419             }
420 
421             try {
422                 ApplicationInfo ai = pm.getApplicationInfo(mProcessName,
423                         PackageManager.MATCH_ANY_USER);
424                 if (ai.uid == mUid) {
425                     mDisplayLabel = ai.loadLabel(pm);
426                     mLabel = mDisplayLabel.toString();
427                     mPackageInfo = ai;
428                     return;
429                 }
430             } catch (PackageManager.NameNotFoundException e) {
431             }
432 
433             // If we couldn't get information about the overall
434             // process, try to find something about the uid.
435             String[] pkgs = pm.getPackagesForUid(mUid);
436 
437             // If there is one package with this uid, that is what we want.
438             if (pkgs.length == 1) {
439                 try {
440                     ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
441                             PackageManager.MATCH_ANY_USER);
442                     mDisplayLabel = ai.loadLabel(pm);
443                     mLabel = mDisplayLabel.toString();
444                     mPackageInfo = ai;
445                     return;
446                 } catch (PackageManager.NameNotFoundException e) {
447                 }
448             }
449 
450             // If there are multiple, see if one gives us the official name
451             // for this uid.
452             for (String name : pkgs) {
453                 try {
454                     PackageInfo pi = pm.getPackageInfo(name, 0);
455                     if (pi.sharedUserLabel != 0) {
456                         CharSequence nm = pm.getText(name,
457                                 pi.sharedUserLabel, pi.applicationInfo);
458                         if (nm != null) {
459                             mDisplayLabel = nm;
460                             mLabel = nm.toString();
461                             mPackageInfo = pi.applicationInfo;
462                             return;
463                         }
464                     }
465                 } catch (PackageManager.NameNotFoundException e) {
466                 }
467             }
468 
469             // If still don't have anything to display, just use the
470             // service info.
471             if (mServices.size() > 0) {
472                 ApplicationInfo ai = mServices.values().iterator().next()
473                         .mServiceInfo.applicationInfo;
474                 mPackageInfo = ai;
475                 mDisplayLabel = mPackageInfo.loadLabel(pm);
476                 mLabel = mDisplayLabel.toString();
477                 return;
478             }
479 
480             // Finally... whatever, just pick the first package's name.
481             try {
482                 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
483                         PackageManager.MATCH_ANY_USER);
484                 mDisplayLabel = ai.loadLabel(pm);
485                 mLabel = mDisplayLabel.toString();
486                 mPackageInfo = ai;
487                 return;
488             } catch (PackageManager.NameNotFoundException e) {
489             }
490         }
491 
updateService(Context context, ActivityManager.RunningServiceInfo service)492         boolean updateService(Context context, ActivityManager.RunningServiceInfo service) {
493             final PackageManager pm = context.getPackageManager();
494 
495             boolean changed = false;
496             ServiceItem si = mServices.get(service.service);
497             if (si == null) {
498                 changed = true;
499                 si = new ServiceItem(mUserId);
500                 si.mRunningService = service;
501                 try {
502                     si.mServiceInfo = ActivityThread.getPackageManager().getServiceInfo(
503                             service.service, PackageManager.MATCH_ANY_USER,
504                             UserHandle.getUserId(service.uid));
505 
506                     if (si.mServiceInfo == null) {
507                         Log.d("RunningService", "getServiceInfo returned null for: "
508                                 + service.service);
509                         return false;
510                     }
511                 } catch (RemoteException e) {
512                 }
513                 si.mDisplayLabel = makeLabel(pm,
514                         si.mRunningService.service.getClassName(), si.mServiceInfo);
515                 mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
516                 si.mPackageInfo = si.mServiceInfo.applicationInfo;
517                 mServices.put(service.service, si);
518             }
519             si.mCurSeq = mCurSeq;
520             si.mRunningService = service;
521             long activeSince = service.restarting == 0 ? service.activeSince : -1;
522             if (si.mActiveSince != activeSince) {
523                 si.mActiveSince = activeSince;
524                 changed = true;
525             }
526             if (service.clientPackage != null && service.clientLabel != 0) {
527                 if (si.mShownAsStarted) {
528                     si.mShownAsStarted = false;
529                     changed = true;
530                 }
531                 try {
532                     Resources clientr = pm.getResourcesForApplication(service.clientPackage);
533                     String label = clientr.getString(service.clientLabel);
534                     si.mDescription = context.getResources().getString(
535                             R.string.service_client_name, label);
536                 } catch (PackageManager.NameNotFoundException e) {
537                     si.mDescription = null;
538                 }
539             } else {
540                 if (!si.mShownAsStarted) {
541                     si.mShownAsStarted = true;
542                     changed = true;
543                 }
544                 si.mDescription = context.getResources().getString(
545                         R.string.service_started_by_app);
546             }
547 
548             return changed;
549         }
550 
updateSize(Context context, long pss, int curSeq)551         boolean updateSize(Context context, long pss, int curSeq) {
552             mSize = pss * 1024;
553             if (mCurSeq == curSeq) {
554                 String sizeStr = Formatter.formatShortFileSize(
555                         context, mSize);
556                 if (!sizeStr.equals(mSizeStr)){
557                     mSizeStr = sizeStr;
558                     // We update this on the second tick where we update just
559                     // the text in the current items, so no need to say we
560                     // changed here.
561                     return false;
562                 }
563             }
564             return false;
565         }
566 
buildDependencyChain(Context context, PackageManager pm, int curSeq)567         boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
568             final int NP = mDependentProcesses.size();
569             boolean changed = false;
570             for (int i=0; i<NP; i++) {
571                 ProcessItem proc = mDependentProcesses.valueAt(i);
572                 if (proc.mClient != this) {
573                     changed = true;
574                     proc.mClient = this;
575                 }
576                 proc.mCurSeq = curSeq;
577                 proc.ensureLabel(pm);
578                 changed |= proc.buildDependencyChain(context, pm, curSeq);
579             }
580 
581             if (mLastNumDependentProcesses != mDependentProcesses.size()) {
582                 changed = true;
583                 mLastNumDependentProcesses = mDependentProcesses.size();
584             }
585 
586             return changed;
587         }
588 
addDependentProcesses(ArrayList<BaseItem> dest, ArrayList<ProcessItem> destProc)589         void addDependentProcesses(ArrayList<BaseItem> dest,
590                 ArrayList<ProcessItem> destProc) {
591             final int NP = mDependentProcesses.size();
592             for (int i=0; i<NP; i++) {
593                 ProcessItem proc = mDependentProcesses.valueAt(i);
594                 proc.addDependentProcesses(dest, destProc);
595                 dest.add(proc);
596                 if (proc.mPid > 0) {
597                     destProc.add(proc);
598                 }
599             }
600         }
601     }
602 
603     static class MergedItem extends BaseItem {
604         ProcessItem mProcess;
605         UserState mUser;
606         final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
607         final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
608         final ArrayList<MergedItem> mChildren = new ArrayList<MergedItem>();
609 
610         private int mLastNumProcesses = -1, mLastNumServices = -1;
611 
MergedItem(int userId)612         MergedItem(int userId) {
613             super(false, userId);
614         }
615 
setDescription(Context context, int numProcesses, int numServices)616         private void setDescription(Context context, int numProcesses, int numServices) {
617             if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) {
618                 mLastNumProcesses = numProcesses;
619                 mLastNumServices = numServices;
620                 int resid = R.string.running_processes_item_description_s_s;
621                 if (numProcesses != 1) {
622                     resid = numServices != 1
623                             ? R.string.running_processes_item_description_p_p
624                             : R.string.running_processes_item_description_p_s;
625                 } else if (numServices != 1) {
626                     resid = R.string.running_processes_item_description_s_p;
627                 }
628                 mDescription = context.getResources().getString(resid, numProcesses,
629                         numServices);
630             }
631         }
632 
update(Context context, boolean background)633         boolean update(Context context, boolean background) {
634             mBackground = background;
635 
636             if (mUser != null) {
637                 // This is a merged item that contains a child collection
638                 // of items...  that is, it is an entire user, containing
639                 // everything associated with that user.  So set it up as such.
640                 // For concrete stuff we need about the process of this item,
641                 // we will just use the info from the first child.
642                 MergedItem child0 = mChildren.get(0);
643                 mPackageInfo = child0.mProcess.mPackageInfo;
644                 mLabel = mUser != null ? mUser.mLabel : null;
645                 mDisplayLabel = mLabel;
646                 int numProcesses = 0;
647                 int numServices = 0;
648                 mActiveSince = -1;
649                 for (int i=0; i<mChildren.size(); i++) {
650                     MergedItem child = mChildren.get(i);
651                     numProcesses += child.mLastNumProcesses;
652                     numServices += child.mLastNumServices;
653                     if (child.mActiveSince >= 0 && mActiveSince < child.mActiveSince) {
654                         mActiveSince = child.mActiveSince;
655                     }
656                 }
657                 if (!mBackground) {
658                     setDescription(context, numProcesses, numServices);
659                 }
660             } else {
661                 mPackageInfo = mProcess.mPackageInfo;
662                 mDisplayLabel = mProcess.mDisplayLabel;
663                 mLabel = mProcess.mLabel;
664 
665                 if (!mBackground) {
666                     setDescription(context, (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(),
667                             mServices.size());
668                 }
669 
670                 mActiveSince = -1;
671                 for (int i=0; i<mServices.size(); i++) {
672                     ServiceItem si = mServices.get(i);
673                     if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
674                         mActiveSince = si.mActiveSince;
675                     }
676                 }
677             }
678 
679             return false;
680         }
681 
updateSize(Context context)682         boolean updateSize(Context context) {
683             if (mUser != null) {
684                 mSize = 0;
685                 for (int i=0; i<mChildren.size(); i++) {
686                     MergedItem child = mChildren.get(i);
687                     child.updateSize(context);
688                     mSize += child.mSize;
689                 }
690             } else {
691                 mSize = mProcess.mSize;
692                 for (int i=0; i<mOtherProcesses.size(); i++) {
693                     mSize += mOtherProcesses.get(i).mSize;
694                 }
695             }
696 
697             String sizeStr = Formatter.formatShortFileSize(
698                     context, mSize);
699             if (!sizeStr.equals(mSizeStr)){
700                 mSizeStr = sizeStr;
701                 // We update this on the second tick where we update just
702                 // the text in the current items, so no need to say we
703                 // changed here.
704                 return false;
705             }
706             return false;
707         }
708 
loadIcon(Context context, RunningState state)709         public Drawable loadIcon(Context context, RunningState state) {
710             if (mUser == null) {
711                 return super.loadIcon(context, state);
712             }
713             if (mUser.mIcon != null) {
714                 ConstantState constState = mUser.mIcon.getConstantState();
715                 if (constState == null) {
716                     return mUser.mIcon;
717                 } else {
718                     return constState.newDrawable();
719                 }
720             }
721             return context.getDrawable(
722                     com.android.internal.R.drawable.ic_menu_cc);
723         }
724     }
725 
726     class ServiceProcessComparator implements Comparator<ProcessItem> {
compare(ProcessItem object1, ProcessItem object2)727         public int compare(ProcessItem object1, ProcessItem object2) {
728             if (object1.mUserId != object2.mUserId) {
729                 if (object1.mUserId == mMyUserId) return -1;
730                 if (object2.mUserId == mMyUserId) return 1;
731                 return object1.mUserId < object2.mUserId ? -1 : 1;
732             }
733             if (object1.mIsStarted != object2.mIsStarted) {
734                 // Non-started processes go last.
735                 return object1.mIsStarted ? -1 : 1;
736             }
737             if (object1.mIsSystem != object2.mIsSystem) {
738                 // System processes go below non-system.
739                 return object1.mIsSystem ? 1 : -1;
740             }
741             if (object1.mActiveSince != object2.mActiveSince) {
742                 // Remaining ones are sorted with the longest running
743                 // services last.
744                 return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
745             }
746             return 0;
747         }
748     }
749 
makeLabel(PackageManager pm, String className, PackageItemInfo item)750     static CharSequence makeLabel(PackageManager pm,
751             String className, PackageItemInfo item) {
752         if (item != null && (item.labelRes != 0
753                 || item.nonLocalizedLabel != null)) {
754             CharSequence label = item.loadLabel(pm);
755             if (label != null) {
756                 return label;
757             }
758         }
759 
760         String label = className;
761         int tail = label.lastIndexOf('.');
762         if (tail >= 0) {
763             label = label.substring(tail+1, label.length());
764         }
765         return label;
766     }
767 
getInstance(Context context)768     static RunningState getInstance(Context context) {
769         synchronized (sGlobalLock) {
770             if (sInstance == null) {
771                 sInstance = new RunningState(context);
772             }
773             return sInstance;
774         }
775     }
776 
RunningState(Context context)777     private RunningState(Context context) {
778         mApplicationContext = context.getApplicationContext();
779         mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE);
780         mPm = mApplicationContext.getPackageManager();
781         mUm = (UserManager)mApplicationContext.getSystemService(Context.USER_SERVICE);
782         mMyUserId = UserHandle.myUserId();
783         UserInfo userInfo = mUm.getUserInfo(mMyUserId);
784         mHideManagedProfiles = userInfo == null || !userInfo.canHaveProfile();
785         mResumed = false;
786         mBackgroundThread = new HandlerThread("RunningState:Background");
787         mBackgroundThread.start();
788         mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
789         mUmBroadcastReceiver.register(mApplicationContext);
790     }
791 
resume(OnRefreshUiListener listener)792     void resume(OnRefreshUiListener listener) {
793         synchronized (mLock) {
794             mResumed = true;
795             mRefreshUiListener = listener;
796             boolean usersChanged = mUmBroadcastReceiver.checkUsersChangedLocked();
797             boolean configChanged =
798                     mInterestingConfigChanges.applyNewConfig(mApplicationContext.getResources());
799             if (usersChanged || configChanged) {
800                 mHaveData = false;
801                 mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
802                 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
803                 mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
804             }
805             if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
806                 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
807             }
808             mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
809         }
810     }
811 
updateNow()812     void updateNow() {
813         synchronized (mLock) {
814             mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
815             mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
816         }
817     }
818 
hasData()819     boolean hasData() {
820         synchronized (mLock) {
821             return mHaveData;
822         }
823     }
824 
waitForData()825     void waitForData() {
826         synchronized (mLock) {
827             while (!mHaveData) {
828                 try {
829                     mLock.wait(0);
830                 } catch (InterruptedException e) {
831                 }
832             }
833         }
834     }
835 
pause()836     void pause() {
837         synchronized (mLock) {
838             mResumed = false;
839             mRefreshUiListener = null;
840             mHandler.removeMessages(MSG_UPDATE_TIME);
841         }
842     }
843 
isInterestingProcess(ActivityManager.RunningAppProcessInfo pi)844     private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) {
845         if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
846             return true;
847         }
848         if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0
849                 && pi.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
850                 && pi.importance < ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
851                 && pi.importanceReasonCode
852                         == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
853             return true;
854         }
855         return false;
856     }
857 
reset()858     private void reset() {
859         mServiceProcessesByName.clear();
860         mServiceProcessesByPid.clear();
861         mInterestingProcesses.clear();
862         mRunningProcesses.clear();
863         mProcessItems.clear();
864         mAllProcessItems.clear();
865     }
866 
addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems, SparseArray<MergedItem> userItems, MergedItem newItem)867     private void addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems,
868             SparseArray<MergedItem> userItems, MergedItem newItem) {
869         MergedItem userItem = userItems.get(newItem.mUserId);
870         boolean first = userItem == null || userItem.mCurSeq != mSequence;
871         if (first) {
872             UserInfo info = mUm.getUserInfo(newItem.mUserId);
873             if (info == null) {
874                 // The user no longer exists, skip
875                 return;
876             }
877             if (mHideManagedProfiles && info.isManagedProfile()) {
878                 return;
879             }
880             if (userItem == null) {
881                 userItem = new MergedItem(newItem.mUserId);
882                 userItems.put(newItem.mUserId, userItem);
883             } else {
884                 userItem.mChildren.clear();
885             }
886             userItem.mCurSeq = mSequence;
887             userItem.mUser = new UserState();
888             userItem.mUser.mInfo = info;
889             userItem.mUser.mIcon = Utils.getUserIcon(context, mUm, info);
890             userItem.mUser.mLabel = Utils.getUserLabel(context, info);
891             newMergedItems.add(userItem);
892         }
893         userItem.mChildren.add(newItem);
894     }
895 
update(Context context, ActivityManager am)896     private boolean update(Context context, ActivityManager am) {
897         final PackageManager pm = context.getPackageManager();
898 
899         mSequence++;
900 
901         boolean changed = false;
902 
903         // Retrieve list of services, filtering out anything that definitely
904         // won't be shown in the UI.
905         List<ActivityManager.RunningServiceInfo> services
906                 = am.getRunningServices(MAX_SERVICES);
907         int NS = services != null ? services.size() : 0;
908         for (int i=0; i<NS; i++) {
909             ActivityManager.RunningServiceInfo si = services.get(i);
910             // We are not interested in services that have not been started
911             // and don't have a known client, because
912             // there is nothing the user can do about them.
913             if (!si.started && si.clientLabel == 0) {
914                 services.remove(i);
915                 i--;
916                 NS--;
917                 continue;
918             }
919             // We likewise don't care about services running in a
920             // persistent process like the system or phone.
921             if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
922                     != 0) {
923                 services.remove(i);
924                 i--;
925                 NS--;
926                 continue;
927             }
928         }
929 
930         // Retrieve list of running processes, organizing them into a sparse
931         // array for easy retrieval.
932         List<ActivityManager.RunningAppProcessInfo> processes
933                 = am.getRunningAppProcesses();
934         final int NP = processes != null ? processes.size() : 0;
935         mTmpAppProcesses.clear();
936         for (int i=0; i<NP; i++) {
937             ActivityManager.RunningAppProcessInfo pi = processes.get(i);
938             mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi));
939         }
940 
941         // Initial iteration through running services to collect per-process
942         // info about them.
943         for (int i=0; i<NS; i++) {
944             ActivityManager.RunningServiceInfo si = services.get(i);
945             if (si.restarting == 0 && si.pid > 0) {
946                 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
947                 if (ainfo != null) {
948                     ainfo.hasServices = true;
949                     if (si.foreground) {
950                         ainfo.hasForegroundServices = true;
951                     }
952                 }
953             }
954         }
955 
956         // Update state we are maintaining about process that are running services.
957         for (int i=0; i<NS; i++) {
958             ActivityManager.RunningServiceInfo si = services.get(i);
959 
960             // If this service's process is in use at a higher importance
961             // due to another process bound to one of its services, then we
962             // won't put it in the top-level list of services.  Instead we
963             // want it to be included in the set of processes that the other
964             // process needs.
965             if (si.restarting == 0 && si.pid > 0) {
966                 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
967                 if (ainfo != null && !ainfo.hasForegroundServices) {
968                     // This process does not have any foreground services.
969                     // If its importance is greater than the service importance
970                     // then there is something else more significant that is
971                     // keeping it around that it should possibly be included as
972                     // a part of instead of being shown by itself.
973                     if (ainfo.info.importance
974                             < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
975                         // Follow process chain to see if there is something
976                         // else that could be shown
977                         boolean skip = false;
978                         ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
979                         while (ainfo != null) {
980                             if (ainfo.hasServices || isInterestingProcess(ainfo.info)) {
981                                 skip = true;
982                                 break;
983                             }
984                             ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
985                         }
986                         if (skip) {
987                             continue;
988                         }
989                     }
990                 }
991             }
992 
993             HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
994             if (procs == null) {
995                 procs = new HashMap<String, ProcessItem>();
996                 mServiceProcessesByName.put(si.uid, procs);
997             }
998             ProcessItem proc = procs.get(si.process);
999             if (proc == null) {
1000                 changed = true;
1001                 proc = new ProcessItem(context, si.uid, si.process);
1002                 procs.put(si.process, proc);
1003             }
1004 
1005             if (proc.mCurSeq != mSequence) {
1006                 int pid = si.restarting == 0 ? si.pid : 0;
1007                 if (pid != proc.mPid) {
1008                     changed = true;
1009                     if (proc.mPid != pid) {
1010                         if (proc.mPid != 0) {
1011                             mServiceProcessesByPid.remove(proc.mPid);
1012                         }
1013                         if (pid != 0) {
1014                             mServiceProcessesByPid.put(pid, proc);
1015                         }
1016                         proc.mPid = pid;
1017                     }
1018                 }
1019                 proc.mDependentProcesses.clear();
1020                 proc.mCurSeq = mSequence;
1021             }
1022             changed |= proc.updateService(context, si);
1023         }
1024 
1025         // Now update the map of other processes that are running (but
1026         // don't have services actively running inside them).
1027         for (int i=0; i<NP; i++) {
1028             ActivityManager.RunningAppProcessInfo pi = processes.get(i);
1029             ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
1030             if (proc == null) {
1031                 // This process is not one that is a direct container
1032                 // of a service, so look for it in the secondary
1033                 // running list.
1034                 proc = mRunningProcesses.get(pi.pid);
1035                 if (proc == null) {
1036                     changed = true;
1037                     proc = new ProcessItem(context, pi.uid, pi.processName);
1038                     proc.mPid = pi.pid;
1039                     mRunningProcesses.put(pi.pid, proc);
1040                 }
1041                 proc.mDependentProcesses.clear();
1042             }
1043 
1044             if (isInterestingProcess(pi)) {
1045                 if (!mInterestingProcesses.contains(proc)) {
1046                     changed = true;
1047                     mInterestingProcesses.add(proc);
1048                 }
1049                 proc.mCurSeq = mSequence;
1050                 proc.mInteresting = true;
1051                 proc.ensureLabel(pm);
1052             } else {
1053                 proc.mInteresting = false;
1054             }
1055 
1056             proc.mRunningSeq = mSequence;
1057             proc.mRunningProcessInfo = pi;
1058         }
1059 
1060         // Build the chains from client processes to the process they are
1061         // dependent on; also remove any old running processes.
1062         int NRP = mRunningProcesses.size();
1063         for (int i = 0; i < NRP;) {
1064             ProcessItem proc = mRunningProcesses.valueAt(i);
1065             if (proc.mRunningSeq == mSequence) {
1066                 int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
1067                 if (clientPid != 0) {
1068                     ProcessItem client = mServiceProcessesByPid.get(clientPid);
1069                     if (client == null) {
1070                         client = mRunningProcesses.get(clientPid);
1071                     }
1072                     if (client != null) {
1073                         client.mDependentProcesses.put(proc.mPid, proc);
1074                     }
1075                 } else {
1076                     // In this pass the process doesn't have a client.
1077                     // Clear to make sure that, if it later gets the same one,
1078                     // we will detect the change.
1079                     proc.mClient = null;
1080                 }
1081                 i++;
1082             } else {
1083                 changed = true;
1084                 mRunningProcesses.remove(mRunningProcesses.keyAt(i));
1085                 NRP--;
1086             }
1087         }
1088 
1089         // Remove any old interesting processes.
1090         int NHP = mInterestingProcesses.size();
1091         for (int i=0; i<NHP; i++) {
1092             ProcessItem proc = mInterestingProcesses.get(i);
1093             if (!proc.mInteresting || mRunningProcesses.get(proc.mPid) == null) {
1094                 changed = true;
1095                 mInterestingProcesses.remove(i);
1096                 i--;
1097                 NHP--;
1098             }
1099         }
1100 
1101         // Follow the tree from all primary service processes to all
1102         // processes they are dependent on, marking these processes as
1103         // still being active and determining if anything has changed.
1104         final int NAP = mServiceProcessesByPid.size();
1105         for (int i=0; i<NAP; i++) {
1106             ProcessItem proc = mServiceProcessesByPid.valueAt(i);
1107             if (proc.mCurSeq == mSequence) {
1108                 changed |= proc.buildDependencyChain(context, pm, mSequence);
1109             }
1110         }
1111 
1112         // Look for services and their primary processes that no longer exist...
1113         ArrayList<Integer> uidToDelete = null;
1114         for (int i=0; i<mServiceProcessesByName.size(); i++) {
1115             HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
1116             Iterator<ProcessItem> pit = procs.values().iterator();
1117             while (pit.hasNext()) {
1118                 ProcessItem pi = pit.next();
1119                 if (pi.mCurSeq == mSequence) {
1120                     pi.ensureLabel(pm);
1121                     if (pi.mPid == 0) {
1122                         // Sanity: a non-process can't be dependent on
1123                         // anything.
1124                         pi.mDependentProcesses.clear();
1125                     }
1126                 } else {
1127                     changed = true;
1128                     pit.remove();
1129                     if (procs.size() == 0) {
1130                         if (uidToDelete == null) {
1131                             uidToDelete = new ArrayList<Integer>();
1132                         }
1133                         uidToDelete.add(mServiceProcessesByName.keyAt(i));
1134                     }
1135                     if (pi.mPid != 0) {
1136                         mServiceProcessesByPid.remove(pi.mPid);
1137                     }
1138                     continue;
1139                 }
1140                 Iterator<ServiceItem> sit = pi.mServices.values().iterator();
1141                 while (sit.hasNext()) {
1142                     ServiceItem si = sit.next();
1143                     if (si.mCurSeq != mSequence) {
1144                         changed = true;
1145                         sit.remove();
1146                     }
1147                 }
1148             }
1149         }
1150 
1151         if (uidToDelete != null) {
1152             for (int i = 0; i < uidToDelete.size(); i++) {
1153                 int uid = uidToDelete.get(i);
1154                 mServiceProcessesByName.remove(uid);
1155             }
1156         }
1157 
1158         if (changed) {
1159             // First determine an order for the services.
1160             ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
1161             for (int i=0; i<mServiceProcessesByName.size(); i++) {
1162                 for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
1163                     pi.mIsSystem = false;
1164                     pi.mIsStarted = true;
1165                     pi.mActiveSince = Long.MAX_VALUE;
1166                     for (ServiceItem si : pi.mServices.values()) {
1167                         if (si.mServiceInfo != null
1168                                 && (si.mServiceInfo.applicationInfo.flags
1169                                         & ApplicationInfo.FLAG_SYSTEM) != 0) {
1170                             pi.mIsSystem = true;
1171                         }
1172                         if (si.mRunningService != null
1173                                 && si.mRunningService.clientLabel != 0) {
1174                             pi.mIsStarted = false;
1175                             if (pi.mActiveSince > si.mRunningService.activeSince) {
1176                                 pi.mActiveSince = si.mRunningService.activeSince;
1177                             }
1178                         }
1179                     }
1180                     sortedProcesses.add(pi);
1181                 }
1182             }
1183 
1184             Collections.sort(sortedProcesses, mServiceProcessComparator);
1185 
1186             ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
1187             ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
1188             mProcessItems.clear();
1189             for (int i=0; i<sortedProcesses.size(); i++) {
1190                 ProcessItem pi = sortedProcesses.get(i);
1191                 pi.mNeedDivider = false;
1192 
1193                 int firstProc = mProcessItems.size();
1194                 // First add processes we are dependent on.
1195                 pi.addDependentProcesses(newItems, mProcessItems);
1196                 // And add the process itself.
1197                 newItems.add(pi);
1198                 if (pi.mPid > 0) {
1199                     mProcessItems.add(pi);
1200                 }
1201 
1202                 // Now add the services running in it.
1203                 MergedItem mergedItem = null;
1204                 boolean haveAllMerged = false;
1205                 boolean needDivider = false;
1206                 for (ServiceItem si : pi.mServices.values()) {
1207                     si.mNeedDivider = needDivider;
1208                     needDivider = true;
1209                     newItems.add(si);
1210                     if (si.mMergedItem != null) {
1211                         if (mergedItem != null && mergedItem != si.mMergedItem) {
1212                             haveAllMerged = false;
1213                         }
1214                         mergedItem = si.mMergedItem;
1215                     } else {
1216                         haveAllMerged = false;
1217                     }
1218                 }
1219 
1220                 if (!haveAllMerged || mergedItem == null
1221                         || mergedItem.mServices.size() != pi.mServices.size()) {
1222                     // Whoops, we need to build a new MergedItem!
1223                     mergedItem = new MergedItem(pi.mUserId);
1224                     for (ServiceItem si : pi.mServices.values()) {
1225                         mergedItem.mServices.add(si);
1226                         si.mMergedItem = mergedItem;
1227                     }
1228                     mergedItem.mProcess = pi;
1229                     mergedItem.mOtherProcesses.clear();
1230                     for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
1231                         mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
1232                     }
1233                 }
1234 
1235                 mergedItem.update(context, false);
1236                 if (mergedItem.mUserId != mMyUserId) {
1237                     addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem);
1238                 } else {
1239                     newMergedItems.add(mergedItem);
1240                 }
1241             }
1242 
1243             // Finally, interesting processes need to be shown and will
1244             // go at the top.
1245             NHP = mInterestingProcesses.size();
1246             for (int i=0; i<NHP; i++) {
1247                 ProcessItem proc = mInterestingProcesses.get(i);
1248                 if (proc.mClient == null && proc.mServices.size() <= 0) {
1249                     if (proc.mMergedItem == null) {
1250                         proc.mMergedItem = new MergedItem(proc.mUserId);
1251                         proc.mMergedItem.mProcess = proc;
1252                     }
1253                     proc.mMergedItem.update(context, false);
1254                     if (proc.mMergedItem.mUserId != mMyUserId) {
1255                         addOtherUserItem(context, newMergedItems, mOtherUserMergedItems,
1256                                 proc.mMergedItem);
1257                     } else {
1258                         newMergedItems.add(0, proc.mMergedItem);
1259                     }
1260                     mProcessItems.add(proc);
1261                 }
1262             }
1263 
1264             // Finally finally, user aggregated merged items need to be
1265             // updated now that they have all of their children.
1266             final int NU = mOtherUserMergedItems.size();
1267             for (int i=0; i<NU; i++) {
1268                 MergedItem user = mOtherUserMergedItems.valueAt(i);
1269                 if (user.mCurSeq == mSequence) {
1270                     user.update(context, false);
1271                 }
1272             }
1273 
1274             synchronized (mLock) {
1275                 mItems = newItems;
1276                 mMergedItems = newMergedItems;
1277             }
1278         }
1279 
1280         // Count number of interesting other (non-active) processes, and
1281         // build a list of all processes we will retrieve memory for.
1282         mAllProcessItems.clear();
1283         mAllProcessItems.addAll(mProcessItems);
1284         int numBackgroundProcesses = 0;
1285         int numForegroundProcesses = 0;
1286         int numServiceProcesses = 0;
1287         NRP = mRunningProcesses.size();
1288         for (int i=0; i<NRP; i++) {
1289             ProcessItem proc = mRunningProcesses.valueAt(i);
1290             if (proc.mCurSeq != mSequence) {
1291                 // We didn't hit this process as a dependency on one
1292                 // of our active ones, so add it up if needed.
1293                 if (proc.mRunningProcessInfo.importance >=
1294                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
1295                     numBackgroundProcesses++;
1296                     mAllProcessItems.add(proc);
1297                 } else if (proc.mRunningProcessInfo.importance <=
1298                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
1299                     numForegroundProcesses++;
1300                     mAllProcessItems.add(proc);
1301                 } else {
1302                     Log.i("RunningState", "Unknown non-service process: "
1303                             + proc.mProcessName + " #" + proc.mPid);
1304                 }
1305             } else {
1306                 numServiceProcesses++;
1307             }
1308         }
1309 
1310         long backgroundProcessMemory = 0;
1311         long foregroundProcessMemory = 0;
1312         long serviceProcessMemory = 0;
1313         ArrayList<MergedItem> newBackgroundItems = null;
1314         ArrayList<MergedItem> newUserBackgroundItems = null;
1315         boolean diffUsers = false;
1316         try {
1317             final int numProc = mAllProcessItems.size();
1318             int[] pids = new int[numProc];
1319             for (int i=0; i<numProc; i++) {
1320                 pids[i] = mAllProcessItems.get(i).mPid;
1321             }
1322             long[] pss = ActivityManager.getService()
1323                     .getProcessPss(pids);
1324             int bgIndex = 0;
1325             for (int i=0; i<pids.length; i++) {
1326                 ProcessItem proc = mAllProcessItems.get(i);
1327                 changed |= proc.updateSize(context, pss[i], mSequence);
1328                 if (proc.mCurSeq == mSequence) {
1329                     serviceProcessMemory += proc.mSize;
1330                 } else if (proc.mRunningProcessInfo.importance >=
1331                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
1332                     backgroundProcessMemory += proc.mSize;
1333                     MergedItem mergedItem;
1334                     if (newBackgroundItems != null) {
1335                         mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
1336                         proc.mMergedItem.mProcess = proc;
1337                         diffUsers |= mergedItem.mUserId != mMyUserId;
1338                         newBackgroundItems.add(mergedItem);
1339                     } else {
1340                         if (bgIndex >= mBackgroundItems.size()
1341                                 || mBackgroundItems.get(bgIndex).mProcess != proc) {
1342                             newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
1343                             for (int bgi=0; bgi<bgIndex; bgi++) {
1344                                 mergedItem = mBackgroundItems.get(bgi);
1345                                 diffUsers |= mergedItem.mUserId != mMyUserId;
1346                                 newBackgroundItems.add(mergedItem);
1347                             }
1348                             mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
1349                             proc.mMergedItem.mProcess = proc;
1350                             diffUsers |= mergedItem.mUserId != mMyUserId;
1351                             newBackgroundItems.add(mergedItem);
1352                         } else {
1353                             mergedItem = mBackgroundItems.get(bgIndex);
1354                         }
1355                     }
1356                     mergedItem.update(context, true);
1357                     mergedItem.updateSize(context);
1358                     bgIndex++;
1359                 } else if (proc.mRunningProcessInfo.importance <=
1360                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
1361                     foregroundProcessMemory += proc.mSize;
1362                 }
1363             }
1364         } catch (RemoteException e) {
1365         }
1366 
1367         if (newBackgroundItems == null) {
1368             // One or more at the bottom may no longer exist.
1369             if (mBackgroundItems.size() > numBackgroundProcesses) {
1370                 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
1371                 for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
1372                     MergedItem mergedItem = mBackgroundItems.get(bgi);
1373                     diffUsers |= mergedItem.mUserId != mMyUserId;
1374                     newBackgroundItems.add(mergedItem);
1375                 }
1376             }
1377         }
1378 
1379         if (newBackgroundItems != null) {
1380             // The background items have changed; we need to re-build the
1381             // per-user items.
1382             if (!diffUsers) {
1383                 // Easy: there are no other users, we can just use the same array.
1384                 newUserBackgroundItems = newBackgroundItems;
1385             } else {
1386                 // We now need to re-build the per-user list so that background
1387                 // items for users are collapsed together.
1388                 newUserBackgroundItems = new ArrayList<MergedItem>();
1389                 final int NB = newBackgroundItems.size();
1390                 for (int i=0; i<NB; i++) {
1391                     MergedItem mergedItem = newBackgroundItems.get(i);
1392                     if (mergedItem.mUserId != mMyUserId) {
1393                         addOtherUserItem(context, newUserBackgroundItems,
1394                                 mOtherUserBackgroundItems, mergedItem);
1395                     } else {
1396                         newUserBackgroundItems.add(mergedItem);
1397                     }
1398                 }
1399                 // And user aggregated merged items need to be
1400                 // updated now that they have all of their children.
1401                 final int NU = mOtherUserBackgroundItems.size();
1402                 for (int i=0; i<NU; i++) {
1403                     MergedItem user = mOtherUserBackgroundItems.valueAt(i);
1404                     if (user.mCurSeq == mSequence) {
1405                         user.update(context, true);
1406                         user.updateSize(context);
1407                     }
1408                 }
1409             }
1410         }
1411 
1412         for (int i=0; i<mMergedItems.size(); i++) {
1413             mMergedItems.get(i).updateSize(context);
1414         }
1415 
1416         synchronized (mLock) {
1417             mNumBackgroundProcesses = numBackgroundProcesses;
1418             mNumForegroundProcesses = numForegroundProcesses;
1419             mNumServiceProcesses = numServiceProcesses;
1420             mBackgroundProcessMemory = backgroundProcessMemory;
1421             mForegroundProcessMemory = foregroundProcessMemory;
1422             mServiceProcessMemory = serviceProcessMemory;
1423             if (newBackgroundItems != null) {
1424                 mBackgroundItems = newBackgroundItems;
1425                 mUserBackgroundItems = newUserBackgroundItems;
1426                 if (mWatchingBackgroundItems) {
1427                     changed = true;
1428                 }
1429             }
1430             if (!mHaveData) {
1431                 mHaveData = true;
1432                 mLock.notifyAll();
1433             }
1434         }
1435 
1436         return changed;
1437     }
1438 
setWatchingBackgroundItems(boolean watching)1439     void setWatchingBackgroundItems(boolean watching) {
1440         synchronized (mLock) {
1441             mWatchingBackgroundItems = watching;
1442         }
1443     }
1444 
getCurrentMergedItems()1445     ArrayList<MergedItem> getCurrentMergedItems() {
1446         synchronized (mLock) {
1447             return mMergedItems;
1448         }
1449     }
1450 
getCurrentBackgroundItems()1451     ArrayList<MergedItem> getCurrentBackgroundItems() {
1452         synchronized (mLock) {
1453             return mUserBackgroundItems;
1454         }
1455     }
1456 }
1457