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.Utils;
49import com.android.settingslib.applications.InterestingConfigChanges;
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        UserInfo userInfo = mUm.getUserInfo(mMyUserId);
785        mHideManagedProfiles = userInfo == null || !userInfo.canHaveProfile();
786        mResumed = false;
787        mBackgroundThread = new HandlerThread("RunningState:Background");
788        mBackgroundThread.start();
789        mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
790        mUmBroadcastReceiver.register(mApplicationContext);
791    }
792
793    void resume(OnRefreshUiListener listener) {
794        synchronized (mLock) {
795            mResumed = true;
796            mRefreshUiListener = listener;
797            boolean usersChanged = mUmBroadcastReceiver.checkUsersChangedLocked();
798            boolean configChanged =
799                    mInterestingConfigChanges.applyNewConfig(mApplicationContext.getResources());
800            if (usersChanged || configChanged) {
801                mHaveData = false;
802                mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
803                mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
804                mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
805            }
806            if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
807                mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
808            }
809            mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
810        }
811    }
812
813    void updateNow() {
814        synchronized (mLock) {
815            mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
816            mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
817        }
818    }
819
820    boolean hasData() {
821        synchronized (mLock) {
822            return mHaveData;
823        }
824    }
825
826    void waitForData() {
827        synchronized (mLock) {
828            while (!mHaveData) {
829                try {
830                    mLock.wait(0);
831                } catch (InterruptedException e) {
832                }
833            }
834        }
835    }
836
837    void pause() {
838        synchronized (mLock) {
839            mResumed = false;
840            mRefreshUiListener = null;
841            mHandler.removeMessages(MSG_UPDATE_TIME);
842        }
843    }
844
845    private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) {
846        if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
847            return true;
848        }
849        if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0
850                && pi.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
851                && pi.importance < ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
852                && pi.importanceReasonCode
853                        == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
854            return true;
855        }
856        return false;
857    }
858
859    private void reset() {
860        mServiceProcessesByName.clear();
861        mServiceProcessesByPid.clear();
862        mInterestingProcesses.clear();
863        mRunningProcesses.clear();
864        mProcessItems.clear();
865        mAllProcessItems.clear();
866    }
867
868    private void addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems,
869            SparseArray<MergedItem> userItems, MergedItem newItem) {
870        MergedItem userItem = userItems.get(newItem.mUserId);
871        boolean first = userItem == null || userItem.mCurSeq != mSequence;
872        if (first) {
873            UserInfo info = mUm.getUserInfo(newItem.mUserId);
874            if (info == null) {
875                // The user no longer exists, skip
876                return;
877            }
878            if (mHideManagedProfiles && info.isManagedProfile()) {
879                return;
880            }
881            if (userItem == null) {
882                userItem = new MergedItem(newItem.mUserId);
883                userItems.put(newItem.mUserId, userItem);
884            } else {
885                userItem.mChildren.clear();
886            }
887            userItem.mCurSeq = mSequence;
888            userItem.mUser = new UserState();
889            userItem.mUser.mInfo = info;
890            userItem.mUser.mIcon = Utils.getUserIcon(context, mUm, info);
891            userItem.mUser.mLabel = Utils.getUserLabel(context, info);
892            newMergedItems.add(userItem);
893        }
894        userItem.mChildren.add(newItem);
895    }
896
897    private boolean update(Context context, ActivityManager am) {
898        final PackageManager pm = context.getPackageManager();
899
900        mSequence++;
901
902        boolean changed = false;
903
904        // Retrieve list of services, filtering out anything that definitely
905        // won't be shown in the UI.
906        List<ActivityManager.RunningServiceInfo> services
907                = am.getRunningServices(MAX_SERVICES);
908        int NS = services != null ? services.size() : 0;
909        for (int i=0; i<NS; i++) {
910            ActivityManager.RunningServiceInfo si = services.get(i);
911            // We are not interested in services that have not been started
912            // and don't have a known client, because
913            // there is nothing the user can do about them.
914            if (!si.started && si.clientLabel == 0) {
915                services.remove(i);
916                i--;
917                NS--;
918                continue;
919            }
920            // We likewise don't care about services running in a
921            // persistent process like the system or phone.
922            if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
923                    != 0) {
924                services.remove(i);
925                i--;
926                NS--;
927                continue;
928            }
929        }
930
931        // Retrieve list of running processes, organizing them into a sparse
932        // array for easy retrieval.
933        List<ActivityManager.RunningAppProcessInfo> processes
934                = am.getRunningAppProcesses();
935        final int NP = processes != null ? processes.size() : 0;
936        mTmpAppProcesses.clear();
937        for (int i=0; i<NP; i++) {
938            ActivityManager.RunningAppProcessInfo pi = processes.get(i);
939            mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi));
940        }
941
942        // Initial iteration through running services to collect per-process
943        // info about them.
944        for (int i=0; i<NS; i++) {
945            ActivityManager.RunningServiceInfo si = services.get(i);
946            if (si.restarting == 0 && si.pid > 0) {
947                AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
948                if (ainfo != null) {
949                    ainfo.hasServices = true;
950                    if (si.foreground) {
951                        ainfo.hasForegroundServices = true;
952                    }
953                }
954            }
955        }
956
957        // Update state we are maintaining about process that are running services.
958        for (int i=0; i<NS; i++) {
959            ActivityManager.RunningServiceInfo si = services.get(i);
960
961            // If this service's process is in use at a higher importance
962            // due to another process bound to one of its services, then we
963            // won't put it in the top-level list of services.  Instead we
964            // want it to be included in the set of processes that the other
965            // process needs.
966            if (si.restarting == 0 && si.pid > 0) {
967                AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid);
968                if (ainfo != null && !ainfo.hasForegroundServices) {
969                    // This process does not have any foreground services.
970                    // If its importance is greater than the service importance
971                    // then there is something else more significant that is
972                    // keeping it around that it should possibly be included as
973                    // a part of instead of being shown by itself.
974                    if (ainfo.info.importance
975                            < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
976                        // Follow process chain to see if there is something
977                        // else that could be shown
978                        boolean skip = false;
979                        ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
980                        while (ainfo != null) {
981                            if (ainfo.hasServices || isInterestingProcess(ainfo.info)) {
982                                skip = true;
983                                break;
984                            }
985                            ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid);
986                        }
987                        if (skip) {
988                            continue;
989                        }
990                    }
991                }
992            }
993
994            HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
995            if (procs == null) {
996                procs = new HashMap<String, ProcessItem>();
997                mServiceProcessesByName.put(si.uid, procs);
998            }
999            ProcessItem proc = procs.get(si.process);
1000            if (proc == null) {
1001                changed = true;
1002                proc = new ProcessItem(context, si.uid, si.process);
1003                procs.put(si.process, proc);
1004            }
1005
1006            if (proc.mCurSeq != mSequence) {
1007                int pid = si.restarting == 0 ? si.pid : 0;
1008                if (pid != proc.mPid) {
1009                    changed = true;
1010                    if (proc.mPid != pid) {
1011                        if (proc.mPid != 0) {
1012                            mServiceProcessesByPid.remove(proc.mPid);
1013                        }
1014                        if (pid != 0) {
1015                            mServiceProcessesByPid.put(pid, proc);
1016                        }
1017                        proc.mPid = pid;
1018                    }
1019                }
1020                proc.mDependentProcesses.clear();
1021                proc.mCurSeq = mSequence;
1022            }
1023            changed |= proc.updateService(context, si);
1024        }
1025
1026        // Now update the map of other processes that are running (but
1027        // don't have services actively running inside them).
1028        for (int i=0; i<NP; i++) {
1029            ActivityManager.RunningAppProcessInfo pi = processes.get(i);
1030            ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
1031            if (proc == null) {
1032                // This process is not one that is a direct container
1033                // of a service, so look for it in the secondary
1034                // running list.
1035                proc = mRunningProcesses.get(pi.pid);
1036                if (proc == null) {
1037                    changed = true;
1038                    proc = new ProcessItem(context, pi.uid, pi.processName);
1039                    proc.mPid = pi.pid;
1040                    mRunningProcesses.put(pi.pid, proc);
1041                }
1042                proc.mDependentProcesses.clear();
1043            }
1044
1045            if (isInterestingProcess(pi)) {
1046                if (!mInterestingProcesses.contains(proc)) {
1047                    changed = true;
1048                    mInterestingProcesses.add(proc);
1049                }
1050                proc.mCurSeq = mSequence;
1051                proc.mInteresting = true;
1052                proc.ensureLabel(pm);
1053            } else {
1054                proc.mInteresting = false;
1055            }
1056
1057            proc.mRunningSeq = mSequence;
1058            proc.mRunningProcessInfo = pi;
1059        }
1060
1061        // Build the chains from client processes to the process they are
1062        // dependent on; also remove any old running processes.
1063        int NRP = mRunningProcesses.size();
1064        for (int i = 0; i < NRP;) {
1065            ProcessItem proc = mRunningProcesses.valueAt(i);
1066            if (proc.mRunningSeq == mSequence) {
1067                int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
1068                if (clientPid != 0) {
1069                    ProcessItem client = mServiceProcessesByPid.get(clientPid);
1070                    if (client == null) {
1071                        client = mRunningProcesses.get(clientPid);
1072                    }
1073                    if (client != null) {
1074                        client.mDependentProcesses.put(proc.mPid, proc);
1075                    }
1076                } else {
1077                    // In this pass the process doesn't have a client.
1078                    // Clear to make sure that, if it later gets the same one,
1079                    // we will detect the change.
1080                    proc.mClient = null;
1081                }
1082                i++;
1083            } else {
1084                changed = true;
1085                mRunningProcesses.remove(mRunningProcesses.keyAt(i));
1086                NRP--;
1087            }
1088        }
1089
1090        // Remove any old interesting processes.
1091        int NHP = mInterestingProcesses.size();
1092        for (int i=0; i<NHP; i++) {
1093            ProcessItem proc = mInterestingProcesses.get(i);
1094            if (!proc.mInteresting || mRunningProcesses.get(proc.mPid) == null) {
1095                changed = true;
1096                mInterestingProcesses.remove(i);
1097                i--;
1098                NHP--;
1099            }
1100        }
1101
1102        // Follow the tree from all primary service processes to all
1103        // processes they are dependent on, marking these processes as
1104        // still being active and determining if anything has changed.
1105        final int NAP = mServiceProcessesByPid.size();
1106        for (int i=0; i<NAP; i++) {
1107            ProcessItem proc = mServiceProcessesByPid.valueAt(i);
1108            if (proc.mCurSeq == mSequence) {
1109                changed |= proc.buildDependencyChain(context, pm, mSequence);
1110            }
1111        }
1112
1113        // Look for services and their primary processes that no longer exist...
1114        ArrayList<Integer> uidToDelete = null;
1115        for (int i=0; i<mServiceProcessesByName.size(); i++) {
1116            HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
1117            Iterator<ProcessItem> pit = procs.values().iterator();
1118            while (pit.hasNext()) {
1119                ProcessItem pi = pit.next();
1120                if (pi.mCurSeq == mSequence) {
1121                    pi.ensureLabel(pm);
1122                    if (pi.mPid == 0) {
1123                        // Sanity: a non-process can't be dependent on
1124                        // anything.
1125                        pi.mDependentProcesses.clear();
1126                    }
1127                } else {
1128                    changed = true;
1129                    pit.remove();
1130                    if (procs.size() == 0) {
1131                        if (uidToDelete == null) {
1132                            uidToDelete = new ArrayList<Integer>();
1133                        }
1134                        uidToDelete.add(mServiceProcessesByName.keyAt(i));
1135                    }
1136                    if (pi.mPid != 0) {
1137                        mServiceProcessesByPid.remove(pi.mPid);
1138                    }
1139                    continue;
1140                }
1141                Iterator<ServiceItem> sit = pi.mServices.values().iterator();
1142                while (sit.hasNext()) {
1143                    ServiceItem si = sit.next();
1144                    if (si.mCurSeq != mSequence) {
1145                        changed = true;
1146                        sit.remove();
1147                    }
1148                }
1149            }
1150        }
1151
1152        if (uidToDelete != null) {
1153            for (int i = 0; i < uidToDelete.size(); i++) {
1154                int uid = uidToDelete.get(i);
1155                mServiceProcessesByName.remove(uid);
1156            }
1157        }
1158
1159        if (changed) {
1160            // First determine an order for the services.
1161            ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
1162            for (int i=0; i<mServiceProcessesByName.size(); i++) {
1163                for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
1164                    pi.mIsSystem = false;
1165                    pi.mIsStarted = true;
1166                    pi.mActiveSince = Long.MAX_VALUE;
1167                    for (ServiceItem si : pi.mServices.values()) {
1168                        if (si.mServiceInfo != null
1169                                && (si.mServiceInfo.applicationInfo.flags
1170                                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
1171                            pi.mIsSystem = true;
1172                        }
1173                        if (si.mRunningService != null
1174                                && si.mRunningService.clientLabel != 0) {
1175                            pi.mIsStarted = false;
1176                            if (pi.mActiveSince > si.mRunningService.activeSince) {
1177                                pi.mActiveSince = si.mRunningService.activeSince;
1178                            }
1179                        }
1180                    }
1181                    sortedProcesses.add(pi);
1182                }
1183            }
1184
1185            Collections.sort(sortedProcesses, mServiceProcessComparator);
1186
1187            ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
1188            ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
1189            mProcessItems.clear();
1190            for (int i=0; i<sortedProcesses.size(); i++) {
1191                ProcessItem pi = sortedProcesses.get(i);
1192                pi.mNeedDivider = false;
1193
1194                int firstProc = mProcessItems.size();
1195                // First add processes we are dependent on.
1196                pi.addDependentProcesses(newItems, mProcessItems);
1197                // And add the process itself.
1198                newItems.add(pi);
1199                if (pi.mPid > 0) {
1200                    mProcessItems.add(pi);
1201                }
1202
1203                // Now add the services running in it.
1204                MergedItem mergedItem = null;
1205                boolean haveAllMerged = false;
1206                boolean needDivider = false;
1207                for (ServiceItem si : pi.mServices.values()) {
1208                    si.mNeedDivider = needDivider;
1209                    needDivider = true;
1210                    newItems.add(si);
1211                    if (si.mMergedItem != null) {
1212                        if (mergedItem != null && mergedItem != si.mMergedItem) {
1213                            haveAllMerged = false;
1214                        }
1215                        mergedItem = si.mMergedItem;
1216                    } else {
1217                        haveAllMerged = false;
1218                    }
1219                }
1220
1221                if (!haveAllMerged || mergedItem == null
1222                        || mergedItem.mServices.size() != pi.mServices.size()) {
1223                    // Whoops, we need to build a new MergedItem!
1224                    mergedItem = new MergedItem(pi.mUserId);
1225                    for (ServiceItem si : pi.mServices.values()) {
1226                        mergedItem.mServices.add(si);
1227                        si.mMergedItem = mergedItem;
1228                    }
1229                    mergedItem.mProcess = pi;
1230                    mergedItem.mOtherProcesses.clear();
1231                    for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
1232                        mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
1233                    }
1234                }
1235
1236                mergedItem.update(context, false);
1237                if (mergedItem.mUserId != mMyUserId) {
1238                    addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem);
1239                } else {
1240                    newMergedItems.add(mergedItem);
1241                }
1242            }
1243
1244            // Finally, interesting processes need to be shown and will
1245            // go at the top.
1246            NHP = mInterestingProcesses.size();
1247            for (int i=0; i<NHP; i++) {
1248                ProcessItem proc = mInterestingProcesses.get(i);
1249                if (proc.mClient == null && proc.mServices.size() <= 0) {
1250                    if (proc.mMergedItem == null) {
1251                        proc.mMergedItem = new MergedItem(proc.mUserId);
1252                        proc.mMergedItem.mProcess = proc;
1253                    }
1254                    proc.mMergedItem.update(context, false);
1255                    if (proc.mMergedItem.mUserId != mMyUserId) {
1256                        addOtherUserItem(context, newMergedItems, mOtherUserMergedItems,
1257                                proc.mMergedItem);
1258                    } else {
1259                        newMergedItems.add(0, proc.mMergedItem);
1260                    }
1261                    mProcessItems.add(proc);
1262                }
1263            }
1264
1265            // Finally finally, user aggregated merged items need to be
1266            // updated now that they have all of their children.
1267            final int NU = mOtherUserMergedItems.size();
1268            for (int i=0; i<NU; i++) {
1269                MergedItem user = mOtherUserMergedItems.valueAt(i);
1270                if (user.mCurSeq == mSequence) {
1271                    user.update(context, false);
1272                }
1273            }
1274
1275            synchronized (mLock) {
1276                mItems = newItems;
1277                mMergedItems = newMergedItems;
1278            }
1279        }
1280
1281        // Count number of interesting other (non-active) processes, and
1282        // build a list of all processes we will retrieve memory for.
1283        mAllProcessItems.clear();
1284        mAllProcessItems.addAll(mProcessItems);
1285        int numBackgroundProcesses = 0;
1286        int numForegroundProcesses = 0;
1287        int numServiceProcesses = 0;
1288        NRP = mRunningProcesses.size();
1289        for (int i=0; i<NRP; i++) {
1290            ProcessItem proc = mRunningProcesses.valueAt(i);
1291            if (proc.mCurSeq != mSequence) {
1292                // We didn't hit this process as a dependency on one
1293                // of our active ones, so add it up if needed.
1294                if (proc.mRunningProcessInfo.importance >=
1295                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
1296                    numBackgroundProcesses++;
1297                    mAllProcessItems.add(proc);
1298                } else if (proc.mRunningProcessInfo.importance <=
1299                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
1300                    numForegroundProcesses++;
1301                    mAllProcessItems.add(proc);
1302                } else {
1303                    Log.i("RunningState", "Unknown non-service process: "
1304                            + proc.mProcessName + " #" + proc.mPid);
1305                }
1306            } else {
1307                numServiceProcesses++;
1308            }
1309        }
1310
1311        long backgroundProcessMemory = 0;
1312        long foregroundProcessMemory = 0;
1313        long serviceProcessMemory = 0;
1314        ArrayList<MergedItem> newBackgroundItems = null;
1315        ArrayList<MergedItem> newUserBackgroundItems = null;
1316        boolean diffUsers = false;
1317        try {
1318            final int numProc = mAllProcessItems.size();
1319            int[] pids = new int[numProc];
1320            for (int i=0; i<numProc; i++) {
1321                pids[i] = mAllProcessItems.get(i).mPid;
1322            }
1323            long[] pss = ActivityManagerNative.getDefault()
1324                    .getProcessPss(pids);
1325            int bgIndex = 0;
1326            for (int i=0; i<pids.length; i++) {
1327                ProcessItem proc = mAllProcessItems.get(i);
1328                changed |= proc.updateSize(context, pss[i], mSequence);
1329                if (proc.mCurSeq == mSequence) {
1330                    serviceProcessMemory += proc.mSize;
1331                } else if (proc.mRunningProcessInfo.importance >=
1332                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
1333                    backgroundProcessMemory += proc.mSize;
1334                    MergedItem mergedItem;
1335                    if (newBackgroundItems != null) {
1336                        mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
1337                        proc.mMergedItem.mProcess = proc;
1338                        diffUsers |= mergedItem.mUserId != mMyUserId;
1339                        newBackgroundItems.add(mergedItem);
1340                    } else {
1341                        if (bgIndex >= mBackgroundItems.size()
1342                                || mBackgroundItems.get(bgIndex).mProcess != proc) {
1343                            newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
1344                            for (int bgi=0; bgi<bgIndex; bgi++) {
1345                                mergedItem = mBackgroundItems.get(bgi);
1346                                diffUsers |= mergedItem.mUserId != mMyUserId;
1347                                newBackgroundItems.add(mergedItem);
1348                            }
1349                            mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId);
1350                            proc.mMergedItem.mProcess = proc;
1351                            diffUsers |= mergedItem.mUserId != mMyUserId;
1352                            newBackgroundItems.add(mergedItem);
1353                        } else {
1354                            mergedItem = mBackgroundItems.get(bgIndex);
1355                        }
1356                    }
1357                    mergedItem.update(context, true);
1358                    mergedItem.updateSize(context);
1359                    bgIndex++;
1360                } else if (proc.mRunningProcessInfo.importance <=
1361                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
1362                    foregroundProcessMemory += proc.mSize;
1363                }
1364            }
1365        } catch (RemoteException e) {
1366        }
1367
1368        if (newBackgroundItems == null) {
1369            // One or more at the bottom may no longer exist.
1370            if (mBackgroundItems.size() > numBackgroundProcesses) {
1371                newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
1372                for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
1373                    MergedItem mergedItem = mBackgroundItems.get(bgi);
1374                    diffUsers |= mergedItem.mUserId != mMyUserId;
1375                    newBackgroundItems.add(mergedItem);
1376                }
1377            }
1378        }
1379
1380        if (newBackgroundItems != null) {
1381            // The background items have changed; we need to re-build the
1382            // per-user items.
1383            if (!diffUsers) {
1384                // Easy: there are no other users, we can just use the same array.
1385                newUserBackgroundItems = newBackgroundItems;
1386            } else {
1387                // We now need to re-build the per-user list so that background
1388                // items for users are collapsed together.
1389                newUserBackgroundItems = new ArrayList<MergedItem>();
1390                final int NB = newBackgroundItems.size();
1391                for (int i=0; i<NB; i++) {
1392                    MergedItem mergedItem = newBackgroundItems.get(i);
1393                    if (mergedItem.mUserId != mMyUserId) {
1394                        addOtherUserItem(context, newUserBackgroundItems,
1395                                mOtherUserBackgroundItems, mergedItem);
1396                    } else {
1397                        newUserBackgroundItems.add(mergedItem);
1398                    }
1399                }
1400                // And user aggregated merged items need to be
1401                // updated now that they have all of their children.
1402                final int NU = mOtherUserBackgroundItems.size();
1403                for (int i=0; i<NU; i++) {
1404                    MergedItem user = mOtherUserBackgroundItems.valueAt(i);
1405                    if (user.mCurSeq == mSequence) {
1406                        user.update(context, true);
1407                        user.updateSize(context);
1408                    }
1409                }
1410            }
1411        }
1412
1413        for (int i=0; i<mMergedItems.size(); i++) {
1414            mMergedItems.get(i).updateSize(context);
1415        }
1416
1417        synchronized (mLock) {
1418            mNumBackgroundProcesses = numBackgroundProcesses;
1419            mNumForegroundProcesses = numForegroundProcesses;
1420            mNumServiceProcesses = numServiceProcesses;
1421            mBackgroundProcessMemory = backgroundProcessMemory;
1422            mForegroundProcessMemory = foregroundProcessMemory;
1423            mServiceProcessMemory = serviceProcessMemory;
1424            if (newBackgroundItems != null) {
1425                mBackgroundItems = newBackgroundItems;
1426                mUserBackgroundItems = newUserBackgroundItems;
1427                if (mWatchingBackgroundItems) {
1428                    changed = true;
1429                }
1430            }
1431            if (!mHaveData) {
1432                mHaveData = true;
1433                mLock.notifyAll();
1434            }
1435        }
1436
1437        return changed;
1438    }
1439
1440    void setWatchingBackgroundItems(boolean watching) {
1441        synchronized (mLock) {
1442            mWatchingBackgroundItems = watching;
1443        }
1444    }
1445
1446    ArrayList<MergedItem> getCurrentMergedItems() {
1447        synchronized (mLock) {
1448            return mMergedItems;
1449        }
1450    }
1451
1452    ArrayList<MergedItem> getCurrentBackgroundItems() {
1453        synchronized (mLock) {
1454            return mUserBackgroundItems;
1455        }
1456    }
1457}
1458