-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Watch destroyed services #2014
Watch destroyed services #2014
Conversation
On a second thought, this approach is imperfect, both of them happen before |
b9784f8
to
bd06346
Compare
This is really cool! Is this hitting any API grey list / black list? |
Yes, all of them are in greylist. From the android-11-lists: Landroid/app/ActivityManager;->IActivityManagerSingleton:Landroid/util/Singleton;
Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread;
Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
Landroid/app/ActivityThread;->mH:Landroid/app/ActivityThread$H;
Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
Landroid/util/Singleton;->mInstance:Ljava/lang/Object; But Landroid/view/WindowManagerGlobal;->getInstance()Landroid/view/WindowManagerGlobal;
Landroid/view/WindowManagerGlobal;->mViews:Ljava/util/ArrayList; Should I provide a config to disable the watcher like |
Depends on whether this has a chance of breaking apps. The RootView hack installs a different array list instance so quite risky. |
Okay, the destroyed service hack has same risk. |
bd06346
to
713856e
Compare
The new approach:
Due to the risk in this approach, now the <?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="leak_canary_watcher_install_service_destroy">false</bool>
</resources> |
Thanks! Sorry for the slow review, this is really cool but I need time to dig into it |
Going through the code on cs.android.com Another thing that's called is ActivityThread.removeContextRegistrations() which removes an entry from ActivityThread.receivers and ActivityThread.unregisteredReceivers which are ArrayMap instances (interestingly only |
private val configProvider: () -> AppWatcher.Config | ||
) { | ||
|
||
private val mTmpServices = mutableMapOf<IBinder, Service>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We likely want a mutableMapOf<IBinder, WeakReference> to not end up being the cause of a service leak, and maybe even a WeakHashMap<IBinder, WeakReference> (ie weak key as well because idk if we should risk retaining the IBinder either)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Followed up here: #2029
serviceDestroyWatcher.onServicePreDestroy(key, it) | ||
} | ||
} | ||
mCallback?.handleMessage(msg) ?: false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah ok this is cool. Replace the handler callback and delegate to it, which ensures that this stays compatible with any other code running the same hack.
Note: merged main to fix conflicts. |
We can handle the |
Does this mean we could use this to detect leaking receivers? I'm not sure what defines a leak for a receiver exactly. After deregistration? |
In my opinion, a leaked receiver means that its associated context has been destroyed (such as destroyed Activity/Service) and it has been unregistered.
In source of On second thought, it is difficult to cover all scenarios. Because there is no way to detect those receivers registered with application context are leaked or not. |
This may help detect leaks of services.
Before API 19,
ActivityThread.mServices
is aHashMap
. TheActivityThread.handleStopService
method will remove the service instance stored inmServices
, and thenService.onDestroy
method will be called. So, we can set a newHashMap
and override theremove
method to listenService.onDestroy
. (Yes, inspired by #2007)Start from API 19,
ActivityThread.mServices
is anArrayMap
, andArrayMap
is a final class. We only can useActivityThread.H.STOP_SERVICE
to listenService.onDestroy
.Test on API 16:
Test on API 29: