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