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