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