/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.applications; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; import com.android.internal.app.procstats.ProcessState; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.app.procstats.ServiceState; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public final class ProcStatsEntry implements Parcelable { private static final String TAG = "ProcStatsEntry"; private static boolean DEBUG = ProcessStatsUi.DEBUG; final String mPackage; final int mUid; final String mName; public CharSequence mLabel; final ArrayList<String> mPackages = new ArrayList<>(); final long mBgDuration; final long mAvgBgMem; final long mMaxBgMem; final double mBgWeight; final long mRunDuration; final long mAvgRunMem; final long mMaxRunMem; final double mRunWeight; String mBestTargetPackage; ArrayMap<String, ArrayList<Service>> mServices = new ArrayMap<>(1); public ProcStatsEntry(ProcessState proc, String packageName, ProcessStats.ProcessDataCollection tmpBgTotals, ProcessStats.ProcessDataCollection tmpRunTotals, boolean useUss) { proc.computeProcessData(tmpBgTotals, 0); proc.computeProcessData(tmpRunTotals, 0); mPackage = proc.getPackage(); mUid = proc.getUid(); mName = proc.getName(); mPackages.add(packageName); mBgDuration = tmpBgTotals.totalTime; mAvgBgMem = useUss ? tmpBgTotals.avgUss : tmpBgTotals.avgPss; mMaxBgMem = useUss ? tmpBgTotals.maxUss : tmpBgTotals.maxPss; mBgWeight = mAvgBgMem * (double) mBgDuration; mRunDuration = tmpRunTotals.totalTime; mAvgRunMem = useUss ? tmpRunTotals.avgUss : tmpRunTotals.avgPss; mMaxRunMem = useUss ? tmpRunTotals.maxUss : tmpRunTotals.maxPss; mRunWeight = mAvgRunMem * (double) mRunDuration; if (DEBUG) Log.d(TAG, "New proc entry " + proc.getName() + ": dur=" + mBgDuration + " avgpss=" + mAvgBgMem + " weight=" + mBgWeight); } public ProcStatsEntry(String pkgName, int uid, String procName, long duration, long mem, long memDuration) { mPackage = pkgName; mUid = uid; mName = procName; mBgDuration = mRunDuration = duration; mAvgBgMem = mMaxBgMem = mAvgRunMem = mMaxRunMem = mem; mBgWeight = mRunWeight = ((double)memDuration) * mem; if (DEBUG) Log.d(TAG, "New proc entry " + procName + ": dur=" + mBgDuration + " avgpss=" + mAvgBgMem + " weight=" + mBgWeight); } public ProcStatsEntry(Parcel in) { mPackage = in.readString(); mUid = in.readInt(); mName = in.readString(); in.readStringList(mPackages); mBgDuration = in.readLong(); mAvgBgMem = in.readLong(); mMaxBgMem = in.readLong(); mBgWeight = in.readDouble(); mRunDuration = in.readLong(); mAvgRunMem = in.readLong(); mMaxRunMem = in.readLong(); mRunWeight = in.readDouble(); mBestTargetPackage = in.readString(); final int N = in.readInt(); if (N > 0) { mServices.ensureCapacity(N); for (int i=0; i<N; i++) { String key = in.readString(); ArrayList<Service> value = new ArrayList<Service>(); in.readTypedList(value, Service.CREATOR); mServices.append(key, value); } } } public void addPackage(String packageName) { mPackages.add(packageName); } public void evaluateTargetPackage(PackageManager pm, ProcessStats stats, ProcessStats.ProcessDataCollection bgTotals, ProcessStats.ProcessDataCollection runTotals, Comparator<ProcStatsEntry> compare, boolean useUss) { mBestTargetPackage = null; if (mPackages.size() == 1) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": single pkg " + mPackages.get(0)); mBestTargetPackage = mPackages.get(0); return; } // If one of the packages is the framework itself, that wins. // See if there is one significant package that was running here. for (int ipkg=0; ipkg<mPackages.size(); ipkg++) { if ("android".equals(mPackages.get(ipkg))) { mBestTargetPackage = mPackages.get(ipkg); return; } } // Collect information about each package running in the process. ArrayList<ProcStatsEntry> subProcs = new ArrayList<>(); for (int ipkg=0; ipkg<mPackages.size(); ipkg++) { SparseArray<ProcessStats.PackageState> vpkgs = stats.mPackages.get(mPackages.get(ipkg), mUid); for (int ivers=0; ivers<vpkgs.size(); ivers++) { ProcessStats.PackageState pkgState = vpkgs.valueAt(ivers); if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ", pkg " + pkgState + ":"); if (pkgState == null) { Log.w(TAG, "No package state found for " + mPackages.get(ipkg) + "/" + mUid + " in process " + mName); continue; } ProcessState pkgProc = pkgState.mProcesses.get(mName); if (pkgProc == null) { Log.w(TAG, "No process " + mName + " found in package state " + mPackages.get(ipkg) + "/" + mUid); continue; } subProcs.add(new ProcStatsEntry(pkgProc, pkgState.mPackageName, bgTotals, runTotals, useUss)); } } if (subProcs.size() > 1) { Collections.sort(subProcs, compare); if (subProcs.get(0).mRunWeight > (subProcs.get(1).mRunWeight *3)) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": best pkg " + subProcs.get(0).mPackage + " weight " + subProcs.get(0).mRunWeight + " better than " + subProcs.get(1).mPackage + " weight " + subProcs.get(1).mRunWeight); mBestTargetPackage = subProcs.get(0).mPackage; return; } // Couldn't find one that is best by weight, let's decide on best another // way: the one that has the longest running service, accounts for at least // half of the maximum weight, and has specified an explicit app icon. double maxWeight = subProcs.get(0).mRunWeight; long bestRunTime = -1; boolean bestPersistent = false; for (int i=0; i<subProcs.size(); i++) { final ProcStatsEntry subProc = subProcs.get(i); if (subProc.mRunWeight < (maxWeight/2)) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " weight " + subProc.mRunWeight + " too small"); continue; } try { ApplicationInfo ai = pm.getApplicationInfo(subProc.mPackage, 0); if (ai.icon == 0) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " has no icon"); continue; } if ((ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0) { long thisRunTime = subProc.mRunDuration; if (!bestPersistent || thisRunTime > bestRunTime) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " new best pers run time " + thisRunTime); bestRunTime = thisRunTime; bestPersistent = true; } else { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " pers run time " + thisRunTime + " not as good as last " + bestRunTime); } continue; } else if (bestPersistent) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " is not persistent"); continue; } } catch (PackageManager.NameNotFoundException e) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " failed finding app info"); continue; } ArrayList<Service> subProcServices = null; for (int isp=0, NSP=mServices.size(); isp<NSP; isp++) { ArrayList<Service> subServices = mServices.valueAt(isp); if (subServices.get(0).mPackage.equals(subProc.mPackage)) { subProcServices = subServices; break; } } long thisRunTime = 0; if (subProcServices != null) { for (int iss=0, NSS=subProcServices.size(); iss<NSS; iss++) { Service service = subProcServices.get(iss); if (service.mDuration > thisRunTime) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " service " + service.mName + " run time is " + service.mDuration); thisRunTime = service.mDuration; break; } } } if (thisRunTime > bestRunTime) { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " new best run time " + thisRunTime); mBestTargetPackage = subProc.mPackage; bestRunTime = thisRunTime; } else { if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg " + subProc.mPackage + " run time " + thisRunTime + " not as good as last " + bestRunTime); } } // Final fallback, just pick the first subProc. if (TextUtils.isEmpty(mBestTargetPackage)) { mBestTargetPackage = subProcs.get(0).mPackage; } } else if (subProcs.size() == 1) { mBestTargetPackage = subProcs.get(0).mPackage; } } public void addService(ServiceState svc) { ArrayList<Service> services = mServices.get(svc.getPackage()); if (services == null) { services = new ArrayList<Service>(); mServices.put(svc.getPackage(), services); } services.add(new Service(svc)); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackage); dest.writeInt(mUid); dest.writeString(mName); dest.writeStringList(mPackages); dest.writeLong(mBgDuration); dest.writeLong(mAvgBgMem); dest.writeLong(mMaxBgMem); dest.writeDouble(mBgWeight); dest.writeLong(mRunDuration); dest.writeLong(mAvgRunMem); dest.writeLong(mMaxRunMem); dest.writeDouble(mRunWeight); dest.writeString(mBestTargetPackage); final int N = mServices.size(); dest.writeInt(N); for (int i=0; i<N; i++) { dest.writeString(mServices.keyAt(i)); dest.writeTypedList(mServices.valueAt(i)); } } public static final Parcelable.Creator<ProcStatsEntry> CREATOR = new Parcelable.Creator<ProcStatsEntry>() { public ProcStatsEntry createFromParcel(Parcel in) { return new ProcStatsEntry(in); } public ProcStatsEntry[] newArray(int size) { return new ProcStatsEntry[size]; } }; public static final class Service implements Parcelable { final String mPackage; final String mName; final String mProcess; final long mDuration; public Service(ServiceState service) { mPackage = service.getPackage(); mName = service.getName(); mProcess = service.getProcessName(); mDuration = service.dumpTime(null, null, ServiceState.SERVICE_RUN, ProcessStats.STATE_NOTHING, 0, 0); } public Service(Parcel in) { mPackage = in.readString(); mName = in.readString(); mProcess = in.readString(); mDuration = in.readLong(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackage); dest.writeString(mName); dest.writeString(mProcess); dest.writeLong(mDuration); } public static final Parcelable.Creator<Service> CREATOR = new Parcelable.Creator<Service>() { public Service createFromParcel(Parcel in) { return new Service(in); } public Service[] newArray(int size) { return new Service[size]; } }; } }