<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 }