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