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