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