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