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