Cancel indefinite vibration in Android
Does your application start indefinite vibrations that you stop some time later, e.g. in a BroadcastReceiver
? Have you received bug reports that your application won’t stop vibrating? Me too, and here is my solution.
TL;DR: Always use the application’s Context
to start and stop indefinite vibrations.
The other day, I received a complaint that my Android application does not stop vibrating, sometimes. It is an indefinite vibration, which is started by a system notification and should be stopped by another system notification. All this was happening in a BroadcastReceiver
, where I obtain the Vibrator
like always1:
val vibrator = context.getSystemService(Vibrator::class.java)
Depending on the BroadcastReceiver
’s action, I either started an indefinite vibration or stopped all vibrations. There is no special way to stop vibrations; there is only one API call:
vibrator.cancel()
No parameters, nothing, and the documentations just says “Turn the vibrator off”.
This code was working fine for 10 years and started misbehaving on some Android 11 devices.
All of my devices were behaving correctly, even Android 12 and 13 devices, so the worst has happened. I bought that specific device2.
Nothing I tried was working, Internet searches didn’t show others having a similar problem, so I have asked the free ChatGPT, just for fun ¯\(ツ)/¯ , and after a short conversation it said:
Make sure that you’re calling
vibrator.cancel()
from the appropriate context, such as the same context from which you initiated the vibration.
Yes, my contexts were different because they were from different BroadcastReceiver
s. The only option was to try the applicationContext
, and that solved all the problems. Indeterminate vibrations are cancelled even on misbehaving Android 11 and newer devices and, my users are happy again.
Kotlin extension
Here is my Kotlin extension for the Context
to always get a “good” Vibrator
:
fun Context.getAppVibrator(): Vibrator =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
applicationContext.getSystemService(VibratorManager::class.java).defaultVibrator
} else {
applicationContext.getSystemService(Vibrator::class.java)
}
Android documentation
This is not a documented behaviour of the Vibrator
, and this code was working at least since Android 4.4 without problems on all devices, even on ones by manufacturers that heavily modify Android. To defend the Android documentation a bit, the documentation of Context.getSystemService(…)
function call has a note saying:
System services obtained via this API may be closely associated with the Context in which they are obtained from. In general, do not share the service objects between various different contexts (Activities, Applications, Services, Providers, etc.)
So, always use the applicationContext
.