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