1/*
2 * Copyright (C) 2013 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.internal.app.procstats;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.os.SystemClock;
22import android.os.SystemProperties;
23import android.os.UserHandle;
24import android.text.format.DateFormat;
25import android.util.ArrayMap;
26import android.util.ArraySet;
27import android.util.DebugUtils;
28import android.util.Log;
29import android.util.Slog;
30import android.util.SparseArray;
31import android.util.TimeUtils;
32
33import com.android.internal.app.procstats.ProcessStats;
34import com.android.internal.app.procstats.ProcessStats.PackageState;
35import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder;
36import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
37import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
38import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
39import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
40import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
41import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
42import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
43import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
44import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
45import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
46import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
47import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
48import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
49import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
50import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
51import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
52import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
53import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
54import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
55import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
56import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
57import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
58import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
59import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
60import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
61
62import dalvik.system.VMRuntime;
63import libcore.util.EmptyArray;
64
65import java.io.IOException;
66import java.io.InputStream;
67import java.io.PrintWriter;
68import java.util.ArrayList;
69import java.util.Arrays;
70import java.util.Collections;
71import java.util.Comparator;
72import java.util.Objects;
73
74public final class ProcessState {
75    private static final String TAG = "ProcessStats";
76    private static final boolean DEBUG = false;
77    private static final boolean DEBUG_PARCEL = false;
78
79    // Map from process states to the states we track.
80    private static final int[] PROCESS_STATE_TO_STATE = new int[] {
81        STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
82        STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
83        STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
84        STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
85        STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
86        STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP_SLEEPING
87        STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
88        STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
89        STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
90        STATE_BACKUP,                   // ActivityManager.PROCESS_STATE_BACKUP
91        STATE_HEAVY_WEIGHT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
92        STATE_SERVICE,                  // ActivityManager.PROCESS_STATE_SERVICE
93        STATE_RECEIVER,                 // ActivityManager.PROCESS_STATE_RECEIVER
94        STATE_HOME,                     // ActivityManager.PROCESS_STATE_HOME
95        STATE_LAST_ACTIVITY,            // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
96        STATE_CACHED_ACTIVITY,          // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
97        STATE_CACHED_ACTIVITY_CLIENT,   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
98        STATE_CACHED_EMPTY,             // ActivityManager.PROCESS_STATE_CACHED_EMPTY
99    };
100
101    public static final Comparator<ProcessState> COMPARATOR = new Comparator<ProcessState>() {
102            @Override
103            public int compare(ProcessState lhs, ProcessState rhs) {
104                if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
105                    return -1;
106                } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
107                    return 1;
108                }
109                return 0;
110            }
111        };
112
113    static class PssAggr {
114        long pss = 0;
115        long samples = 0;
116
117        void add(long newPss, long newSamples) {
118            pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) )
119                    / (samples+newSamples);
120            samples += newSamples;
121        }
122    }
123
124    // Used by reset to count rather than storing extra maps. Be careful.
125    public int tmpNumInUse;
126    public ProcessState tmpFoundSubProc;
127
128    private final ProcessStats mStats;
129    private final String mName;
130    private final String mPackage;
131    private final int mUid;
132    private final int mVersion;
133    private final DurationsTable mDurations;
134    private final PssTable mPssTable;
135
136    private ProcessState mCommonProcess;
137    private int mCurState = STATE_NOTHING;
138    private long mStartTime;
139
140    private int mLastPssState = STATE_NOTHING;
141    private long mLastPssTime;
142
143    private boolean mActive;
144    private int mNumActiveServices;
145    private int mNumStartedServices;
146
147    private int mNumExcessiveWake;
148    private int mNumExcessiveCpu;
149
150    private int mNumCachedKill;
151    private long mMinCachedKillPss;
152    private long mAvgCachedKillPss;
153    private long mMaxCachedKillPss;
154
155    private boolean mMultiPackage;
156    private boolean mDead;
157
158    // Set in computeProcessTimeLocked and used by COMPARATOR to sort. Be careful.
159    private long mTmpTotalTime;
160
161    /**
162     * Create a new top-level process state, for the initial case where there is only
163     * a single package running in a process.  The initial state is not running.
164     */
165    public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) {
166        mStats = processStats;
167        mName = name;
168        mCommonProcess = this;
169        mPackage = pkg;
170        mUid = uid;
171        mVersion = vers;
172        mDurations = new DurationsTable(processStats.mTableData);
173        mPssTable = new PssTable(processStats.mTableData);
174    }
175
176    /**
177     * Create a new per-package process state for an existing top-level process
178     * state.  The current running state of the top-level process is also copied,
179     * marked as started running at 'now'.
180     */
181    public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name,
182            long now) {
183        mStats = commonProcess.mStats;
184        mName = name;
185        mCommonProcess = commonProcess;
186        mPackage = pkg;
187        mUid = uid;
188        mVersion = vers;
189        mCurState = commonProcess.mCurState;
190        mStartTime = now;
191        mDurations = new DurationsTable(commonProcess.mStats.mTableData);
192        mPssTable = new PssTable(commonProcess.mStats.mTableData);
193    }
194
195    public ProcessState clone(long now) {
196        ProcessState pnew = new ProcessState(this, mPackage, mUid, mVersion, mName, now);
197        pnew.mDurations.addDurations(mDurations);
198        pnew.mPssTable.copyFrom(mPssTable, PSS_COUNT);
199        pnew.mNumExcessiveWake = mNumExcessiveWake;
200        pnew.mNumExcessiveCpu = mNumExcessiveCpu;
201        pnew.mNumCachedKill = mNumCachedKill;
202        pnew.mMinCachedKillPss = mMinCachedKillPss;
203        pnew.mAvgCachedKillPss = mAvgCachedKillPss;
204        pnew.mMaxCachedKillPss = mMaxCachedKillPss;
205        pnew.mActive = mActive;
206        pnew.mNumActiveServices = mNumActiveServices;
207        pnew.mNumStartedServices = mNumStartedServices;
208        return pnew;
209    }
210
211    public String getName() {
212        return mName;
213    }
214
215    public ProcessState getCommonProcess() {
216        return mCommonProcess;
217    }
218
219    /**
220     * Say that we are not part of a shared process, so mCommonProcess = this.
221     */
222    public void makeStandalone() {
223        mCommonProcess = this;
224    }
225
226    public String getPackage() {
227        return mPackage;
228    }
229
230    public int getUid() {
231        return mUid;
232    }
233
234    public int getVersion() {
235        return mVersion;
236    }
237
238    public boolean isMultiPackage() {
239        return mMultiPackage;
240    }
241
242    public void setMultiPackage(boolean val) {
243        mMultiPackage = val;
244    }
245
246    public int getDurationsBucketCount() {
247        return mDurations.getKeyCount();
248    }
249
250    public void add(ProcessState other) {
251        mDurations.addDurations(other.mDurations);
252        mPssTable.mergeStats(other.mPssTable);
253        mNumExcessiveWake += other.mNumExcessiveWake;
254        mNumExcessiveCpu += other.mNumExcessiveCpu;
255        if (other.mNumCachedKill > 0) {
256            addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss,
257                    other.mAvgCachedKillPss, other.mMaxCachedKillPss);
258        }
259    }
260
261    public void resetSafely(long now) {
262        mDurations.resetTable();
263        mPssTable.resetTable();
264        mStartTime = now;
265        mLastPssState = STATE_NOTHING;
266        mLastPssTime = 0;
267        mNumExcessiveWake = 0;
268        mNumExcessiveCpu = 0;
269        mNumCachedKill = 0;
270        mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
271    }
272
273    public void makeDead() {
274        mDead = true;
275    }
276
277    private void ensureNotDead() {
278        if (!mDead) {
279            return;
280        }
281        Slog.w(TAG, "ProcessState dead: name=" + mName
282                + " pkg=" + mPackage + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
283    }
284
285    public void writeToParcel(Parcel out, long now) {
286        out.writeInt(mMultiPackage ? 1 : 0);
287        mDurations.writeToParcel(out);
288        mPssTable.writeToParcel(out);
289        out.writeInt(mNumExcessiveWake);
290        out.writeInt(mNumExcessiveCpu);
291        out.writeInt(mNumCachedKill);
292        if (mNumCachedKill > 0) {
293            out.writeLong(mMinCachedKillPss);
294            out.writeLong(mAvgCachedKillPss);
295            out.writeLong(mMaxCachedKillPss);
296        }
297    }
298
299    public boolean readFromParcel(Parcel in, boolean fully) {
300        boolean multiPackage = in.readInt() != 0;
301        if (fully) {
302            mMultiPackage = multiPackage;
303        }
304        if (DEBUG_PARCEL) Slog.d(TAG, "Reading durations table...");
305        if (!mDurations.readFromParcel(in)) {
306            return false;
307        }
308        if (DEBUG_PARCEL) Slog.d(TAG, "Reading pss table...");
309        if (!mPssTable.readFromParcel(in)) {
310            return false;
311        }
312        mNumExcessiveWake = in.readInt();
313        mNumExcessiveCpu = in.readInt();
314        mNumCachedKill = in.readInt();
315        if (mNumCachedKill > 0) {
316            mMinCachedKillPss = in.readLong();
317            mAvgCachedKillPss = in.readLong();
318            mMaxCachedKillPss = in.readLong();
319        } else {
320            mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
321        }
322        return true;
323    }
324
325    public void makeActive() {
326        ensureNotDead();
327        mActive = true;
328    }
329
330    public void makeInactive() {
331        mActive = false;
332    }
333
334    public boolean isInUse() {
335        return mActive || mNumActiveServices > 0 || mNumStartedServices > 0
336                || mCurState != STATE_NOTHING;
337    }
338
339    public boolean isActive() {
340        return mActive;
341    }
342
343    public boolean hasAnyData() {
344        return !(mDurations.getKeyCount() == 0
345                && mCurState == STATE_NOTHING
346                && mPssTable.getKeyCount() == 0);
347    }
348
349    /**
350     * Update the current state of the given list of processes.
351     *
352     * @param state Current ActivityManager.PROCESS_STATE_*
353     * @param memFactor Current mem factor constant.
354     * @param now Current time.
355     * @param pkgList Processes to update.
356     */
357    public void setState(int state, int memFactor, long now,
358            ArrayMap<String, ProcessStateHolder> pkgList) {
359        if (state < 0) {
360            state = mNumStartedServices > 0
361                    ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
362        } else {
363            state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
364        }
365
366        // First update the common process.
367        mCommonProcess.setState(state, now);
368
369        // If the common process is not multi-package, there is nothing else to do.
370        if (!mCommonProcess.mMultiPackage) {
371            return;
372        }
373
374        if (pkgList != null) {
375            for (int ip=pkgList.size()-1; ip>=0; ip--) {
376                pullFixedProc(pkgList, ip).setState(state, now);
377            }
378        }
379    }
380
381    public void setState(int state, long now) {
382        ensureNotDead();
383        if (!mDead && (mCurState != state)) {
384            //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
385            commitStateTime(now);
386            mCurState = state;
387        }
388    }
389
390    public void commitStateTime(long now) {
391        if (mCurState != STATE_NOTHING) {
392            long dur = now - mStartTime;
393            if (dur > 0) {
394                mDurations.addDuration(mCurState, dur);
395            }
396        }
397        mStartTime = now;
398    }
399
400    public void incActiveServices(String serviceName) {
401        if (DEBUG && "".equals(mName)) {
402            RuntimeException here = new RuntimeException("here");
403            here.fillInStackTrace();
404            Slog.d(TAG, "incActiveServices: " + this + " service=" + serviceName
405                    + " to " + (mNumActiveServices+1), here);
406        }
407        if (mCommonProcess != this) {
408            mCommonProcess.incActiveServices(serviceName);
409        }
410        mNumActiveServices++;
411    }
412
413    public void decActiveServices(String serviceName) {
414        if (DEBUG && "".equals(mName)) {
415            RuntimeException here = new RuntimeException("here");
416            here.fillInStackTrace();
417            Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
418                    + " to " + (mNumActiveServices-1), here);
419        }
420        if (mCommonProcess != this) {
421            mCommonProcess.decActiveServices(serviceName);
422        }
423        mNumActiveServices--;
424        if (mNumActiveServices < 0) {
425            Slog.wtfStack(TAG, "Proc active services underrun: pkg=" + mPackage
426                    + " uid=" + mUid + " proc=" + mName + " service=" + serviceName);
427            mNumActiveServices = 0;
428        }
429    }
430
431    public void incStartedServices(int memFactor, long now, String serviceName) {
432        if (false) {
433            RuntimeException here = new RuntimeException("here");
434            here.fillInStackTrace();
435            Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName
436                    + " to " + (mNumStartedServices+1), here);
437        }
438        if (mCommonProcess != this) {
439            mCommonProcess.incStartedServices(memFactor, now, serviceName);
440        }
441        mNumStartedServices++;
442        if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
443            setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
444        }
445    }
446
447    public void decStartedServices(int memFactor, long now, String serviceName) {
448        if (false) {
449            RuntimeException here = new RuntimeException("here");
450            here.fillInStackTrace();
451            Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
452                    + " to " + (mNumStartedServices-1), here);
453        }
454        if (mCommonProcess != this) {
455            mCommonProcess.decStartedServices(memFactor, now, serviceName);
456        }
457        mNumStartedServices--;
458        if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) {
459            setState(STATE_NOTHING, now);
460        } else if (mNumStartedServices < 0) {
461            Slog.wtfStack(TAG, "Proc started services underrun: pkg="
462                    + mPackage + " uid=" + mUid + " name=" + mName);
463            mNumStartedServices = 0;
464        }
465    }
466
467    public void addPss(long pss, long uss, boolean always,
468            ArrayMap<String, ProcessStateHolder> pkgList) {
469        ensureNotDead();
470        if (!always) {
471            if (mLastPssState == mCurState && SystemClock.uptimeMillis()
472                    < (mLastPssTime+(30*1000))) {
473                return;
474            }
475        }
476        mLastPssState = mCurState;
477        mLastPssTime = SystemClock.uptimeMillis();
478        if (mCurState != STATE_NOTHING) {
479            // First update the common process.
480            mCommonProcess.mPssTable.mergeStats(mCurState, 1, pss, pss, pss, uss, uss, uss);
481
482            // If the common process is not multi-package, there is nothing else to do.
483            if (!mCommonProcess.mMultiPackage) {
484                return;
485            }
486
487            if (pkgList != null) {
488                for (int ip=pkgList.size()-1; ip>=0; ip--) {
489                    pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurState, 1,
490                            pss, pss, pss, uss, uss, uss);
491                }
492            }
493        }
494    }
495
496    public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) {
497        ensureNotDead();
498        mCommonProcess.mNumExcessiveWake++;
499        if (!mCommonProcess.mMultiPackage) {
500            return;
501        }
502
503        for (int ip=pkgList.size()-1; ip>=0; ip--) {
504            pullFixedProc(pkgList, ip).mNumExcessiveWake++;
505        }
506    }
507
508    public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) {
509        ensureNotDead();
510        mCommonProcess.mNumExcessiveCpu++;
511        if (!mCommonProcess.mMultiPackage) {
512            return;
513        }
514
515        for (int ip=pkgList.size()-1; ip>=0; ip--) {
516            pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
517        }
518    }
519
520    private void addCachedKill(int num, long minPss, long avgPss, long maxPss) {
521        if (mNumCachedKill <= 0) {
522            mNumCachedKill = num;
523            mMinCachedKillPss = minPss;
524            mAvgCachedKillPss = avgPss;
525            mMaxCachedKillPss = maxPss;
526        } else {
527            if (minPss < mMinCachedKillPss) {
528                mMinCachedKillPss = minPss;
529            }
530            if (maxPss > mMaxCachedKillPss) {
531                mMaxCachedKillPss = maxPss;
532            }
533            mAvgCachedKillPss = (long)( ((mAvgCachedKillPss*(double)mNumCachedKill) + avgPss)
534                    / (mNumCachedKill+num) );
535            mNumCachedKill += num;
536        }
537    }
538
539    public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
540        ensureNotDead();
541        mCommonProcess.addCachedKill(1, pss, pss, pss);
542        if (!mCommonProcess.mMultiPackage) {
543            return;
544        }
545
546        for (int ip=pkgList.size()-1; ip>=0; ip--) {
547            pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss);
548        }
549    }
550
551    public ProcessState pullFixedProc(String pkgName) {
552        if (mMultiPackage) {
553            // The array map is still pointing to a common process state
554            // that is now shared across packages.  Update it to point to
555            // the new per-package state.
556            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
557            if (vpkg == null) {
558                throw new IllegalStateException("Didn't find package " + pkgName
559                        + " / " + mUid);
560            }
561            PackageState pkg = vpkg.get(mVersion);
562            if (pkg == null) {
563                throw new IllegalStateException("Didn't find package " + pkgName
564                        + " / " + mUid + " vers " + mVersion);
565            }
566            ProcessState proc = pkg.mProcesses.get(mName);
567            if (proc == null) {
568                throw new IllegalStateException("Didn't create per-package process "
569                        + mName + " in pkg " + pkgName + " / " + mUid + " vers " + mVersion);
570            }
571            return proc;
572        }
573        return this;
574    }
575
576    private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList,
577            int index) {
578        ProcessStateHolder holder = pkgList.valueAt(index);
579        ProcessState proc = holder.state;
580        if (mDead && proc.mCommonProcess != proc) {
581            // Somehow we are contining to use a process state that is dead, because
582            // it was not being told it was active during the last commit.  We can recover
583            // from this by generating a fresh new state, but this is bad because we
584            // are losing whatever data we had in the old process state.
585            Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage
586                    + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
587            proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mVersion,
588                    proc.mName);
589        }
590        if (proc.mMultiPackage) {
591            // The array map is still pointing to a common process state
592            // that is now shared across packages.  Update it to point to
593            // the new per-package state.
594            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
595                    proc.mUid);
596            if (vpkg == null) {
597                throw new IllegalStateException("No existing package "
598                        + pkgList.keyAt(index) + "/" + proc.mUid
599                        + " for multi-proc " + proc.mName);
600            }
601            PackageState pkg = vpkg.get(proc.mVersion);
602            if (pkg == null) {
603                throw new IllegalStateException("No existing package "
604                        + pkgList.keyAt(index) + "/" + proc.mUid
605                        + " for multi-proc " + proc.mName + " version " + proc.mVersion);
606            }
607            String savedName = proc.mName;
608            proc = pkg.mProcesses.get(proc.mName);
609            if (proc == null) {
610                throw new IllegalStateException("Didn't create per-package process "
611                        + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
612            }
613            holder.state = proc;
614        }
615        return proc;
616    }
617
618    public long getDuration(int state, long now) {
619        long time = mDurations.getValueForId((byte)state);
620        if (mCurState == state) {
621            time += now - mStartTime;
622        }
623        return time;
624    }
625
626    public long getPssSampleCount(int state) {
627        return mPssTable.getValueForId((byte)state, PSS_SAMPLE_COUNT);
628    }
629
630    public long getPssMinimum(int state) {
631        return mPssTable.getValueForId((byte)state, PSS_MINIMUM);
632    }
633
634    public long getPssAverage(int state) {
635        return mPssTable.getValueForId((byte)state, PSS_AVERAGE);
636    }
637
638    public long getPssMaximum(int state) {
639        return mPssTable.getValueForId((byte)state, PSS_MAXIMUM);
640    }
641
642    public long getPssUssMinimum(int state) {
643        return mPssTable.getValueForId((byte)state, PSS_USS_MINIMUM);
644    }
645
646    public long getPssUssAverage(int state) {
647        return mPssTable.getValueForId((byte)state, PSS_USS_AVERAGE);
648    }
649
650    public long getPssUssMaximum(int state) {
651        return mPssTable.getValueForId((byte)state, PSS_USS_MAXIMUM);
652    }
653
654    /**
655     * Sums up the PSS data and adds it to 'data'.
656     *
657     * @param data The aggregate data is added here.
658     * @param now SystemClock.uptimeMillis()
659     */
660    public void aggregatePss(TotalMemoryUseCollection data, long now) {
661        final PssAggr fgPss = new PssAggr();
662        final PssAggr bgPss = new PssAggr();
663        final PssAggr cachedPss = new PssAggr();
664        boolean havePss = false;
665        for (int i=0; i<mDurations.getKeyCount(); i++) {
666            final int key = mDurations.getKeyAt(i);
667            int type = SparseMappingTable.getIdFromKey(key);
668            int procState = type % STATE_COUNT;
669            long samples = getPssSampleCount(type);
670            if (samples > 0) {
671                long avg = getPssAverage(type);
672                havePss = true;
673                if (procState <= STATE_IMPORTANT_FOREGROUND) {
674                    fgPss.add(avg, samples);
675                } else if (procState <= STATE_RECEIVER) {
676                    bgPss.add(avg, samples);
677                } else {
678                    cachedPss.add(avg, samples);
679                }
680            }
681        }
682        if (!havePss) {
683            return;
684        }
685        boolean fgHasBg = false;
686        boolean fgHasCached = false;
687        boolean bgHasCached = false;
688        if (fgPss.samples < 3 && bgPss.samples > 0) {
689            fgHasBg = true;
690            fgPss.add(bgPss.pss, bgPss.samples);
691        }
692        if (fgPss.samples < 3 && cachedPss.samples > 0) {
693            fgHasCached = true;
694            fgPss.add(cachedPss.pss, cachedPss.samples);
695        }
696        if (bgPss.samples < 3 && cachedPss.samples > 0) {
697            bgHasCached = true;
698            bgPss.add(cachedPss.pss, cachedPss.samples);
699        }
700        if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) {
701            bgPss.add(fgPss.pss, fgPss.samples);
702        }
703        if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) {
704            cachedPss.add(bgPss.pss, bgPss.samples);
705        }
706        if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) {
707            cachedPss.add(fgPss.pss, fgPss.samples);
708        }
709        for (int i=0; i<mDurations.getKeyCount(); i++) {
710            final int key = mDurations.getKeyAt(i);
711            final int type = SparseMappingTable.getIdFromKey(key);
712            long time = mDurations.getValue(key);
713            if (mCurState == type) {
714                time += now - mStartTime;
715            }
716            final int procState = type % STATE_COUNT;
717            data.processStateTime[procState] += time;
718            long samples = getPssSampleCount(type);
719            long avg;
720            if (samples > 0) {
721                avg = getPssAverage(type);
722            } else if (procState <= STATE_IMPORTANT_FOREGROUND) {
723                samples = fgPss.samples;
724                avg = fgPss.pss;
725            } else if (procState <= STATE_RECEIVER) {
726                samples = bgPss.samples;
727                avg = bgPss.pss;
728            } else {
729                samples = cachedPss.samples;
730                avg = cachedPss.pss;
731            }
732            double newAvg = ( (data.processStatePss[procState]
733                    * (double)data.processStateSamples[procState])
734                        + (avg*(double)samples)
735                    ) / (data.processStateSamples[procState]+samples);
736            data.processStatePss[procState] = (long)newAvg;
737            data.processStateSamples[procState] += samples;
738            data.processStateWeight[procState] += avg * (double)time;
739        }
740    }
741
742    public long computeProcessTimeLocked(int[] screenStates, int[] memStates,
743                int[] procStates, long now) {
744        long totalTime = 0;
745        for (int is=0; is<screenStates.length; is++) {
746            for (int im=0; im<memStates.length; im++) {
747                for (int ip=0; ip<procStates.length; ip++) {
748                    int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT)
749                            + procStates[ip];
750                    totalTime += getDuration(bucket, now);
751                }
752            }
753        }
754        mTmpTotalTime = totalTime;
755        return totalTime;
756    }
757
758    public void dumpSummary(PrintWriter pw, String prefix,
759            int[] screenStates, int[] memStates, int[] procStates,
760            long now, long totalTime) {
761        pw.print(prefix);
762        pw.print("* ");
763        pw.print(mName);
764        pw.print(" / ");
765        UserHandle.formatUid(pw, mUid);
766        pw.print(" / v");
767        pw.print(mVersion);
768        pw.println(":");
769        dumpProcessSummaryDetails(pw, prefix, "         TOTAL: ", screenStates, memStates,
770                procStates, now, totalTime, true);
771        dumpProcessSummaryDetails(pw, prefix, "    Persistent: ", screenStates, memStates,
772                new int[] { STATE_PERSISTENT }, now, totalTime, true);
773        dumpProcessSummaryDetails(pw, prefix, "           Top: ", screenStates, memStates,
774                new int[] {STATE_TOP}, now, totalTime, true);
775        dumpProcessSummaryDetails(pw, prefix, "        Imp Fg: ", screenStates, memStates,
776                new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
777        dumpProcessSummaryDetails(pw, prefix, "        Imp Bg: ", screenStates, memStates,
778                new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
779        dumpProcessSummaryDetails(pw, prefix, "        Backup: ", screenStates, memStates,
780                new int[] {STATE_BACKUP}, now, totalTime, true);
781        dumpProcessSummaryDetails(pw, prefix, "     Heavy Wgt: ", screenStates, memStates,
782                new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
783        dumpProcessSummaryDetails(pw, prefix, "       Service: ", screenStates, memStates,
784                new int[] {STATE_SERVICE}, now, totalTime, true);
785        dumpProcessSummaryDetails(pw, prefix, "    Service Rs: ", screenStates, memStates,
786                new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
787        dumpProcessSummaryDetails(pw, prefix, "      Receiver: ", screenStates, memStates,
788                new int[] {STATE_RECEIVER}, now, totalTime, true);
789        dumpProcessSummaryDetails(pw, prefix, "        (Home): ", screenStates, memStates,
790                new int[] {STATE_HOME}, now, totalTime, true);
791        dumpProcessSummaryDetails(pw, prefix, "    (Last Act): ", screenStates, memStates,
792                new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
793        dumpProcessSummaryDetails(pw, prefix, "      (Cached): ", screenStates, memStates,
794                new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
795                        STATE_CACHED_EMPTY}, now, totalTime, true);
796    }
797
798    public void dumpProcessState(PrintWriter pw, String prefix,
799            int[] screenStates, int[] memStates, int[] procStates, long now) {
800        long totalTime = 0;
801        int printedScreen = -1;
802        for (int is=0; is<screenStates.length; is++) {
803            int printedMem = -1;
804            for (int im=0; im<memStates.length; im++) {
805                for (int ip=0; ip<procStates.length; ip++) {
806                    final int iscreen = screenStates[is];
807                    final int imem = memStates[im];
808                    final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
809                    long time = mDurations.getValueForId((byte)bucket);
810                    String running = "";
811                    if (mCurState == bucket) {
812                        running = " (running)";
813                    }
814                    if (time != 0) {
815                        pw.print(prefix);
816                        if (screenStates.length > 1) {
817                            DumpUtils.printScreenLabel(pw, printedScreen != iscreen
818                                    ? iscreen : STATE_NOTHING);
819                            printedScreen = iscreen;
820                        }
821                        if (memStates.length > 1) {
822                            DumpUtils.printMemLabel(pw,
823                                    printedMem != imem ? imem : STATE_NOTHING, '/');
824                            printedMem = imem;
825                        }
826                        pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
827                        TimeUtils.formatDuration(time, pw); pw.println(running);
828                        totalTime += time;
829                    }
830                }
831            }
832        }
833        if (totalTime != 0) {
834            pw.print(prefix);
835            if (screenStates.length > 1) {
836                DumpUtils.printScreenLabel(pw, STATE_NOTHING);
837            }
838            if (memStates.length > 1) {
839                DumpUtils.printMemLabel(pw, STATE_NOTHING, '/');
840            }
841            pw.print("TOTAL  : ");
842            TimeUtils.formatDuration(totalTime, pw);
843            pw.println();
844        }
845    }
846
847    public void dumpPss(PrintWriter pw, String prefix,
848            int[] screenStates, int[] memStates, int[] procStates) {
849        boolean printedHeader = false;
850        int printedScreen = -1;
851        for (int is=0; is<screenStates.length; is++) {
852            int printedMem = -1;
853            for (int im=0; im<memStates.length; im++) {
854                for (int ip=0; ip<procStates.length; ip++) {
855                    final int iscreen = screenStates[is];
856                    final int imem = memStates[im];
857                    final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
858                    long count = getPssSampleCount(bucket);
859                    if (count > 0) {
860                        if (!printedHeader) {
861                            pw.print(prefix);
862                            pw.print("PSS/USS (");
863                            pw.print(mPssTable.getKeyCount());
864                            pw.println(" entries):");
865                            printedHeader = true;
866                        }
867                        pw.print(prefix);
868                        pw.print("  ");
869                        if (screenStates.length > 1) {
870                            DumpUtils.printScreenLabel(pw,
871                                    printedScreen != iscreen ? iscreen : STATE_NOTHING);
872                            printedScreen = iscreen;
873                        }
874                        if (memStates.length > 1) {
875                            DumpUtils.printMemLabel(pw,
876                                    printedMem != imem ? imem : STATE_NOTHING, '/');
877                            printedMem = imem;
878                        }
879                        pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
880                        pw.print(count);
881                        pw.print(" samples ");
882                        DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024);
883                        pw.print(" ");
884                        DebugUtils.printSizeValue(pw, getPssAverage(bucket) * 1024);
885                        pw.print(" ");
886                        DebugUtils.printSizeValue(pw, getPssMaximum(bucket) * 1024);
887                        pw.print(" / ");
888                        DebugUtils.printSizeValue(pw, getPssUssMinimum(bucket) * 1024);
889                        pw.print(" ");
890                        DebugUtils.printSizeValue(pw, getPssUssAverage(bucket) * 1024);
891                        pw.print(" ");
892                        DebugUtils.printSizeValue(pw, getPssUssMaximum(bucket) * 1024);
893                        pw.println();
894                    }
895                }
896            }
897        }
898        if (mNumExcessiveWake != 0) {
899            pw.print(prefix); pw.print("Killed for excessive wake locks: ");
900                    pw.print(mNumExcessiveWake); pw.println(" times");
901        }
902        if (mNumExcessiveCpu != 0) {
903            pw.print(prefix); pw.print("Killed for excessive CPU use: ");
904                    pw.print(mNumExcessiveCpu); pw.println(" times");
905        }
906        if (mNumCachedKill != 0) {
907            pw.print(prefix); pw.print("Killed from cached state: ");
908                    pw.print(mNumCachedKill); pw.print(" times from pss ");
909                    DebugUtils.printSizeValue(pw, mMinCachedKillPss * 1024); pw.print("-");
910                    DebugUtils.printSizeValue(pw, mAvgCachedKillPss * 1024); pw.print("-");
911                    DebugUtils.printSizeValue(pw, mMaxCachedKillPss * 1024); pw.println();
912        }
913    }
914
915    private void dumpProcessSummaryDetails(PrintWriter pw, String prefix,
916            String label, int[] screenStates, int[] memStates, int[] procStates,
917            long now, long totalTime, boolean full) {
918        ProcessStats.ProcessDataCollection totals = new ProcessStats.ProcessDataCollection(
919                screenStates, memStates, procStates);
920        computeProcessData(totals, now);
921        final double percentage = (double) totals.totalTime / (double) totalTime * 100;
922        // We don't print percentages < .01, so just drop those.
923        if (percentage >= 0.005 || totals.numPss != 0) {
924            if (prefix != null) {
925                pw.print(prefix);
926            }
927            if (label != null) {
928                pw.print(label);
929            }
930            totals.print(pw, totalTime, full);
931            if (prefix != null) {
932                pw.println();
933            }
934        }
935    }
936
937    public void dumpInternalLocked(PrintWriter pw, String prefix, boolean dumpAll) {
938        if (dumpAll) {
939            pw.print(prefix); pw.print("myID=");
940                    pw.print(Integer.toHexString(System.identityHashCode(this)));
941                    pw.print(" mCommonProcess=");
942                    pw.print(Integer.toHexString(System.identityHashCode(mCommonProcess)));
943                    pw.print(" mPackage="); pw.println(mPackage);
944            if (mMultiPackage) {
945                pw.print(prefix); pw.print("mMultiPackage="); pw.println(mMultiPackage);
946            }
947            if (this != mCommonProcess) {
948                pw.print(prefix); pw.print("Common Proc: "); pw.print(mCommonProcess.mName);
949                        pw.print("/"); pw.print(mCommonProcess.mUid);
950                        pw.print(" pkg="); pw.println(mCommonProcess.mPackage);
951            }
952        }
953        if (mActive) {
954            pw.print(prefix); pw.print("mActive="); pw.println(mActive);
955        }
956        if (mDead) {
957            pw.print(prefix); pw.print("mDead="); pw.println(mDead);
958        }
959        if (mNumActiveServices != 0 || mNumStartedServices != 0) {
960            pw.print(prefix); pw.print("mNumActiveServices="); pw.print(mNumActiveServices);
961                    pw.print(" mNumStartedServices=");
962                    pw.println(mNumStartedServices);
963        }
964    }
965
966    public void computeProcessData(ProcessStats.ProcessDataCollection data, long now) {
967        data.totalTime = 0;
968        data.numPss = data.minPss = data.avgPss = data.maxPss =
969                data.minUss = data.avgUss = data.maxUss = 0;
970        for (int is=0; is<data.screenStates.length; is++) {
971            for (int im=0; im<data.memStates.length; im++) {
972                for (int ip=0; ip<data.procStates.length; ip++) {
973                    int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT)
974                            + data.procStates[ip];
975                    data.totalTime += getDuration(bucket, now);
976                    long samples = getPssSampleCount(bucket);
977                    if (samples > 0) {
978                        long minPss = getPssMinimum(bucket);
979                        long avgPss = getPssAverage(bucket);
980                        long maxPss = getPssMaximum(bucket);
981                        long minUss = getPssUssMinimum(bucket);
982                        long avgUss = getPssUssAverage(bucket);
983                        long maxUss = getPssUssMaximum(bucket);
984                        if (data.numPss == 0) {
985                            data.minPss = minPss;
986                            data.avgPss = avgPss;
987                            data.maxPss = maxPss;
988                            data.minUss = minUss;
989                            data.avgUss = avgUss;
990                            data.maxUss = maxUss;
991                        } else {
992                            if (minPss < data.minPss) {
993                                data.minPss = minPss;
994                            }
995                            data.avgPss = (long)( ((data.avgPss*(double)data.numPss)
996                                    + (avgPss*(double)samples)) / (data.numPss+samples) );
997                            if (maxPss > data.maxPss) {
998                                data.maxPss = maxPss;
999                            }
1000                            if (minUss < data.minUss) {
1001                                data.minUss = minUss;
1002                            }
1003                            data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
1004                                    + (avgUss*(double)samples)) / (data.numPss+samples) );
1005                            if (maxUss > data.maxUss) {
1006                                data.maxUss = maxUss;
1007                            }
1008                        }
1009                        data.numPss += samples;
1010                    }
1011                }
1012            }
1013        }
1014    }
1015
1016    public void dumpCsv(PrintWriter pw,
1017            boolean sepScreenStates, int[] screenStates, boolean sepMemStates,
1018            int[] memStates, boolean sepProcStates, int[] procStates, long now) {
1019        final int NSS = sepScreenStates ? screenStates.length : 1;
1020        final int NMS = sepMemStates ? memStates.length : 1;
1021        final int NPS = sepProcStates ? procStates.length : 1;
1022        for (int iss=0; iss<NSS; iss++) {
1023            for (int ims=0; ims<NMS; ims++) {
1024                for (int ips=0; ips<NPS; ips++) {
1025                    final int vsscreen = sepScreenStates ? screenStates[iss] : 0;
1026                    final int vsmem = sepMemStates ? memStates[ims] : 0;
1027                    final int vsproc = sepProcStates ? procStates[ips] : 0;
1028                    final int NSA = sepScreenStates ? 1 : screenStates.length;
1029                    final int NMA = sepMemStates ? 1 : memStates.length;
1030                    final int NPA = sepProcStates ? 1 : procStates.length;
1031                    long totalTime = 0;
1032                    for (int isa=0; isa<NSA; isa++) {
1033                        for (int ima=0; ima<NMA; ima++) {
1034                            for (int ipa=0; ipa<NPA; ipa++) {
1035                                final int vascreen = sepScreenStates ? 0 : screenStates[isa];
1036                                final int vamem = sepMemStates ? 0 : memStates[ima];
1037                                final int vaproc = sepProcStates ? 0 : procStates[ipa];
1038                                final int bucket = ((vsscreen + vascreen + vsmem + vamem)
1039                                        * STATE_COUNT) + vsproc + vaproc;
1040                                totalTime += getDuration(bucket, now);
1041                            }
1042                        }
1043                    }
1044                    pw.print(DumpUtils.CSV_SEP);
1045                    pw.print(totalTime);
1046                }
1047            }
1048        }
1049    }
1050
1051    public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, int vers,
1052            String itemName, long now) {
1053        pw.print("pkgproc,");
1054        pw.print(pkgName);
1055        pw.print(",");
1056        pw.print(uid);
1057        pw.print(",");
1058        pw.print(vers);
1059        pw.print(",");
1060        pw.print(DumpUtils.collapseString(pkgName, itemName));
1061        dumpAllStateCheckin(pw, now);
1062        pw.println();
1063        if (mPssTable.getKeyCount() > 0) {
1064            pw.print("pkgpss,");
1065            pw.print(pkgName);
1066            pw.print(",");
1067            pw.print(uid);
1068            pw.print(",");
1069            pw.print(vers);
1070            pw.print(",");
1071            pw.print(DumpUtils.collapseString(pkgName, itemName));
1072            dumpAllPssCheckin(pw);
1073            pw.println();
1074        }
1075        if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
1076            pw.print("pkgkills,");
1077            pw.print(pkgName);
1078            pw.print(",");
1079            pw.print(uid);
1080            pw.print(",");
1081            pw.print(vers);
1082            pw.print(",");
1083            pw.print(DumpUtils.collapseString(pkgName, itemName));
1084            pw.print(",");
1085            pw.print(mNumExcessiveWake);
1086            pw.print(",");
1087            pw.print(mNumExcessiveCpu);
1088            pw.print(",");
1089            pw.print(mNumCachedKill);
1090            pw.print(",");
1091            pw.print(mMinCachedKillPss);
1092            pw.print(":");
1093            pw.print(mAvgCachedKillPss);
1094            pw.print(":");
1095            pw.print(mMaxCachedKillPss);
1096            pw.println();
1097        }
1098    }
1099
1100    public void dumpProcCheckin(PrintWriter pw, String procName, int uid, long now) {
1101        if (mDurations.getKeyCount() > 0) {
1102            pw.print("proc,");
1103            pw.print(procName);
1104            pw.print(",");
1105            pw.print(uid);
1106            dumpAllStateCheckin(pw, now);
1107            pw.println();
1108        }
1109        if (mPssTable.getKeyCount() > 0) {
1110            pw.print("pss,");
1111            pw.print(procName);
1112            pw.print(",");
1113            pw.print(uid);
1114            dumpAllPssCheckin(pw);
1115            pw.println();
1116        }
1117        if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
1118            pw.print("kills,");
1119            pw.print(procName);
1120            pw.print(",");
1121            pw.print(uid);
1122            pw.print(",");
1123            pw.print(mNumExcessiveWake);
1124            pw.print(",");
1125            pw.print(mNumExcessiveCpu);
1126            pw.print(",");
1127            pw.print(mNumCachedKill);
1128            pw.print(",");
1129            pw.print(mMinCachedKillPss);
1130            pw.print(":");
1131            pw.print(mAvgCachedKillPss);
1132            pw.print(":");
1133            pw.print(mMaxCachedKillPss);
1134            pw.println();
1135        }
1136    }
1137
1138    public void dumpAllStateCheckin(PrintWriter pw, long now) {
1139        boolean didCurState = false;
1140        for (int i=0; i<mDurations.getKeyCount(); i++) {
1141            final int key = mDurations.getKeyAt(i);
1142            final int type = SparseMappingTable.getIdFromKey(key);
1143            long time = mDurations.getValue(key);
1144            if (mCurState == type) {
1145                didCurState = true;
1146                time += now - mStartTime;
1147            }
1148            DumpUtils.printProcStateTagAndValue(pw, type, time);
1149        }
1150        if (!didCurState && mCurState != STATE_NOTHING) {
1151            DumpUtils.printProcStateTagAndValue(pw, mCurState, now - mStartTime);
1152        }
1153    }
1154
1155    public void dumpAllPssCheckin(PrintWriter pw) {
1156        final int N = mPssTable.getKeyCount();
1157        for (int i=0; i<N; i++) {
1158            final int key = mPssTable.getKeyAt(i);
1159            final int type = SparseMappingTable.getIdFromKey(key);
1160            pw.print(',');
1161            DumpUtils.printProcStateTag(pw, type);
1162            pw.print(':');
1163            pw.print(mPssTable.getValue(key, PSS_SAMPLE_COUNT));
1164            pw.print(':');
1165            pw.print(mPssTable.getValue(key, PSS_MINIMUM));
1166            pw.print(':');
1167            pw.print(mPssTable.getValue(key, PSS_AVERAGE));
1168            pw.print(':');
1169            pw.print(mPssTable.getValue(key, PSS_MAXIMUM));
1170            pw.print(':');
1171            pw.print(mPssTable.getValue(key, PSS_USS_MINIMUM));
1172            pw.print(':');
1173            pw.print(mPssTable.getValue(key, PSS_USS_AVERAGE));
1174            pw.print(':');
1175            pw.print(mPssTable.getValue(key, PSS_USS_MAXIMUM));
1176        }
1177    }
1178
1179    public String toString() {
1180        StringBuilder sb = new StringBuilder(128);
1181        sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this)))
1182                .append(" ").append(mName).append("/").append(mUid)
1183                .append(" pkg=").append(mPackage);
1184        if (mMultiPackage) sb.append(" (multi)");
1185        if (mCommonProcess != this) sb.append(" (sub)");
1186        sb.append("}");
1187        return sb.toString();
1188    }
1189}
1190