Home | History | Annotate | Download | only in applications
      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 
     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 
     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 
     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 
    118     public void addPackage(String packageName) {
    119         mPackages.add(packageName);
    120     }
    121 
    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 
    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
    273     public int describeContents() {
    274         return 0;
    275     }
    276 
    277     @Override
    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 
    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 
    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
    333         public int describeContents() {
    334             return 0;
    335         }
    336 
    337         @Override
    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