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