• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ProcStatsData(Context context, boolean useXfer)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 
setTotalTime(int totalTime)88     public void setTotalTime(int totalTime) {
89         memTotalTime = totalTime;
90     }
91 
xferStats()92     public void xferStats() {
93         sStatsXfer = mStats;
94     }
95 
setMemStates(int[] memStates)96     public void setMemStates(int[] memStates) {
97         mMemStates = memStates;
98         refreshStats(false);
99     }
100 
setStats(int[] stats)101     public void setStats(int[] stats) {
102         this.mStates = stats;
103         refreshStats(false);
104     }
105 
getMemState()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 
getMemInfo()117     public MemInfo getMemInfo() {
118         return mMemInfo;
119     }
120 
getElapsedTime()121     public long getElapsedTime() {
122         return mStats.mTimePeriodEndRealtime - mStats.mTimePeriodStartRealtime;
123     }
124 
setDuration(long duration)125     public void setDuration(long duration) {
126         if (duration != mDuration) {
127             mDuration = duration;
128             refreshStats(true);
129         }
130     }
131 
getDuration()132     public long getDuration() {
133         return mDuration;
134     }
135 
getEntries()136     public List<ProcStatsPackageEntry> getEntries() {
137         return pkgEntries;
138     }
139 
refreshStats(boolean forceLoad)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 
createPkgMap(ArrayList<ProcStatsEntry> procEntries, ProcessDataCollection bgTotals, ProcessDataCollection runTotals)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 
distributeZRam(double zramWeight)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 
createOsEntry(ProcessDataCollection bgTotals, ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam)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 
getProcs(ProcessDataCollection bgTotals, ProcessDataCollection runTotals)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 
load()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 
getWeightToRam()376         public double getWeightToRam() {
377             return weightToRam;
378         }
379 
MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem, long memTotalTime)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 
calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem, long memTotalTime)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