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