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