blob: 0821ced23364fa32f25575af4bbe48ecfd805514 [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.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.ProcessStats;
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;
final ArrayList<String> mPackages = new ArrayList<String>();
final long mDuration;
final long mAvgPss;
final long mMaxPss;
final long mAvgUss;
final long mMaxUss;
final long mWeight;
String mBestTargetPackage;
ArrayMap<String, ArrayList<Service>> mServices = new ArrayMap<String, ArrayList<Service>>(1);
public ApplicationInfo mUiTargetApp;
public String mUiLabel;
public String mUiBaseLabel;
public String mUiPackage;
public ProcStatsEntry(ProcessStats.ProcessState proc, String packageName,
ProcessStats.ProcessDataCollection tmpTotals, boolean useUss, boolean weightWithTime) {
ProcessStats.computeProcessData(proc, tmpTotals, 0);
mPackage = proc.mPackage;
mUid = proc.mUid;
mName = proc.mName;
mPackages.add(packageName);
mDuration = tmpTotals.totalTime;
mAvgPss = tmpTotals.avgPss;
mMaxPss = tmpTotals.maxPss;
mAvgUss = tmpTotals.avgUss;
mMaxUss = tmpTotals.maxUss;
mWeight = (weightWithTime ? mDuration : 1) * (useUss ? mAvgUss : mAvgPss);
if (DEBUG) Log.d(TAG, "New proc entry " + proc.mName + ": dur=" + mDuration
+ " avgpss=" + mAvgPss + " weight=" + mWeight);
}
public ProcStatsEntry(Parcel in) {
mPackage = in.readString();
mUid = in.readInt();
mName = in.readString();
in.readStringList(mPackages);
mDuration = in.readLong();
mAvgPss = in.readLong();
mMaxPss = in.readLong();
mAvgUss = in.readLong();
mMaxUss = in.readLong();
mWeight = in.readLong();
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 totals, Comparator<ProcStatsEntry> compare,
boolean useUss, boolean weightWithTime) {
mBestTargetPackage = null;
if (mPackages.size() == 1) {
if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": single pkg " + mPackages.get(0));
mBestTargetPackage = mPackages.get(0);
} else {
// See if there is one significant package that was running here.
ArrayList<ProcStatsEntry> subProcs = new ArrayList<ProcStatsEntry>();
for (int ipkg=0; ipkg<mPackages.size(); ipkg++) {
ProcessStats.PackageState pkgState = stats.mPackages.get(mPackages.get(ipkg), mUid);
if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ", pkg "
+ mPackages.get(ipkg) + ":");
if (pkgState == null) {
Log.w(TAG, "No package state found for " + mPackages.get(ipkg) + "/"
+ mUid + " in process " + mName);
continue;
}
ProcessStats.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, totals, useUss,
weightWithTime));
}
if (subProcs.size() > 1) {
Collections.sort(subProcs, compare);
if (subProcs.get(0).mWeight > (subProcs.get(1).mWeight*3)) {
if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": best pkg "
+ subProcs.get(0).mPackage + " weight " + subProcs.get(0).mWeight
+ " better than " + subProcs.get(1).mPackage
+ " weight " + subProcs.get(1).mWeight);
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.
long maxWeight = subProcs.get(0).mWeight;
long bestRunTime = -1;
for (int i=0; i<subProcs.size(); i++) {
if (subProcs.get(i).mWeight < (maxWeight/2)) {
if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+ subProcs.get(i).mPackage + " weight " + subProcs.get(i).mWeight
+ " too small");
continue;
}
try {
ApplicationInfo ai = pm.getApplicationInfo(subProcs.get(i).mPackage, 0);
if (ai.icon == 0) {
if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+ subProcs.get(i).mPackage + " has no icon");
continue;
}
} catch (PackageManager.NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+ subProcs.get(i).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(subProcs.get(i).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 "
+ subProcs.get(i).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 "
+ subProcs.get(i).mPackage + " new best run time " + thisRunTime);
mBestTargetPackage = subProcs.get(i).mPackage;
bestRunTime = thisRunTime;
} else {
if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ": pkg "
+ subProcs.get(i).mPackage + " run time " + thisRunTime
+ " not as good as last " + bestRunTime);
}
}
} else if (subProcs.size() == 1) {
mBestTargetPackage = subProcs.get(0).mPackage;
}
}
}
public void retrieveUiData(PackageManager pm) {
mUiTargetApp = null;
mUiLabel = mUiBaseLabel = mName;
mUiPackage = mBestTargetPackage;
if (mUiPackage != null) {
// Only one app associated with this process.
try {
mUiTargetApp = pm.getApplicationInfo(mUiPackage,
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES);
String name = mUiBaseLabel = mUiTargetApp.loadLabel(pm).toString();
if (mName.equals(mUiPackage)) {
mUiLabel = name;
} else {
if (mName.startsWith(mUiPackage)) {
int off = mUiPackage.length();
if (mName.length() > off) {
off++;
}
mUiLabel = name + " (" + mName.substring(off) + ")";
} else {
mUiLabel = name + " (" + mName + ")";
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
if (mUiTargetApp == null) {
String[] packages = pm.getPackagesForUid(mUid);
if (packages != null) {
for (String curPkg : packages) {
try {
final PackageInfo pi = pm.getPackageInfo(curPkg,
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES);
if (pi.sharedUserLabel != 0) {
mUiTargetApp = pi.applicationInfo;
final CharSequence nm = pm.getText(curPkg,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
mUiBaseLabel = nm.toString();
mUiLabel = mUiBaseLabel + " (" + mName + ")";
} else {
mUiBaseLabel = mUiTargetApp.loadLabel(pm).toString();
mUiLabel = mUiBaseLabel + " (" + mName + ")";
}
break;
}
} catch (PackageManager.NameNotFoundException e) {
}
}
} else {
// no current packages for this uid, typically because of uninstall
Log.i(TAG, "No package for uid " + mUid);
}
}
}
public void addService(ProcessStats.ServiceState svc) {
ArrayList<Service> services = mServices.get(svc.mPackage);
if (services == null) {
services = new ArrayList<Service>();
mServices.put(svc.mPackage, 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(mDuration);
dest.writeLong(mAvgPss);
dest.writeLong(mMaxPss);
dest.writeLong(mAvgUss);
dest.writeLong(mMaxUss);
dest.writeLong(mWeight);
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(ProcessStats.ServiceState service) {
mPackage = service.mPackage;
mName = service.mName;
mProcess = service.mProcessName;
mDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.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];
}
};
}
}