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