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.ActivityThread;
21import android.content.BroadcastReceiver;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.ApplicationInfo;
27import android.content.pm.PackageInfo;
28import android.content.pm.PackageItemInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.ServiceInfo;
31import android.content.pm.UserInfo;
32import android.content.res.Resources;
33import android.graphics.drawable.Drawable;
34import android.graphics.drawable.Drawable.ConstantState;
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 com.android.settings.R;
47import com.android.settingslib.Utils;
48import com.android.settingslib.applications.InterestingConfigChanges;
49
50import java.util.ArrayList;
51import java.util.Collections;
52import java.util.Comparator;
53import java.util.HashMap;
54import java.util.Iterator;
55import java.util.List;
56
57/**
58 * Singleton for retrieving and monitoring the state about all running
59 * applications/processes/services.
60 */
61public class RunningState {
62    static final String TAG = "RunningState";
63    static final boolean DEBUG_COMPARE = false;
64
65    static Object sGlobalLock = new Object();
66    static RunningState sInstance;
67
68    static final int MSG_RESET_CONTENTS = 1;
69    static final int MSG_UPDATE_CONTENTS = 2;
70    static final int MSG_REFRESH_UI = 3;
71    static final int MSG_UPDATE_TIME = 4;
72
73    static final long TIME_UPDATE_DELAY = 1000;
74    static final long CONTENTS_UPDATE_DELAY = 2000;
75
76    static final int MAX_SERVICES = 100;
77
78    final Context mApplicationContext;
79    final ActivityManager mAm;
80    final PackageManager mPm;
81    final UserManager mUm;
82    final int mMyUserId;
83    final boolean mHideManagedProfiles;
84
85    OnRefreshUiListener mRefreshUiListener;
86
87    final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
88
89    // Processes that are hosting a service we are interested in, organized
90    // by uid and name.  Note that this mapping does not change even across
91    // service restarts, and during a restart there will still be a process
92    // entry.
93    final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName
94            = new SparseArray<HashMap<String, ProcessItem>>();
95
96    // Processes that are hosting a service we are interested in, organized
97    // by their pid.  These disappear and re-appear as services are restarted.
98    final SparseArray<ProcessItem> mServiceProcessesByPid
99            = new SparseArray<ProcessItem>();
100
101    // Used to sort the interesting processes.
102    final ServiceProcessComparator mServiceProcessComparator
103            = new ServiceProcessComparator();
104
105    // Additional interesting processes to be shown to the user, even if
106    // there is no service running in them.
107    final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>();
108
109    // All currently running processes, for finding dependencies etc.
110    final SparseArray<ProcessItem> mRunningProcesses
111            = new SparseArray<ProcessItem>();
112
113    // The processes associated with services, in sorted order.
114    final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
115
116    // All processes, used for retrieving memory information.
117    final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
118
119    // If there are other users on the device, these are the merged items
120    // representing all items that would be put in mMergedItems for that user.
121    final SparseArray<MergedItem> mOtherUserMergedItems = new SparseArray<MergedItem>();
122
123    // If there are other users on the device, these are the merged items
124    // representing all items that would be put in mUserBackgroundItems for that user.
125    final SparseArray<MergedItem> mOtherUserBackgroundItems = new SparseArray<MergedItem>();
126
127    static class AppProcessInfo {
128        final ActivityManager.RunningAppProcessInfo info;
129        boolean hasServices;
130        boolean hasForegroundServices;
131
132        AppProcessInfo(ActivityManager.RunningAppProcessInfo _info) {
133            info = _info;
134        }
135    }
136
137    // Temporary structure used when updating above information.
138    final SparseArray<AppProcessInfo> mTmpAppProcesses = new SparseArray<AppProcessInfo>();
139
140    int mSequence = 0;
141
142    final Comparator<RunningState.MergedItem> mBackgroundComparator
143        = new Comparator<RunningState.MergedItem>() {
144            @Override
145            public int compare(MergedItem lhs, MergedItem rhs) {
146                if (DEBUG_COMPARE) {
147                    Log.i(TAG, "Comparing " + lhs + " with " + rhs);
148                    Log.i(TAG, "     Proc " + lhs.mProcess + " with " + rhs.mProcess);
149                    Log.i(TAG, "   UserId " + lhs.mUserId + " with " + rhs.mUserId);
150                }
151                if (lhs.mUserId != rhs.mUserId) {
152                    if (lhs.mUserId == mMyUserId) return -1;
153                    if (rhs.mUserId == mMyUserId) return 1;
154                    return lhs.mUserId < rhs.mUserId ? -1 : 1;
155                }
156                if (lhs.mProcess == rhs.mProcess) {
157                    if (lhs.mLabel == rhs.mLabel) {
158                        return 0;
159                    }
160                    return lhs.mLabel != null ? lhs.mLabel.compareTo(rhs.mLabel) : -1;
161                }
162                if (lhs.mProcess == null) return -1;
163                if (rhs.mProcess == null) return 1;
164                if (DEBUG_COMPARE) Log.i(TAG, "    Label " + lhs.mProcess.mLabel
165                        + " with " + rhs.mProcess.mLabel);
166                final ActivityManager.RunningAppProcessInfo lhsInfo
167                        = lhs.mProcess.mRunningProcessInfo;
168                final ActivityManager.RunningAppProcessInfo rhsInfo
169                        = rhs.mProcess.mRunningProcessInfo;
170                final boolean lhsBg = lhsInfo.importance
171                        >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
172                final boolean rhsBg = rhsInfo.importance
173                        >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
174                        if (DEBUG_COMPARE) Log.i(TAG, "       Bg " + lhsBg + " with " + rhsBg);
175                if (lhsBg != rhsBg) {
176                    return lhsBg ? 1 : -1;
177                }
178                final boolean lhsA = (lhsInfo.flags
179                        & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
180                final boolean rhsA = (rhsInfo.flags
181                        & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0;
182                if (DEBUG_COMPARE) Log.i(TAG, "      Act " + lhsA + " with " + rhsA);
183                if (lhsA != rhsA) {
184                    return lhsA ? -1 : 1;
185                }
186                if (DEBUG_COMPARE) Log.i(TAG, "      Lru " + lhsInfo.lru + " with " + rhsInfo.lru);
187                if (lhsInfo.lru != rhsInfo.lru) {
188                    return lhsInfo.lru < rhsInfo.lru ? -1 : 1;
189                }
190                if (lhs.mProcess.mLabel == rhs.mProcess.mLabel) {
191                    return 0;
192                }
193                if (lhs.mProcess.mLabel == null) return 1;
194                if (rhs.mProcess.mLabel == null) return -1;
195                return lhs.mProcess.mLabel.compareTo(rhs.mProcess.mLabel);
196            }
197    };
198
199    // ----- following protected by mLock -----
200
201    // Lock for protecting the state that will be shared between the
202    // background update thread and the UI thread.
203    final Object mLock = new Object();
204
205    boolean mResumed;
206    boolean mHaveData;
207    boolean mWatchingBackgroundItems;
208
209    ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
210    ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
211    ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>();
212    ArrayList<MergedItem> mUserBackgroundItems = new ArrayList<MergedItem>();
213
214    int mNumBackgroundProcesses;
215    long mBackgroundProcessMemory;
216    int mNumForegroundProcesses;
217    long mForegroundProcessMemory;
218    int mNumServiceProcesses;
219    long mServiceProcessMemory;
220
221    // ----- BACKGROUND MONITORING THREAD -----
222
223    final HandlerThread mBackgroundThread;
224    final class BackgroundHandler extends Handler {
225        public BackgroundHandler(Looper looper) {
226            super(looper);
227        }
228
229        @Override
230        public void handleMessage(Message msg) {
231            switch (msg.what) {
232                case MSG_RESET_CONTENTS:
233                    reset();
234                    break;
235                case MSG_UPDATE_CONTENTS:
236                    synchronized (mLock) {
237                        if (!mResumed) {
238                            return;
239                        }
240                    }
241                    Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
242                    cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0;
243                    mHandler.sendMessage(cmd);
244                    removeMessages(MSG_UPDATE_CONTENTS);
245                    msg = obtainMessage(MSG_UPDATE_CONTENTS);
246                    sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
247                    break;
248            }
249        }
250    };
251
252    final BackgroundHandler mBackgroundHandler;
253
254    final Handler mHandler = new Handler() {
255        int mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
256
257        @Override
258        public void handleMessage(Message msg) {
259            switch (msg.what) {
260                case MSG_REFRESH_UI:
261                    mNextUpdate = msg.arg1 != 0
262                            ? OnRefreshUiListener.REFRESH_STRUCTURE
263                            : OnRefreshUiListener.REFRESH_DATA;
264                    break;
265                case MSG_UPDATE_TIME:
266                    synchronized (mLock) {
267                        if (!mResumed) {
268                            return;
269                        }
270                    }
271                    removeMessages(MSG_UPDATE_TIME);
272                    Message m = obtainMessage(MSG_UPDATE_TIME);
273                    sendMessageDelayed(m, TIME_UPDATE_DELAY);
274
275                    if (mRefreshUiListener != null) {
276                        //Log.i("foo", "Refresh UI: " + mNextUpdate
277                        //        + " @ " + SystemClock.uptimeMillis());
278                        mRefreshUiListener.onRefreshUi(mNextUpdate);
279                        mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
280                    }
281                    break;
282            }
283        }
284    };
285
286    private final class UserManagerBroadcastReceiver extends BroadcastReceiver {
287        private volatile boolean usersChanged;
288
289        @Override
290        public void onReceive(Context context, Intent intent) {
291            synchronized (mLock) {
292                if (mResumed) {
293                    mHaveData = false;
294                    mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
295                    mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
296                    mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
297                    mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
298                } else {
299                    usersChanged = true;
300                }
301            }
302        }
303
304        public boolean checkUsersChangedLocked() {
305            boolean oldValue = usersChanged;
306            usersChanged = false;
307            return oldValue;
308        }
309
310        void register(Context context) {
311            IntentFilter filter = new IntentFilter();
312            filter.addAction(Intent.ACTION_USER_STOPPED);
313            filter.addAction(Intent.ACTION_USER_STARTED);
314            filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
315            context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
316        }
317    }
318
319    private final UserManagerBroadcastReceiver mUmBroadcastReceiver =
320            new UserManagerBroadcastReceiver();
321
322    // ----- DATA STRUCTURES -----
323
324    static interface OnRefreshUiListener {
325        public static final int REFRESH_TIME = 0;
326        public static final int REFRESH_DATA = 1;
327        public static final int REFRESH_STRUCTURE = 2;
328
329        public void onRefreshUi(int what);
330    }
331
332    static class UserState {
333        UserInfo mInfo;
334        String mLabel;
335        Drawable mIcon;
336    }
337
338    static class BaseItem {
339        final boolean mIsProcess;
340        final int mUserId;
341
342        PackageItemInfo mPackageInfo;
343        CharSequence mDisplayLabel;
344        String mLabel;
345        String mDescription;
346
347        int mCurSeq;
348
349        long mActiveSince;
350        long mSize;
351        String mSizeStr;
352        String mCurSizeStr;
353        boolean mNeedDivider;
354        boolean mBackground;
355
356        public BaseItem(boolean isProcess, int userId) {
357            mIsProcess = isProcess;
358            mUserId = userId;
359        }
360
361        public Drawable loadIcon(Context context, RunningState state) {
362            if (mPackageInfo != null) {
363                Drawable unbadgedIcon = mPackageInfo.loadUnbadgedIcon(state.mPm);
364                Drawable icon = state.mPm.getUserBadgedIcon(unbadgedIcon, new UserHandle(mUserId));
365                return icon;
366            }
367            return null;
368        }
369    }
370
371    static class ServiceItem extends BaseItem {
372        ActivityManager.RunningServiceInfo mRunningService;
373        ServiceInfo mServiceInfo;
374        boolean mShownAsStarted;
375
376        MergedItem mMergedItem;
377
378        public ServiceItem(int userId) {
379            super(false, userId);
380        }
381    }
382
383    static class ProcessItem extends BaseItem {
384        final HashMap<ComponentName, ServiceItem> mServices
385                = new HashMap<ComponentName, ServiceItem>();
386        final SparseArray<ProcessItem> mDependentProcesses
387                = new SparseArray<ProcessItem>();
388
389        final int mUid;
390        final String mProcessName;
391        int mPid;
392
393        ProcessItem mClient;
394        int mLastNumDependentProcesses;
395
396        int mRunningSeq;
397        ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
398
399        MergedItem mMergedItem;
400
401        boolean mInteresting;
402
403        // Purely for sorting.
404        boolean mIsSystem;
405        boolean mIsStarted;
406        long mActiveSince;
407
408        public ProcessItem(Context context, int uid, String processName) {
409            super(true, UserHandle.getUserId(uid));
410            mDescription = context.getResources().getString(
411                    R.string.service_process_name, processName);
412            mUid = uid;
413            mProcessName = processName;
414        }
415
416        void ensureLabel(PackageManager pm) {
417            if (mLabel != null) {
418                return;
419            }
420
421            try {
422                ApplicationInfo ai = pm.getApplicationInfo(mProcessName,
423                        PackageManager.MATCH_ANY_USER);
424                if (ai.uid == mUid) {
425                    mDisplayLabel = ai.loadLabel(pm);
426                    mLabel = mDisplayLabel.toString();
427                    mPackageInfo = ai;
428                    return;
429                }
430            } catch (PackageManager.NameNotFoundException e) {
431            }
432
433            // If we couldn't get information about the overall
434            // process, try to find something about the uid.
435            String[] pkgs = pm.getPackagesForUid(mUid);
436
437            // If there is one package with this uid, that is what we want.
438            if (pkgs.length == 1) {
439                try {
440                    ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
441                            PackageManager.MATCH_ANY_USER);
442                    mDisplayLabel = ai.loadLabel(pm);
443                    mLabel = mDisplayLabel.toString();
444                    mPackageInfo = ai;
445                    return;
446                } catch (PackageManager.NameNotFoundException e) {
447                }
448            }
449
450            // If there are multiple, see if one gives us the official name
451            // for this uid.
452            for (String name : pkgs) {
453                try {
454                    PackageInfo pi = pm.getPackageInfo(name, 0);
455                    if (pi.sharedUserLabel != 0) {
456                        CharSequence nm = pm.getText(name,
457                                pi.sharedUserLabel, pi.applicationInfo);
458                        if (nm != null) {
459                            mDisplayLabel = nm;
460                            mLabel = nm.toString();
461                            mPackageInfo = pi.applicationInfo;
462                            return;
463                        }
464                    }
465                } catch (PackageManager.NameNotFoundException e) {
466                }
467            }
468
469            // If still don't have anything to display, just use the
470            // service info.
471            if (mServices.size() > 0) {
472                ApplicationInfo ai = mServices.values().iterator().next()
473                        .mServiceInfo.applicationInfo;
474                mPackageInfo = ai;
475                mDisplayLabel = mPackageInfo.loadLabel(pm);
476                mLabel = mDisplayLabel.toString();
477                return;
478            }
479
480            // Finally... whatever, just pick the first package's name.
481            try {
482                ApplicationInfo ai = pm.getApplicationInfo(pkgs[0],
483                        PackageManager.MATCH_ANY_USER);
484                mDisplayLabel = ai.loadLabel(pm);
485                mLabel = mDisplayLabel.toString();
486                mPackageInfo = ai;
487                return;
488            } catch (PackageManager.NameNotFoundException e) {
489            }
490        }
491
492        boolean updateService(Context context, ActivityManager.RunningServiceInfo service) {
493            final PackageManager pm = context.getPackageManager();
494
495            boolean changed = false;
496            ServiceItem si = mServices.get(service.service);
497            if (si == null) {
498                changed = true;
499                si = new ServiceItem(mUserId);
500                si.mRunningService = service;
501                try {
502                    si.mServiceInfo = ActivityThread.getPackageManager().getServiceInfo(
503                            service.service, PackageManager.MATCH_ANY_USER,
504                            UserHandle.getUserId(service.uid));
505
506                    if (si.mServiceInfo == null) {
507                        Log.d("RunningService", "getServiceInfo returned null for: "
508                                + service.service);
509                        return false;
510                    }
511                } catch (RemoteException e) {
512                }
513                si.mDisplayLabel = makeLabel(pm,
514                        si.mRunningService.service.getClassName(), si.mServiceInfo);
515                mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
516                si.mPackageInfo = si.mServiceInfo.applicationInfo;
517                mServices.put(service.service, si);
518            }
519            si.mCurSeq = mCurSeq;
520            si.mRunningService = service;
521            long activeSince = service.restarting == 0 ? service.activeSince : -1;
522            if (si.mActiveSince != activeSince) {
523                si.mActiveSince = activeSince;
524                changed = true;
525            }
526            if (service.clientPackage != null && service.clientLabel != 0) {
527                if (si.mShownAsStarted) {
528                    si.mShownAsStarted = false;
529                    changed = true;
530                }
531                try {
532                    Resources clientr = pm.getResourcesForApplication(service.clientPackage);
533                    String label = clientr.getString(service.clientLabel);
534                    si.mDescription = context.getResources().getString(
535                            R.string.service_client_name, label);
536                } catch (PackageManager.NameNotFoundException e) {
537                    si.mDescription = null;
538                }
539            } else {
540                if (!si.mShownAsStarted) {
541                    si.mShownAsStarted = true;
542                    changed = true;
543                }
544                si.mDescription = context.getResources().getString(
545                        R.string.service_started_by_app);
546            }
547
548            return changed;
549        }
550
551        boolean updateSize(Context context, long pss, int curSeq) {
552            mSize = pss * 1024;
553            if (mCurSeq == curSeq) {
554                String sizeStr = Formatter.formatShortFileSize(
555                        context, mSize);
556                if (!sizeStr.equals(mSizeStr)){
557                    mSizeStr = sizeStr;
558                    // We update this on the second tick where we update just
559                    // the text in the current items, so no need to say we
560                    // changed here.
561                    return false;
562                }
563            }
564            return false;
565        }
566
567        boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
568            final int NP = mDependentProcesses.size();
569            boolean changed = false;
570            for (int i=0; i<NP; i++) {
571                ProcessItem proc = mDependentProcesses.valueAt(i);
572                if (proc.mClient != this) {
573                    changed = true;
574                    proc.mClient = this;
575                }
576                proc.mCurSeq = curSeq;
577                proc.ensureLabel(pm);
578                changed |= proc.buildDependencyChain(context, pm, curSeq);
579            }
580
581            if (mLastNumDependentProcesses != mDependentProcesses.size()) {
582                changed = true;
583                mLastNumDependentProcesses = mDependentProcesses.size();
584            }
585
586            return changed;
587        }
588
589        void addDependentProcesses(ArrayList<BaseItem> dest,
590                ArrayList<ProcessItem> destProc) {
591            final int NP = mDependentProcesses.size();
592            for (int i=0; i<NP; i++) {
593                ProcessItem proc = mDependentProcesses.valueAt(i);
594                proc.addDependentProcesses(dest, destProc);
595                dest.add(proc);
596                if (proc.mPid > 0) {
597                    destProc.add(proc);
598                }
599            }
600        }
601    }
602
603    static class MergedItem extends BaseItem {
604        ProcessItem mProcess;
605        UserState mUser;
606        final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
607        final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
608        final ArrayList<MergedItem> mChildren = new ArrayList<MergedItem>();
609
610        private int mLastNumProcesses = -1, mLastNumServices = -1;
611
612        MergedItem(int userId) {
613            super(false, userId);
614        }
615
616        private void setDescription(Context context, int numProcesses, int numServices) {
617            if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) {
618                mLastNumProcesses = numProcesses;
619                mLastNumServices = numServices;
620                int resid = R.string.running_processes_item_description_s_s;
621                if (numProcesses != 1) {
622                    resid = numServices != 1
623                            ? R.string.running_processes_item_description_p_p
624                            : R.string.running_processes_item_description_p_s;
625                } else if (numServices != 1) {
626                    resid = R.string.running_processes_item_description_s_p;
627                }
628                mDescription = context.getResources().getString(resid, numProcesses,
629                        numServices);
630            }
631        }
632
633        boolean update(Context context, boolean background) {
634            mBackground = background;
635
636            if (mUser != null) {
637                // This is a merged item that contains a child collection
638                // of items...  that is, it is an entire user, containing
639                // everything associated with that user.  So set it up as such.
640                // For concrete stuff we need about the process of this item,
641                // we will just use the info from the first child.
642                MergedItem child0 = mChildren.get(0);
643                mPackageInfo = child0.mProcess.mPackageInfo;
644                mLabel = mUser != null ? mUser.mLabel : null;
645                mDisplayLabel = mLabel;
646                int numProcesses = 0;
647                int numServices = 0;
648                mActiveSince = -1;
649                for (int i=0; i<mChildren.size(); i++) {
650                    MergedItem child = mChildren.get(i);
651                    numProcesses += child.mLastNumProcesses;
652                    numServices += child.mLastNumServices;
653                    if (child.mActiveSince >= 0 && mActiveSince < child.mActiveSince) {
654                        mActiveSince = child.mActiveSince;
655                    }
656                }
657                if (!mBackground) {
658                    setDescription(context, numProcesses, numServices);
659                }
660            } else {
661                mPackageInfo = mProcess.mPackageInfo;
662                mDisplayLabel = mProcess.mDisplayLabel;
663                mLabel = mProcess.mLabel;
664
665                if (!mBackground) {
666                    setDescription(context, (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(),
667                            mServices.size());
668                }
669
670                mActiveSince = -1;
671                for (int i=0; i<mServices.size(); i++) {
672                    ServiceItem si = mServices.get(i);
673                    if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
674                        mActiveSince = si.mActiveSince;
675                    }
676                }
677            }
678
679            return false;
680        }
681
682        boolean updateSize(Context context) {
683            if (mUser != null) {
684                mSize = 0;
685                for (int i=0; i<mChildren.size(); i++) {
686                    MergedItem child = mChildren.get(i);
687                    child.updateSize(context);
688                    mSize += child.mSize;
689                }
690            } else {
691                mSize = mProcess.mSize;
692                for (int i=0; i<mOtherProcesses.size(); i++) {
693                    mSize += mOtherProcesses.get(i).mSize;
694                }
695            }
696
697            String sizeStr = Formatter.formatShortFileSize(
698                    context, mSize);
699            if (!sizeStr.equals(mSizeStr)){
700                mSizeStr = sizeStr;
701                // We update this on the second tick where we update just
702                // the text in the current items, so no need to say we
703                // changed here.
704                return false;
705            }
706            return false;
707        }
708
709        public Drawable loadIcon(Context context, RunningState state) {
710            if (mUser == null) {
711                return super.loadIcon(context, state);
712            }
713            if (mUser.mIcon != null) {
714                ConstantState constState = mUser.mIcon.getConstantState();
715                if (constState == null) {
716                    return mUser.mIcon;
717                } else {
718                    return constState.newDrawable();
719                }
720            }
721            return context.getDrawable(
722                    com.android.internal.R.drawable.ic_menu_cc);
723        }
724    }
725
726    class ServiceProcessComparator implements Comparator<ProcessItem> {
727        public int compare(ProcessItem object1, ProcessItem object2) {
728            if (object1.mUserId != object2.mUserId) {
729                if (object1.mUserId == mMyUserId) return -1;
730                if (object2.mUserId == mMyUserId) return 1;
731                return object1.mUserId < object2.mUserId ? -1 : 1;
732            }
733            if (object1.mIsStarted != object2.mIsStarted) {
734                // Non-started processes go last.
735                return object1.mIsStarted ? -1 : 1;
736            }
737            if (object1.mIsSystem != object2.mIsSystem) {
738                // System processes go below non-system.
739                return object1.mIsSystem ? 1 : -1;
740            }
741            if (object1.mActiveSince != object2.mActiveSince) {
742                // Remaining ones are sorted with the longest running
743                // services last.
744                return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
745            }
746            return 0;
747        }
748    }
749
750    static CharSequence makeLabel(PackageManager pm,
751            String className, PackageItemInfo item) {
752        if (item != null && (item.labelRes != 0
753                || item.nonLocalizedLabel != null)) {
754            CharSequence label = item.loadLabel(pm);
755            if (label != null) {
756                return label;
757            }
758        }
759
760        String label = className;
761        int tail = label.lastIndexOf('.');
762        if (tail >= 0) {
763            label = label.substring(tail+1, label.length());
764        }
765        return label;
766    }
767
768    static RunningState getInstance(Context context) {
769        synchronized (sGlobalLock) {
770            if (sInstance == null) {
771                sInstance = new RunningState(context);
772            }
773            return sInstance;
774        }
775    }
776
777    private RunningState(Context context) {
778        mApplicationContext = context.getApplicationContext();
779        mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE);
780        mPm = mApplicationContext.getPackageManager();
781        mUm = (UserManager)mApplicationContext.getSystemService(Context.USER_SERVICE);
782        mMyUserId = UserHandle.myUserId();
783        UserInfo userInfo = mUm.getUserInfo(mMyUserId);
784        mHideManagedProfiles = userInfo == null || !userInfo.canHaveProfile();
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 = ActivityManager.getService()
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