Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2015 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.app.ActivityManager;
     20 import android.content.Context;
     21 import android.content.pm.PackageManager;
     22 import android.os.ParcelFileDescriptor;
     23 import android.os.RemoteException;
     24 import android.os.ServiceManager;
     25 import android.os.SystemClock;
     26 import android.text.format.Formatter;
     27 import android.util.ArrayMap;
     28 import android.util.Log;
     29 import android.util.SparseArray;
     30 
     31 import com.android.internal.app.ProcessMap;
     32 import com.android.internal.app.procstats.DumpUtils;
     33 import com.android.internal.app.procstats.IProcessStats;
     34 import com.android.internal.app.procstats.ProcessState;
     35 import com.android.internal.app.procstats.ProcessStats;
     36 import com.android.internal.app.procstats.ProcessStats.ProcessDataCollection;
     37 import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
     38 import com.android.internal.app.procstats.ServiceState;
     39 import com.android.internal.util.MemInfoReader;
     40 import com.android.settings.R;
     41 import com.android.settings.Utils;
     42 
     43 import java.io.IOException;
     44 import java.io.InputStream;
     45 import java.util.ArrayList;
     46 import java.util.Comparator;
     47 import java.util.List;
     48 
     49 public class ProcStatsData {
     50 
     51     private static final String TAG = "ProcStatsManager";
     52 
     53     private static final boolean DEBUG = ProcessStatsUi.DEBUG;
     54 
     55     private static ProcessStats sStatsXfer;
     56 
     57     private PackageManager mPm;
     58     private Context mContext;
     59     private long memTotalTime;
     60 
     61     private IProcessStats mProcessStats;
     62     private ProcessStats mStats;
     63 
     64     private boolean mUseUss;
     65     private long mDuration;
     66 
     67     private int[] mMemStates;
     68 
     69     private int[] mStates;
     70 
     71     private MemInfo mMemInfo;
     72 
     73     private ArrayList<ProcStatsPackageEntry> pkgEntries;
     74 
     75     public ProcStatsData(Context context, boolean useXfer) {
     76         mContext = context;
     77         mPm = context.getPackageManager();
     78         mProcessStats = IProcessStats.Stub.asInterface(
     79                 ServiceManager.getService(ProcessStats.SERVICE_NAME));
     80         mMemStates = ProcessStats.ALL_MEM_ADJ;
     81         mStates = ProcessStats.BACKGROUND_PROC_STATES;
     82         if (useXfer) {
     83             mStats = sStatsXfer;
     84         }
     85     }
     86 
     87     public void setTotalTime(int totalTime) {
     88         memTotalTime = totalTime;
     89     }
     90 
     91     public void xferStats() {
     92         sStatsXfer = mStats;
     93     }
     94 
     95     public void setMemStates(int[] memStates) {
     96         mMemStates = memStates;
     97         refreshStats(false);
     98     }
     99 
    100     public void setStats(int[] stats) {
    101         this.mStates = stats;
    102         refreshStats(false);
    103     }
    104 
    105     public int getMemState() {
    106         int factor = mStats.mMemFactor;
    107         if (factor == ProcessStats.ADJ_NOTHING) {
    108             return ProcessStats.ADJ_MEM_FACTOR_NORMAL;
    109         }
    110         if (factor >= ProcessStats.ADJ_SCREEN_ON) {
    111             factor -= ProcessStats.ADJ_SCREEN_ON;
    112         }
    113         return factor;
    114     }
    115 
    116     public MemInfo getMemInfo() {
    117         return mMemInfo;
    118     }
    119 
    120     public long getElapsedTime() {
    121         return mStats.mTimePeriodEndRealtime - mStats.mTimePeriodStartRealtime;
    122     }
    123 
    124     public void setDuration(long duration) {
    125         if (duration != mDuration) {
    126             mDuration = duration;
    127             refreshStats(true);
    128         }
    129     }
    130 
    131     public long getDuration() {
    132         return mDuration;
    133     }
    134 
    135     public List<ProcStatsPackageEntry> getEntries() {
    136         return pkgEntries;
    137     }
    138 
    139     public void refreshStats(boolean forceLoad) {
    140         if (mStats == null || forceLoad) {
    141             load();
    142         }
    143 
    144         pkgEntries = new ArrayList<>();
    145 
    146         long now = SystemClock.uptimeMillis();
    147 
    148         memTotalTime = DumpUtils.dumpSingleTime(null, null, mStats.mMemFactorDurations,
    149                 mStats.mMemFactor, mStats.mStartTime, now);
    150 
    151         ProcessStats.TotalMemoryUseCollection totalMem = new ProcessStats.TotalMemoryUseCollection(
    152                 ProcessStats.ALL_SCREEN_ADJ, mMemStates);
    153         mStats.computeTotalMemoryUse(totalMem, now);
    154 
    155         mMemInfo = new MemInfo(mContext, totalMem, memTotalTime);
    156 
    157         ProcessDataCollection bgTotals = new ProcessDataCollection(
    158                 ProcessStats.ALL_SCREEN_ADJ, mMemStates, mStates);
    159         ProcessDataCollection runTotals = new ProcessDataCollection(
    160                 ProcessStats.ALL_SCREEN_ADJ, mMemStates, ProcessStats.NON_CACHED_PROC_STATES);
    161 
    162         createPkgMap(getProcs(bgTotals, runTotals), bgTotals, runTotals);
    163         if (totalMem.sysMemZRamWeight > 0 && !totalMem.hasSwappedOutPss) {
    164             distributeZRam(totalMem.sysMemZRamWeight);
    165         }
    166 
    167         ProcStatsPackageEntry osPkg = createOsEntry(bgTotals, runTotals, totalMem,
    168                 mMemInfo.baseCacheRam);
    169         pkgEntries.add(osPkg);
    170     }
    171 
    172     private void createPkgMap(ArrayList<ProcStatsEntry> procEntries, ProcessDataCollection bgTotals,
    173             ProcessDataCollection runTotals) {
    174         // Combine processes into packages.
    175         ArrayMap<String, ProcStatsPackageEntry> pkgMap = new ArrayMap<>();
    176         for (int i = procEntries.size() - 1; i >= 0; i--) {
    177             ProcStatsEntry proc = procEntries.get(i);
    178             proc.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
    179             ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage);
    180             if (pkg == null) {
    181                 pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage, memTotalTime);
    182                 pkgMap.put(proc.mBestTargetPackage, pkg);
    183                 pkgEntries.add(pkg);
    184             }
    185             pkg.addEntry(proc);
    186         }
    187     }
    188 
    189     private void distributeZRam(double zramWeight) {
    190         // Distribute kernel's Z-Ram across processes, based on how much they have been running.
    191         // The idea is that the memory used by the kernel for this is not really the kernel's
    192         // responsibility, but that of whoever got swapped in to it...  and we will take how
    193         // much a process runs for as a sign of the proportion of Z-Ram it is responsible for.
    194 
    195         long zramMem = (long) (zramWeight / memTotalTime);
    196         long totalTime = 0;
    197         for (int i = pkgEntries.size() - 1; i >= 0; i--) {
    198             ProcStatsPackageEntry entry = pkgEntries.get(i);
    199             for (int j = entry.mEntries.size() - 1; j >= 0; j--) {
    200                 ProcStatsEntry proc = entry.mEntries.get(j);
    201                 totalTime += proc.mRunDuration;
    202             }
    203         }
    204         for (int i = pkgEntries.size() - 1; i >= 0 && totalTime > 0; i--) {
    205             ProcStatsPackageEntry entry = pkgEntries.get(i);
    206             long pkgRunTime = 0;
    207             long maxRunTime = 0;
    208             for (int j = entry.mEntries.size() - 1; j >= 0; j--) {
    209                 ProcStatsEntry proc = entry.mEntries.get(j);
    210                 pkgRunTime += proc.mRunDuration;
    211                 if (proc.mRunDuration > maxRunTime) {
    212                     maxRunTime = proc.mRunDuration;
    213                 }
    214             }
    215             long pkgZRam = (zramMem*pkgRunTime)/totalTime;
    216             if (pkgZRam > 0) {
    217                 zramMem -= pkgZRam;
    218                 totalTime -= pkgRunTime;
    219                 ProcStatsEntry procEntry = new ProcStatsEntry(entry.mPackage, 0,
    220                         mContext.getString(R.string.process_stats_os_zram), maxRunTime,
    221                         pkgZRam, memTotalTime);
    222                 procEntry.evaluateTargetPackage(mPm, mStats, null, null, sEntryCompare, mUseUss);
    223                 entry.addEntry(procEntry);
    224             }
    225         }
    226     }
    227 
    228     private ProcStatsPackageEntry createOsEntry(ProcessDataCollection bgTotals,
    229             ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam) {
    230         // Add in fake entry representing the OS itself.
    231         ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime);
    232         ProcStatsEntry osEntry;
    233         if (totalMem.sysMemNativeWeight > 0) {
    234             osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
    235                     mContext.getString(R.string.process_stats_os_native), memTotalTime,
    236                     (long) (totalMem.sysMemNativeWeight / memTotalTime), memTotalTime);
    237             osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
    238             osPkg.addEntry(osEntry);
    239         }
    240         if (totalMem.sysMemKernelWeight > 0) {
    241             osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
    242                     mContext.getString(R.string.process_stats_os_kernel), memTotalTime,
    243                     (long) (totalMem.sysMemKernelWeight / memTotalTime), memTotalTime);
    244             osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
    245             osPkg.addEntry(osEntry);
    246         }
    247         /*  Turned off now -- zram is being distributed across running apps.
    248         if (totalMem.sysMemZRamWeight > 0) {
    249             osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
    250                     mContext.getString(R.string.process_stats_os_zram), memTotalTime,
    251                     (long) (totalMem.sysMemZRamWeight / memTotalTime));
    252             osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
    253             osPkg.addEntry(osEntry);
    254         }
    255         */
    256         if (baseCacheRam > 0) {
    257             osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
    258                     mContext.getString(R.string.process_stats_os_cache), memTotalTime,
    259                     baseCacheRam / 1024, memTotalTime);
    260             osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
    261             osPkg.addEntry(osEntry);
    262         }
    263         return osPkg;
    264     }
    265 
    266     private ArrayList<ProcStatsEntry> getProcs(ProcessDataCollection bgTotals,
    267             ProcessDataCollection runTotals) {
    268         final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>();
    269         if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES");
    270 
    271         final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>();
    272         for (int ipkg = 0, N = mStats.mPackages.getMap().size(); ipkg < N; ipkg++) {
    273             final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids = mStats.mPackages
    274                     .getMap().valueAt(ipkg);
    275             for (int iu = 0; iu < pkgUids.size(); iu++) {
    276                 final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu);
    277                 for (int iv = 0; iv < vpkgs.size(); iv++) {
    278                     final ProcessStats.PackageState st = vpkgs.valueAt(iv);
    279                     for (int iproc = 0; iproc < st.mProcesses.size(); iproc++) {
    280                         final ProcessState pkgProc = st.mProcesses.valueAt(iproc);
    281                         final ProcessState proc = mStats.mProcesses.get(pkgProc.getName(),
    282                                 pkgProc.getUid());
    283                         if (proc == null) {
    284                             Log.w(TAG, "No process found for pkg " + st.mPackageName
    285                                     + "/" + st.mUid + " proc name " + pkgProc.getName());
    286                             continue;
    287                         }
    288                         ProcStatsEntry ent = entriesMap.get(proc.getName(), proc.getUid());
    289                         if (ent == null) {
    290                             ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals,
    291                                     mUseUss);
    292                             if (ent.mRunWeight > 0) {
    293                                 if (DEBUG) Log.d(TAG, "Adding proc " + proc.getName() + "/"
    294                                             + proc.getUid() + ": time="
    295                                             + ProcessStatsUi.makeDuration(ent.mRunDuration) + " ("
    296                                             + ((((double) ent.mRunDuration) / memTotalTime) * 100)
    297                                             + "%)"
    298                                             + " pss=" + ent.mAvgRunMem);
    299                                 entriesMap.put(proc.getName(), proc.getUid(), ent);
    300                                 procEntries.add(ent);
    301                             }
    302                         } else {
    303                             ent.addPackage(st.mPackageName);
    304                         }
    305                     }
    306                 }
    307             }
    308         }
    309 
    310         if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES");
    311 
    312         // Add in service info.
    313         for (int ip = 0, N = mStats.mPackages.getMap().size(); ip < N; ip++) {
    314             SparseArray<SparseArray<ProcessStats.PackageState>> uids = mStats.mPackages.getMap()
    315                     .valueAt(ip);
    316             for (int iu = 0; iu < uids.size(); iu++) {
    317                 SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu);
    318                 for (int iv = 0; iv < vpkgs.size(); iv++) {
    319                     ProcessStats.PackageState ps = vpkgs.valueAt(iv);
    320                     for (int is = 0, NS = ps.mServices.size(); is < NS; is++) {
    321                         ServiceState ss = ps.mServices.valueAt(is);
    322                         if (ss.getProcessName() != null) {
    323                             ProcStatsEntry ent = entriesMap.get(ss.getProcessName(),
    324                                     uids.keyAt(iu));
    325                             if (ent != null) {
    326                                 if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName
    327                                             + "/" + ss.getName() + "/" + uids.keyAt(iu)
    328                                             + " to proc " + ss.getProcessName());
    329                                 ent.addService(ss);
    330                             } else {
    331                                 Log.w(TAG, "No process " + ss.getProcessName() + "/"
    332                                         + uids.keyAt(iu) + " for service " + ss.getName());
    333                             }
    334                         }
    335                     }
    336                 }
    337             }
    338         }
    339 
    340         return procEntries;
    341     }
    342 
    343     private void load() {
    344         try {
    345             ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
    346             mStats = new ProcessStats(false);
    347             InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
    348             mStats.read(is);
    349             try {
    350                 is.close();
    351             } catch (IOException e) {
    352             }
    353             if (mStats.mReadError != null) {
    354                 Log.w(TAG, "Failure reading process stats: " + mStats.mReadError);
    355             }
    356         } catch (RemoteException e) {
    357             Log.e(TAG, "RemoteException:", e);
    358         }
    359     }
    360 
    361     public static class MemInfo {
    362         public double realUsedRam;
    363         public double realFreeRam;
    364         public double realTotalRam;
    365         long baseCacheRam;
    366 
    367         double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT];
    368         double freeWeight;
    369         double usedWeight;
    370         double weightToRam;
    371         double totalRam;
    372         double totalScale;
    373         long memTotalTime;
    374 
    375         private MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem,
    376                 long memTotalTime) {
    377             this.memTotalTime = memTotalTime;
    378             calculateWeightInfo(context, totalMem, memTotalTime);
    379 
    380             double usedRam = (usedWeight * 1024) / memTotalTime;
    381             double freeRam = (freeWeight * 1024) / memTotalTime;
    382             totalRam = usedRam + freeRam;
    383             totalScale = realTotalRam / totalRam;
    384             weightToRam = totalScale / memTotalTime * 1024;
    385 
    386             realUsedRam = usedRam * totalScale;
    387             realFreeRam = freeRam * totalScale;
    388             if (DEBUG) {
    389                 Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(context,
    390                         (long) realUsedRam));
    391                 Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(context,
    392                         (long) realFreeRam));
    393             }
    394             if (DEBUG) {
    395                 Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(context,
    396                         (long) realUsedRam));
    397                 Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(context,
    398                         (long) realFreeRam));
    399             }
    400 
    401             ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
    402             ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(
    403                     memInfo);
    404             if (memInfo.hiddenAppThreshold >= realFreeRam) {
    405                 realUsedRam = freeRam;
    406                 realFreeRam = 0;
    407                 baseCacheRam = (long) realFreeRam;
    408             } else {
    409                 realUsedRam += memInfo.hiddenAppThreshold;
    410                 realFreeRam -= memInfo.hiddenAppThreshold;
    411                 baseCacheRam = memInfo.hiddenAppThreshold;
    412             }
    413         }
    414 
    415         private void calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem,
    416                 long memTotalTime) {
    417             MemInfoReader memReader = new MemInfoReader();
    418             memReader.readMemInfo();
    419             realTotalRam = memReader.getTotalSize();
    420             freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight;
    421             usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight;
    422             if (!totalMem.hasSwappedOutPss) {
    423                 usedWeight += totalMem.sysMemZRamWeight;
    424             }
    425             for (int i = 0; i < ProcessStats.STATE_COUNT; i++) {
    426                 if (i == ProcessStats.STATE_SERVICE_RESTARTING) {
    427                     // These don't really run.
    428                     mMemStateWeights[i] = 0;
    429                 } else {
    430                     mMemStateWeights[i] = totalMem.processStateWeight[i];
    431                     if (i >= ProcessStats.STATE_HOME) {
    432                         freeWeight += totalMem.processStateWeight[i];
    433                     } else {
    434                         usedWeight += totalMem.processStateWeight[i];
    435                     }
    436                 }
    437             }
    438             if (DEBUG) {
    439                 Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(context,
    440                         (long) ((usedWeight * 1024) / memTotalTime)));
    441                 Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(context,
    442                         (long) ((freeWeight * 1024) / memTotalTime)));
    443                 Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(context,
    444                         (long) (((freeWeight + usedWeight) * 1024) / memTotalTime)));
    445             }
    446         }
    447     }
    448 
    449     final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
    450         @Override
    451         public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
    452             if (lhs.mRunWeight < rhs.mRunWeight) {
    453                 return 1;
    454             } else if (lhs.mRunWeight > rhs.mRunWeight) {
    455                 return -1;
    456             } else if (lhs.mRunDuration < rhs.mRunDuration) {
    457                 return 1;
    458             } else if (lhs.mRunDuration > rhs.mRunDuration) {
    459                 return -1;
    460             }
    461             return 0;
    462         }
    463     };
    464 }
    465