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