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