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