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