ProcStatsEntry.java revision b6c7b050565b259eb098cb2a701254b6676aae1c
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.settings.applications;
18
19import android.content.pm.ApplicationInfo;
20import android.content.pm.PackageManager;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.util.ArrayMap;
24import android.util.Log;
25import android.util.SparseArray;
26import com.android.internal.app.ProcessStats;
27
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.Comparator;
31
32public final class ProcStatsEntry implements Parcelable {
33    private static final String TAG = "ProcStatsEntry";
34    private static boolean DEBUG = ProcessStatsUi.DEBUG;
35
36    final String mPackage;
37    final int mUid;
38    final String mName;
39    public CharSequence mLabel;
40    final ArrayList<String> mPackages = new ArrayList<String>();
41    final long mBgDuration;
42    final long mAvgBgMem;
43    final long mMaxBgMem;
44    final double mBgWeight;
45    final long mRunDuration;
46    final long mAvgRunMem;
47    final long mMaxRunMem;
48    final double mRunWeight;
49
50    String mBestTargetPackage;
51
52    ArrayMap<String, ArrayList<Service>> mServices = new ArrayMap<String, ArrayList<Service>>(1);
53
54    public ProcStatsEntry(ProcessStats.ProcessState proc, String packageName,
55            ProcessStats.ProcessDataCollection tmpBgTotals,
56            ProcessStats.ProcessDataCollection tmpRunTotals, boolean useUss) {
57        ProcessStats.computeProcessData(proc, tmpBgTotals, 0);
58        ProcessStats.computeProcessData(proc, tmpRunTotals, 0);
59        mPackage = proc.mPackage;
60        mUid = proc.mUid;
61        mName = proc.mName;
62        mPackages.add(packageName);
63        mBgDuration = tmpBgTotals.totalTime;
64        mAvgBgMem = useUss ? tmpBgTotals.avgUss : tmpBgTotals.avgPss;
65        mMaxBgMem = useUss ? tmpBgTotals.maxUss : tmpBgTotals.maxPss;
66        mBgWeight = mAvgBgMem * (double) mBgDuration;
67        mRunDuration = tmpRunTotals.totalTime;
68        mAvgRunMem = useUss ? tmpRunTotals.avgUss : tmpRunTotals.avgPss;
69        mMaxRunMem = useUss ? tmpRunTotals.maxUss : tmpRunTotals.maxPss;
70        mRunWeight = mAvgRunMem * (double) mRunDuration;
71        if (DEBUG) Log.d(TAG, "New proc entry " + proc.mName + ": dur=" + mBgDuration
72                + " avgpss=" + mAvgBgMem + " weight=" + mBgWeight);
73    }
74
75    public ProcStatsEntry(String pkgName, int uid, String procName, long duration, long mem,
76            long memDuration) {
77        mPackage = pkgName;
78        mUid = uid;
79        mName = procName;
80        mBgDuration = mRunDuration = duration;
81        mAvgBgMem = mMaxBgMem = mAvgRunMem = mMaxRunMem = mem;
82        mBgWeight = mRunWeight = ((double)memDuration) * mem;
83        if (DEBUG) Log.d(TAG, "New proc entry " + procName + ": dur=" + mBgDuration
84                + " avgpss=" + mAvgBgMem + " weight=" + mBgWeight);
85    }
86
87    public ProcStatsEntry(Parcel in) {
88        mPackage = in.readString();
89        mUid = in.readInt();
90        mName = in.readString();
91        in.readStringList(mPackages);
92        mBgDuration = in.readLong();
93        mAvgBgMem = in.readLong();
94        mMaxBgMem = in.readLong();
95        mBgWeight = in.readDouble();
96        mRunDuration = in.readLong();
97        mAvgRunMem = in.readLong();
98        mMaxRunMem = in.readLong();
99        mRunWeight = in.readDouble();
100        mBestTargetPackage = in.readString();
101        final int N = in.readInt();
102        if (N > 0) {
103            mServices.ensureCapacity(N);
104            for (int i=0; i<N; i++) {
105                String key = in.readString();
106                ArrayList<Service> value = new ArrayList<Service>();
107                in.readTypedList(value, Service.CREATOR);
108                mServices.append(key, value);
109            }
110        }
111    }
112
113    public void addPackage(String packageName) {
114        mPackages.add(packageName);
115    }
116
117    public void evaluateTargetPackage(PackageManager pm, ProcessStats stats,
118            ProcessStats.ProcessDataCollection bgTotals,
119            ProcessStats.ProcessDataCollection runTotals, Comparator<ProcStatsEntry> compare,
120            boolean useUss) {
121        mBestTargetPackage = null;
122        if (mPackages.size() == 1) {
123            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": single pkg " + mPackages.get(0));
124            mBestTargetPackage = mPackages.get(0);
125            return;
126        }
127
128        // If one of the packages is the framework itself, that wins.
129        // See if there is one significant package that was running here.
130        for (int ipkg=0; ipkg<mPackages.size(); ipkg++) {
131            if ("android".equals(mPackages.get(ipkg))) {
132                mBestTargetPackage = mPackages.get(ipkg);
133                return;
134            }
135        }
136
137        // Collect information about each package running in the process.
138        ArrayList<ProcStatsEntry> subProcs = new ArrayList<>();
139        for (int ipkg=0; ipkg<mPackages.size(); ipkg++) {
140            SparseArray<ProcessStats.PackageState> vpkgs
141                    = stats.mPackages.get(mPackages.get(ipkg), mUid);
142            for (int ivers=0;  ivers<vpkgs.size(); ivers++) {
143                ProcessStats.PackageState pkgState = vpkgs.valueAt(ivers);
144                if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ", pkg "
145                        + pkgState + ":");
146                if (pkgState == null) {
147                    Log.w(TAG, "No package state found for " + mPackages.get(ipkg) + "/"
148                            + mUid + " in process " + mName);
149                    continue;
150                }
151                ProcessStats.ProcessState pkgProc = pkgState.mProcesses.get(mName);
152                if (pkgProc == null) {
153                    Log.w(TAG, "No process " + mName + " found in package state "
154                            + mPackages.get(ipkg) + "/" + mUid);
155                    continue;
156                }
157                subProcs.add(new ProcStatsEntry(pkgProc, pkgState.mPackageName, bgTotals,
158                        runTotals, useUss));
159            }
160        }
161
162        if (subProcs.size() > 1) {
163            Collections.sort(subProcs, compare);
164            if (subProcs.get(0).mRunWeight > (subProcs.get(1).mRunWeight *3)) {
165                if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": best pkg "
166                        + subProcs.get(0).mPackage + " weight " + subProcs.get(0).mRunWeight
167                        + " better than " + subProcs.get(1).mPackage
168                        + " weight " + subProcs.get(1).mRunWeight);
169                mBestTargetPackage = subProcs.get(0).mPackage;
170                return;
171            }
172            // Couldn't find one that is best by weight, let's decide on best another
173            // way: the one that has the longest running service, accounts for at least
174            // half of the maximum weight, and has specified an explicit app icon.
175            double maxWeight = subProcs.get(0).mRunWeight;
176            long bestRunTime = -1;
177            boolean bestPersistent = false;
178            for (int i=0; i<subProcs.size(); i++) {
179                final ProcStatsEntry subProc = subProcs.get(i);
180                if (subProc.mRunWeight < (maxWeight/2)) {
181                    if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
182                            + subProc.mPackage + " weight " + subProc.mRunWeight
183                            + " too small");
184                    continue;
185                }
186                try {
187                    ApplicationInfo ai = pm.getApplicationInfo(subProc.mPackage, 0);
188                    if (ai.icon == 0) {
189                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
190                                + subProc.mPackage + " has no icon");
191                        continue;
192                    }
193                    if ((ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0) {
194                        long thisRunTime = subProc.mRunDuration;
195                        if (!bestPersistent || thisRunTime > bestRunTime) {
196                            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
197                                    + subProc.mPackage + " new best pers run time "
198                                    + thisRunTime);
199                            bestRunTime = thisRunTime;
200                            bestPersistent = true;
201                        } else {
202                            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
203                                    + subProc.mPackage + " pers run time " + thisRunTime
204                                    + " not as good as last " + bestRunTime);
205                        }
206                        continue;
207                    } else if (bestPersistent) {
208                        if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
209                                + subProc.mPackage + " is not persistent");
210                        continue;
211                    }
212                } catch (PackageManager.NameNotFoundException e) {
213                    if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
214                            + subProc.mPackage + " failed finding app info");
215                    continue;
216                }
217                ArrayList<Service> subProcServices = null;
218                for (int isp=0, NSP=mServices.size(); isp<NSP; isp++) {
219                    ArrayList<Service> subServices = mServices.valueAt(isp);
220                    if (subServices.get(0).mPackage.equals(subProc.mPackage)) {
221                        subProcServices = subServices;
222                        break;
223                    }
224                }
225                long thisRunTime = 0;
226                if (subProcServices != null) {
227                    for (int iss=0, NSS=subProcServices.size(); iss<NSS; iss++) {
228                        Service service = subProcServices.get(iss);
229                        if (service.mDuration > thisRunTime) {
230                            if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
231                                    + subProc.mPackage + " service " + service.mName
232                                    + " run time is " + service.mDuration);
233                            thisRunTime = service.mDuration;
234                            break;
235                        }
236                    }
237                }
238                if (thisRunTime > bestRunTime) {
239                    if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
240                            + subProc.mPackage + " new best run time " + thisRunTime);
241                    mBestTargetPackage = subProc.mPackage;
242                    bestRunTime = thisRunTime;
243                } else {
244                    if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
245                            + subProc.mPackage + " run time " + thisRunTime
246                            + " not as good as last " + bestRunTime);
247                }
248            }
249        } else if (subProcs.size() == 1) {
250            mBestTargetPackage = subProcs.get(0).mPackage;
251        }
252    }
253
254    public void addService(ProcessStats.ServiceState svc) {
255        ArrayList<Service> services = mServices.get(svc.mPackage);
256        if (services == null) {
257            services = new ArrayList<Service>();
258            mServices.put(svc.mPackage, services);
259        }
260        services.add(new Service(svc));
261    }
262
263    @Override
264    public int describeContents() {
265        return 0;
266    }
267
268    @Override
269    public void writeToParcel(Parcel dest, int flags) {
270        dest.writeString(mPackage);
271        dest.writeInt(mUid);
272        dest.writeString(mName);
273        dest.writeStringList(mPackages);
274        dest.writeLong(mBgDuration);
275        dest.writeLong(mAvgBgMem);
276        dest.writeLong(mMaxBgMem);
277        dest.writeDouble(mBgWeight);
278        dest.writeLong(mRunDuration);
279        dest.writeLong(mAvgRunMem);
280        dest.writeLong(mMaxRunMem);
281        dest.writeDouble(mRunWeight);
282        dest.writeString(mBestTargetPackage);
283        final int N = mServices.size();
284        dest.writeInt(N);
285        for (int i=0; i<N; i++) {
286            dest.writeString(mServices.keyAt(i));
287            dest.writeTypedList(mServices.valueAt(i));
288        }
289    }
290
291    public static final Parcelable.Creator<ProcStatsEntry> CREATOR
292            = new Parcelable.Creator<ProcStatsEntry>() {
293        public ProcStatsEntry createFromParcel(Parcel in) {
294            return new ProcStatsEntry(in);
295        }
296
297        public ProcStatsEntry[] newArray(int size) {
298            return new ProcStatsEntry[size];
299        }
300    };
301
302    public static final class Service implements Parcelable {
303        final String mPackage;
304        final String mName;
305        final String mProcess;
306        final long mDuration;
307
308        public Service(ProcessStats.ServiceState service) {
309            mPackage = service.mPackage;
310            mName = service.mName;
311            mProcess = service.mProcessName;
312            mDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
313                    ProcessStats.ServiceState.SERVICE_RUN,
314                    ProcessStats.STATE_NOTHING, 0, 0);
315        }
316
317        public Service(Parcel in) {
318            mPackage = in.readString();
319            mName = in.readString();
320            mProcess = in.readString();
321            mDuration = in.readLong();
322        }
323
324        @Override
325        public int describeContents() {
326            return 0;
327        }
328
329        @Override
330        public void writeToParcel(Parcel dest, int flags) {
331            dest.writeString(mPackage);
332            dest.writeString(mName);
333            dest.writeString(mProcess);
334            dest.writeLong(mDuration);
335        }
336
337        public static final Parcelable.Creator<Service> CREATOR
338                = new Parcelable.Creator<Service>() {
339            public Service createFromParcel(Parcel in) {
340                return new Service(in);
341            }
342
343            public Service[] newArray(int size) {
344                return new Service[size];
345            }
346        };
347    }
348}
349