blob: bda2b97fa12e56af8e112291a4d803392d3e4294 [file] [log] [blame]
/*
* 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.LongSparseArray;
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++) {
LongSparseArray<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 int getUid() {
return mUid;
}
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];
}
};
}
}