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