• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package leakcanary
2 
3 import android.annotation.SuppressLint
4 import android.app.Service
5 import android.os.Build
6 import android.os.Handler
7 import android.os.IBinder
8 import leakcanary.internal.friendly.checkMainThread
9 import shark.SharkLog
10 import java.lang.ref.WeakReference
11 import java.lang.reflect.InvocationTargetException
12 import java.lang.reflect.Proxy
13 import java.util.WeakHashMap
14 
15 /**
16  * Expects services to become weakly reachable soon after they receive the [Service.onDestroy]
17  * callback.
18  */
19 @SuppressLint("PrivateApi")
20 class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
21 
22   private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
23 
24   private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }
25 
26   private val activityThreadInstance by lazy {
27     activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
28   }
29 
30   private val activityThreadServices by lazy {
31     val mServicesField =
32       activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }
33 
34     @Suppress("UNCHECKED_CAST")
35     mServicesField[activityThreadInstance] as Map<IBinder, Service>
36   }
37 
38   private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
39   private var uninstallActivityManager: (() -> Unit)? = null
40 
41   override fun install() {
42     checkMainThread()
43     check(uninstallActivityThreadHandlerCallback == null) {
44       "ServiceWatcher already installed"
45     }
46     check(uninstallActivityManager == null) {
47       "ServiceWatcher already installed"
48     }
49     try {
50       swapActivityThreadHandlerCallback { mCallback ->
51         uninstallActivityThreadHandlerCallback = {
52           swapActivityThreadHandlerCallback {
53             mCallback
54           }
55         }
56         Handler.Callback { msg ->
57           // https://github.com/square/leakcanary/issues/2114
58           // On some Motorola devices (Moto E5 and G6), the msg.obj returns an ActivityClientRecord
59           // instead of an IBinder. This crashes on a ClassCastException. Adding a type check
60           // here to prevent the crash.
61           if (msg.obj !is IBinder) {
62             return@Callback false
63           }
64 
65           if (msg.what == STOP_SERVICE) {
66             val key = msg.obj as IBinder
67             activityThreadServices[key]?.let {
68               onServicePreDestroy(key, it)
69             }
70           }
71           mCallback?.handleMessage(msg) ?: false
72         }
73       }
74       swapActivityManager { activityManagerInterface, activityManagerInstance ->
75         uninstallActivityManager = {
76           swapActivityManager { _, _ ->
77             activityManagerInstance
78           }
79         }
80         Proxy.newProxyInstance(
81           activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
82         ) { _, method, args ->
83           if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
84             val token = args!![0] as IBinder
85             if (servicesToBeDestroyed.containsKey(token)) {
86               onServiceDestroyed(token)
87             }
88           }
89           try {
90             if (args == null) {
91               method.invoke(activityManagerInstance)
92             } else {
93               method.invoke(activityManagerInstance, *args)
94             }
95           } catch (invocationException: InvocationTargetException) {
96             throw invocationException.targetException
97           }
98         }
99       }
100     } catch (ignored: Throwable) {
101       SharkLog.d(ignored) { "Could not watch destroyed services" }
102     }
103   }
104 
105   override fun uninstall() {
106     checkMainThread()
107     uninstallActivityManager?.invoke()
108     uninstallActivityThreadHandlerCallback?.invoke()
109     uninstallActivityManager = null
110     uninstallActivityThreadHandlerCallback = null
111   }
112 
113   private fun onServicePreDestroy(
114     token: IBinder,
115     service: Service
116   ) {
117     servicesToBeDestroyed[token] = WeakReference(service)
118   }
119 
120   private fun onServiceDestroyed(token: IBinder) {
121     servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
122       serviceWeakReference.get()?.let { service ->
123         reachabilityWatcher.expectWeaklyReachable(
124           service, "${service::class.java.name} received Service#onDestroy() callback"
125         )
126       }
127     }
128   }
129 
130   private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {
131     val mHField =
132       activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
133     val mH = mHField[activityThreadInstance] as Handler
134 
135     val mCallbackField =
136       Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
137     val mCallback = mCallbackField[mH] as Handler.Callback?
138     mCallbackField[mH] = swap(mCallback)
139   }
140 
141   @SuppressLint("PrivateApi")
142   private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
143     val singletonClass = Class.forName("android.util.Singleton")
144     val mInstanceField =
145       singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }
146 
147     val singletonGetMethod = singletonClass.getDeclaredMethod("get")
148 
149     val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
150       "android.app.ActivityManager" to "IActivityManagerSingleton"
151     } else {
152       "android.app.ActivityManagerNative" to "gDefault"
153     }
154 
155     val activityManagerClass = Class.forName(className)
156     val activityManagerSingletonField =
157       activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
158     val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]
159 
160     // Calling get() instead of reading from the field directly to ensure the singleton is
161     // created.
162     val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)
163 
164     val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
165     mInstanceField[activityManagerSingletonInstance] =
166       swap(iActivityManagerInterface, activityManagerInstance!!)
167   }
168 
169   companion object {
170     private const val STOP_SERVICE = 116
171 
172     private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
173   }
174 }