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