<lambda>null1package leakcanary 2 3 import android.annotation.SuppressLint 4 import android.app.ActivityManager 5 import android.app.ActivityManager.MemoryInfo 6 import android.app.ActivityManager.RunningAppProcessInfo 7 import android.content.Context 8 import android.os.Build.VERSION.SDK_INT 9 import android.os.Process 10 import android.os.SystemClock 11 import android.system.Os 12 import android.system.OsConstants 13 import java.io.File 14 import java.io.FileReader 15 import leakcanary.ProcessInfo.AvailableRam.BelowThreshold 16 import leakcanary.ProcessInfo.AvailableRam.LowRamDevice 17 import leakcanary.ProcessInfo.AvailableRam.Memory 18 19 interface ProcessInfo { 20 21 val isImportanceBackground: Boolean 22 23 val elapsedMillisSinceStart: Long 24 25 fun availableDiskSpaceBytes(path: File): Long 26 27 sealed class AvailableRam { 28 object LowRamDevice : AvailableRam() 29 object BelowThreshold : AvailableRam() 30 class Memory(val bytes: Long) : AvailableRam() 31 } 32 33 fun availableRam(context: Context): AvailableRam 34 35 @SuppressLint("NewApi") 36 object Real : ProcessInfo { 37 private val memoryOutState = RunningAppProcessInfo() 38 private val memoryInfo = MemoryInfo() 39 40 private val processStartUptimeMillis by lazy { 41 Process.getStartUptimeMillis() 42 } 43 44 private val processForkRealtimeMillis by lazy { 45 readProcessForkRealtimeMillis() 46 } 47 48 override val isImportanceBackground: Boolean 49 get() { 50 ActivityManager.getMyMemoryState(memoryOutState) 51 return memoryOutState.importance >= RunningAppProcessInfo.IMPORTANCE_BACKGROUND 52 } 53 54 override val elapsedMillisSinceStart: Long 55 get() = if (SDK_INT >= 24) { 56 SystemClock.uptimeMillis() - processStartUptimeMillis 57 } else { 58 SystemClock.elapsedRealtime() - processForkRealtimeMillis 59 } 60 61 @SuppressLint("UsableSpace") 62 override fun availableDiskSpaceBytes(path: File) = path.usableSpace 63 64 override fun availableRam(context: Context): AvailableRam { 65 val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 66 67 if (SDK_INT >= 19 && activityManager.isLowRamDevice) { 68 return LowRamDevice 69 } else { 70 activityManager.getMemoryInfo(memoryInfo) 71 72 return if (memoryInfo.lowMemory || memoryInfo.availMem <= memoryInfo.threshold) { 73 BelowThreshold 74 } else { 75 val systemAvailableMemory = memoryInfo.availMem - memoryInfo.threshold 76 77 val runtime = Runtime.getRuntime() 78 val appUsedMemory = runtime.totalMemory() - runtime.freeMemory() 79 val appAvailableMemory = runtime.maxMemory() - appUsedMemory 80 81 val availableMemory = systemAvailableMemory.coerceAtMost(appAvailableMemory) 82 Memory(availableMemory) 83 } 84 } 85 } 86 87 /** 88 * See https://dev.to/pyricau/android-vitals-when-did-my-app-start-24p4#process-fork-time 89 */ 90 private fun readProcessForkRealtimeMillis(): Long { 91 val myPid = Process.myPid() 92 val ticksAtProcessStart = readProcessStartTicks(myPid) 93 94 val ticksPerSecond = if (SDK_INT >= 21) { 95 Os.sysconf(OsConstants._SC_CLK_TCK) 96 } else { 97 val tckConstant = try { 98 Class.forName("android.system.OsConstants").getField("_SC_CLK_TCK").getInt(null) 99 } catch (e: ClassNotFoundException) { 100 Class.forName("libcore.io.OsConstants").getField("_SC_CLK_TCK").getInt(null) 101 } 102 val os = Class.forName("libcore.io.Libcore").getField("os").get(null)!! 103 os::class.java.getMethod("sysconf", Integer.TYPE).invoke(os, tckConstant) as Long 104 } 105 return ticksAtProcessStart * 1000 / ticksPerSecond 106 } 107 108 // Benchmarked (with Jetpack Benchmark) on Pixel 3 running 109 // Android 10. Median time: 0.13ms 110 private fun readProcessStartTicks(pid: Int): Long { 111 val path = "/proc/$pid/stat" 112 val stat = FileReader(path).buffered().use { reader -> 113 reader.readLine() 114 } 115 val fields = stat.substringAfter(") ") 116 .split(' ') 117 return fields[19].toLong() 118 } 119 } 120 } 121