fireborn

Have You Tried Turning TalkBack Off and On Again

A few days ago my phone stopped letting me read it by touch.

Not the screen reader. The phone. I could still swipe through items, double-tap to activate them, and use every two-, three- and four-finger gesture, but the moment I put one finger on the glass and dragged it across the screen to find something, which is the most basic thing a blind person does with a touch screen, nothing happened. No focus moved. Nothing was spoken. No tick as my finger crossed from one item to the next. The screen reader's event stream showed a TOUCH_INTERACTION_START when my finger landed and a TOUCH_INTERACTION_END when it lifted, and between those two events, where there should have been a stream of hover events telling it what was under my finger, there was nothing.

If you've never used a touch screen reader, it's worth explaining why that matters, because sighted people tend to assume swiping is how we use our phones and touch is an extra. It isn't. Swiping moves you through everything on the screen one item at a time, in whatever order the app's developer built it, which on a busy screen can mean forty or fifty swipes to get from the top to the thing you want at the bottom. Touch exploration is how you learn where things are. It's how you find the send button without swiping through the whole conversation, how you work out the layout of an app you've never used, how you jump to the bottom navigation bar, how you read a screen spatially the way a sighted person glances at it. Take it away and you haven't lost a feature. You've lost the map.

I restarted the screen reader. Nothing. I stopped it and started it again, several times. Nothing. I switched to a different build. Nothing. I turned accessibility off at the settings level, waited, and turned it back on. Nothing. Rebooting the phone fixed it, and some hours later it happened again.

Most people in that position would be stuck, because nobody gives blind users the tools to see what their own device is doing. I'm a developer. I work on Backtalk, the open source TalkBack fork a few of us blind developers have been building to clean up the years of rot Google has left in its own screen reader, and I know my way around adb, logcat and dumpsys. So I went digging, and I found it.

What's broken

To understand the bug, you need a couple of minutes of how touch exploration works under the hood.

When a screen reader like TalkBack or Backtalk turns on Explore by Touch, the system stops sending your touches directly to apps. Everything goes through a piece of the Android framework called TouchExplorer, which lives inside system_server, the central process that runs most of the operating system. TouchExplorer watches your fingers and decides what you're doing. If you swipe, it hands the gesture to the screen reader. If you double-tap, it tells the screen reader to activate the focused item. If you drag with two fingers, and the screen reader has asked for two-finger passthrough, it passes the drag through to the app so you can scroll. If you put one finger down and move it, it converts that into hover events, a HOVER_ENTER, a stream of HOVER_MOVEs and a HOVER_EXIT, and injects them back into the system, and the screen reader uses those hovers to work out what's under your finger and speak it.

Injected events don't come from the real touchscreen. They come from a virtual device, which on my phone has the id -1. The real touchscreen is device 9. Every event TouchExplorer injects is checked by the input dispatcher, the part of system_server that decides where every touch on the device goes. The dispatcher tracks, per device, which pointers are down and which are hovering, so that it can reject event streams that don't make sense. That's reasonable. You don't want apps receiving a finger that lifts before it touched down.

Here's what dumpsys input showed after the failure:

Touching pointer IDs by device: {DeviceId(-1): {3}}
Hovering pointer IDs by device: {}
Down times by device: {DeviceId(-1): 35493713000000, DeviceId(9): 25534642174000}
TouchStatesByDisplay: <no displays touched>

The first line says the dispatcher believed pointer id 3, on the injection device, was touching the screen. Not a real finger; a fake one that TouchExplorer had put down on my behalf. It went down at 35,493.7 seconds of uptime and never came back up. I checked that dump repeatedly over more than fifty minutes, with no fingers near the screen, and it never changed: same pointer, same device, same down time. The last line, TouchStatesByDisplay: <no displays touched>, is the dispatcher's other bookkeeping agreeing that nothing was touching the display. The two sets of state disagreed, and the one that governed injection was wrong.

A hover can't happen while a pointer is down. You're either touching or hovering. So every time TouchExplorer tried to inject a hover for my exploring finger, the dispatcher looked at device -1, saw a pointer it thought was still pressed, and threw the hover away:

E InputDispatcher: Inconsistent event: MotionEvent { action=HOVER_ENTER, id[0]=0, x[0]=351.738, y[0]=1747.88, eventTime=37892674107000ns, downTime=37876906000000ns, deviceId=-1, source=TOUCHSCREEN, displayId=0, eventId=0xdae08c87}, reason: Injection on 0: Invalid HOVER_ENTER event - pointers are down for device DeviceId(-1): {DeviceId(-1): {3}}

HOVER_ENTER, rejected because pointers are down. HOVER_MOVE, rejected because pointers are down. Then HOVER_EXIT, rejected for the opposite reason, because no pointers were hovering, which they weren't, because the enter had been rejected. The timestamps on that line matter too: the stuck pointer went down at 35,493.7 seconds, and this hover was trying to get through at 37,892.7. Forty minutes later, the dispatcher was still holding on to that finger.

I counted. My first capture had 1,371 of these rejections: 16 HOVER_ENTERs, 16 HOVER_EXITs and the rest HOVER_MOVEs, which is sixteen separate attempts to explore the screen, each thrown away in full. The second capture had 1,425. A third, taken after the log buffer had rolled over and lost the earlier lines, had another 578. Every one of those lines is me dragging a finger across my own phone, trying to read it, and the operating system deciding I wasn't allowed to.

How it probably got that way

The log has a strong candidate for how the pointer got stuck.

At 18:54:51 there was a single messy multi-finger touch. It started with two fingers, grew to three, four, then five, with fingers added and lifted in no particular order, ACTION_POINTER_DOWN(1), ACTION_POINTER_DOWN(4), ACTION_POINTER_UP(1), ACTION_POINTER_UP(3) and so on, before everything lifted. That's not a deliberate gesture. That's what happens on a big foldable screen when a palm brushes the edge, you shift your grip, or a stray finger lands while you're holding the phone open in one hand. On a Fold, with that much glass, it happens constantly.

For roughly 47 events during that touch, TouchExplorer logged the same error:

E TouchExplorer: mDraggingPointerId doesn't match any pointers on current event. mDraggingPointerId: -1, Event: MotionEvent { action=ACTION_POINTER_UP(3), ... pointerCount=4 ... deviceId=9 ...

That's TouchExplorer saying it was in its dragging state, the state it uses when passing a drag through to an app, but that the pointer it thought it was dragging had an id of -1, which isn't a pointer. It's the "nothing" value. The same error came back in bursts afterwards: 148 events at 19:06:45, more at 19:07:51 and 19:08:51, and at 19:34:42 to 19:34:44 for plain single-finger touches, ending in an ordinary ACTION_UP with the same complaint.

My reading is that TouchExplorer entered the dragging state without a valid pointer to drag, injected a DOWN for one of the extra fingers, and then, when the UP events came, took the early return in the branch that logs "doesn't match any pointers" instead of injecting the matching UP or CANCEL. The dispatcher saw the DOWN, never saw the UP, and held that fake finger down from then on. I said in the report that this was my reading of the log, that I didn't have the framework source for this build to confirm it, and that I hadn't been able to reproduce it on demand. I try not to claim more than I can prove, and that matters most when you're the kind of person a support team might be inclined to dismiss.

Why only a reboot fixes it

This is what turns an irritating bug into a serious one: no app can clear it, and I tried everything an app can do.

Restarting the screen reader doesn't touch it, because the state isn't in the screen reader. It's in system_server. The screen reader process can die and come back as often as you like; the dispatcher doesn't care.

Turning accessibility off doesn't touch it. I set accessibility_enabled to 0 and left it there for four seconds. TouchExplorer stayed in the list of enabled input features on both displays, and pointer 3 stayed stuck.

Injecting a gesture to lift the stuck finger doesn't touch it. I used AccessibilityService.dispatchGesture, the supported way for an accessibility service to perform touches, to inject a gesture with four simultaneous strokes covering pointer ids 0 to 3, so that if the system was going to match anything up, it had every chance. I tried it for 80 milliseconds and for four seconds. Both times the callback returned onCompleted, the system's way of saying the gesture went through, and both times dumpsys input showed the same stuck pointer with the same down time. During the four-second gesture, the dispatcher's verifier showed only the stuck pointer, not mine, which suggests that gestures dispatched this way don't pass through the same state at all, or go out on a different device id. Either way, they can't reach it.

There is no supported call that tells the system to forget injected input state. The screen reader can't fix it. The user can't fix it from settings. Only a reboot clears it, by throwing away all of system_server and starting again, and then you're one stray palm away from it happening again.

It isn't a Backtalk bug. It lives below every screen reader on the device. I've seen it with TalkBack 16, 16.1, 16.2 and 17.0, on Android 15, 16 and 17, and other screen reader users have described the same symptom to me: swipes fine, touch dead, reboot fixes it. Anything that turns on Explore by Touch is exposed to it, which on Android means nearly every blind user who relies on a screen reader.

The report

So I wrote it up properly.

The subject line told you what was broken and where: touch exploration stops working system-wide because the input dispatcher keeps an injected pointer, device -1, id 3, down after a multi-finger touch, and rejects all later hover events. Then a summary. Then an impact section explaining in plain language that a blind user who can't explore by touch has lost the main way they read a screen, until they reboot.

Then the device and software details: Samsung Galaxy Z Fold7, model SM-F966B, Android 17, SDK 37, One UI 9.0, the full build number, the September security patch. The screen reader, which service, what it was based on, and the exact flags it registered with: requestMultiFingerGestures=true, requestTwoFingerPassthrough=true, serviceHandlesDoubleTap=true, touchExplorationEnabled=true. Confirmation that magnification was off, so nobody would waste time blaming the magnification gesture handler. The full list of enabled input features on the display. Confirmation that only one accessibility service was bound, so nobody would waste time blaming a second service fighting the first.

Then the symptom. Then the dumpsys input output. Then the rejected events with their counts and reasons. Then the TouchExplorer errors around the likely trigger, with timestamps, and every later burst. Then my analysis, labelled as analysis. Then a section headed "What I tried, and what did not clear it", covering restarting the screen reader, switching builds, the four-stroke gestures, disabling accessibility, and the reboot. Then reproduction notes, honest that I couldn't trigger it on demand, with an offer to do a controlled reproduction if they told me what to capture. Then expected behaviour, actual behaviour, and three requests:

  1. Confirm whether this is the known AOSP TouchExplorer dragging-state issue and which fix applies to Android 17.
  2. Release injected pointers when a touch sequence ends in that "doesn't match any pointers" branch, and when the service that asked for touch exploration disconnects.
  3. If there's an app-side workaround, a supported call that clears injected-device state, name it.

And a list of what I could send them: three filtered log captures, the input and accessibility dumps from the broken state, and the build properties. I filtered the logs to TouchExplorer, InputDispatcher and AccessibilityManagerService lines and said why: the unfiltered logs contain my notification text and app activity, and I'm not sending a stranger my private messages to prove a point about hover events.

Notice I said "could send", not "sent". Google's disability support form has an attachment field, and that field will happily let you pick a text file, or a zip of text files. It shows them as attached. Then it refuses to submit. The only attachments it accepts are screenshots. So the evidence for a bug in the input system had to stay on my laptop, and I offered in the report to send the logs any other way they liked. A disability support form, used heavily by blind people, that will take a picture of a log but not the log itself, is its own small monument to how much thought goes into this channel.

It's the report I'd want to receive if I were the engineer who had to fix this. It names the component, the state, the probable code path and the evidence. It lets an engineer go straight to the right file. I sent it to Google Disability Support, because that's the channel Google tells disabled people to use for this, and I waited.

The first reply

The first reply arrived at a quarter past two in the morning, UK time. An introduction, a note that the agent would be handling my case, and a one-paragraph summary of my report that was accurate: touch exploration stops after a multi-finger touch, the screen reader gets no finger movement, no focus or speech, until a reboot. Then a promise to help "once I have a better understanding of the issue", and nine questions.

Here they are, against what the report already said.

Does it happen with official TalkBack, or only Backtalk? The report's second paragraph says the problem isn't specific to Backtalk and that other screen reader users see it with TalkBack.

Am I the developer of Backtalk, or a user reporting it? The same paragraph says "I am a screen reader user and the developer of Backtalk."

Which other screen readers are affected? Partly fair. The report says other screen readers too, without naming them. It also explains that the bug is in the system, below the screen reader, which is what makes the answer "all of them".

What version of Backtalk? Fair. I'd said it was based on TalkBack 16.2, but hadn't given a build number.

Does it only happen after a touch with more than three fingers, or other gestures too? The report has a section on the probable trigger, including the single-finger bursts at 19:34, and says I haven't been able to reproduce it on demand.

Does touch exploration fail on every screen and app, while swipes, taps and multi-finger gestures keep working? That's the Symptom section, nearly word for word, and the subject line says "system-wide".

Have I seen it on other devices or Android versions? Fair. I hadn't said.

Is a reboot consistently the only fix? The report says so in the first paragraph, in the "What I tried" section, and under Actual.

Please include any additional details, including the troubleshooting steps you've already performed. In reply to a report with a section titled "What I tried, and what did not clear it".

Two of the nine were reasonable and a third was arguable. I hadn't given a Backtalk build number or said whether I'd seen it on other devices or Android versions. If the reply had been those questions, I'd have answered in thirty seconds and been pleased someone was paying attention. Instead they were buried among six others the report had already answered, some more than once. Whoever handled this had a template of questions for this kind of case, asked all of them, and didn't check them against what I'd sent.

What's missing from those nine questions says as much. Nobody asked for a full adb bugreport, the first thing an Android engineer would want, and something I'd said I hadn't taken. Nobody took up the offer of a controlled reproduction. Nobody asked whether I'd seen it on a Pixel, which would separate a Samsung problem from an AOSP one. Nobody took me up on the offer of the logs, or told me another way to get them to anyone. Nobody asked a single question about the log excerpts that were in the report itself. The questions were about whether I was who I said I was and whether I'd done the basics, not about the bug.

I answered anyway, at half eight that morning, and kept it short. Every build of Backtalk, currently 318. TalkBack 17.0, 16.2, 16.1 and 16. Android 17, 16 and 15. Any screen reader that requests Explore by Touch. Every screen, every app. Reboot is always the only fix. I'm reporting it as both the developer and a user. The exact trigger is hard to pin down because it seems to happen at random. I'd restarted the screen reader, restarted touch exploration and tried to release the stuck accessibility pointer, and none of it worked; only a full reboot restores touch exploration.

The second reply

The second reply came at a quarter to three the following morning, and it's the one that made me open a new file and start writing this.

It thanked me for the additional details and summarised them back correctly: it happens across multiple screen readers and Android versions, it affects every screen and app, and only rebooting resolves it. It said it appreciated the troubleshooting I'd already done as both a developer and a user. Then, having written in its own words that I'd done the troubleshooting and that only a reboot fixes it, it asked me to try four things.

First, turn TalkBack off and back on again. Six numbered steps, written for a screen reader user: open Settings, swipe right or left until you hear Accessibility, double-tap, swipe until you hear TalkBack, double-tap, find the switch, turn it off, wait about ten seconds, turn it back on. With a note that this "refreshes the TalkBack service and may resolve temporary issues". And prefaced with a warning that I should only try it if I had a TalkBack shortcut enabled or someone sighted available to help. Sent to the person who had just explained how TouchExplorer injects hover events on device -1 and why the input dispatcher rejects them. Sent to an assistive technology trainer who teaches other people to set up that shortcut. Sent in reply to two messages that both said restarting the screen reader does nothing. That was the whole fucking point: the bug lives below the screen reader, in system_server, which is why restarting the screen reader can't fix it. I'm not even running TalkBack as my daily screen reader. Turning off a screen reader I'm not using, to fix a bug I'd shown doesn't live in the screen reader, is like rebooting the router to fix a flat tyre.

Second, check the Play Store for an Android Accessibility Suite update. Four steps: open the Play Store, search for it, select it, tap Update if one's available. The bug is in system_server, which is part of the operating system and is updated with system updates, not from the Play Store. Android Accessibility Suite is an app, the package TalkBack ships in. I'm running a different screen reader, which I said in the first line of the device section of the report. And even if I weren't, updating the app that receives the hover events would do nothing about the system component throwing them away before they arrive.

Third, clear Android Accessibility Suite's cache. Seven steps, with a note explaining that clearing the cache won't affect my settings but clearing data would, so please only clear the cache. Settings, Apps, See all apps, find it, Storage and cache, Clear cache, test touch exploration. A stuck injected pointer inside the input dispatcher's verifier is not stored in the cache directory of a Play Store app. The cache is temporary files. The stuck pointer is a number in the memory of a different process.

Fourth, restart the device and check whether touch exploration continues to work normally. It does, until it doesn't. I said in the report that a reboot is the only recovery. I didn't say a reboot cures it. A workaround I'd already reported is not a new fix for me to test, and offering it as one is either not reading or not caring. I can't tell which is worse.

After all that came the sentence that matters: if the issue continues after these steps, please let us know the results, "so we can forward this to our internal team for further review."

That's the deal. A report with log excerpts, dumpsys output, an offer of the full captures, a probable code path, a list of everything already ruled out, and three engineering questions doesn't go to an engineer. It's held at the door until I've performed a ritual of steps the report had already explained can't work, so that a box can be ticked saying the user completed the troubleshooting script. Only then, maybe, does it move.

The reply then asked three more questions. How does the touch exploration issue affect my ability to navigate my device and use apps? Are there specific tasks I can't complete when it happens? Are there other details I'd like to share? The report has a section called Impact. It's the second section. It says that a blind user who can't explore by touch has lost the main way a touch screen reader user reads a screen, and that the phone stays like that until a reboot. As for other details, I'd already shared the dispatcher's internal state.

Finally, the reply mentioned that I might receive a survey in a day or two about my support experience, and that they'd love to hear from me.

You will, Google. You will.

None of my three requests was addressed. Not answered, not acknowledged, not deflected, not "we can't comment on unreleased fixes", not even "this is a Samsung build, so you may need to report it to Samsung", which would have been an answer, and on a Samsung phone might have been a reasonable one. The questions weren't there. Across two replies and two days, nobody in that process engaged with a single technical sentence I wrote.

What good support would have looked like

I didn't expect a fix by return of email. I didn't expect the agent to read dumpsys output, know what an injection device is, or diagnose anything. Front-line support isn't engineering and shouldn't pretend to be.

I expected triage. Someone to look at a report that names a framework component, quotes system dumps and asks engineering questions, and recognise it as a report that needs to go to an engineer. Give me a way to send the logs their own form wouldn't accept. Ask for a full bug report, since I'd said I didn't have one. Ask me to confirm it on a Pixel if they wanted to know whether it was Google's problem or Samsung's. Take up the controlled reproduction I'd offered. Pass it on, give me a reference, and tell me what happens next. For a case like this, that's the whole job: recognise it, route it, and don't make the reporter jump through hoops the report already explains are pointless.

Instead I got a process that treated a detailed technical report the same as "my TalkBack stopped talking", because the process has no way to tell the difference, or doesn't care to.

This isn't about the agent

I'm not naming the person who handled my case, and here's why. I don't believe the person on the other end of these emails is stupid, lazy or malicious. Both replies were polite. Both summaries of what I'd said were accurate. The steps were written for a screen reader user, with "swipe right or left until you hear" instead of "tap", which is more than a lot of companies manage. That person is doing what the system they work in asks of them: match the case to a category, send that category's questions, send that category's troubleshooting script, collect the impact statement, and only escalate when the script has been exhausted.

Front-line support at a company the size of Google is measured on throughput, handling time and the survey, not on whether a framework bug ends up in front of someone who can fix it. The people doing that work are rarely given the time, the training or the authority to look at a log and say "this one's real, it needs to go upstairs now". They're given a script and judged on how well they follow it. My contempt here is for that system and the company that built it, not for the person stuck operating it.

But that system is the problem, Google built it, and Google keeps it like this on purpose.

A pattern, not an incident

Google Disability Support is the channel Google points disabled people towards. It's presented as the accessible, human, specialist route for people like me, the place to go when the product doesn't work for us. In practice, it's a moat.

This isn't my first time. For years I've reported TalkBack bugs through it. Focus skipping items while scrolling through lists. Scrolling being painfully slow. The braille keyboard not working properly in tabletop orientation, with the phone flat on a table. Braille keyboard echo being chained to the on-screen keyboard's echo setting, so you can't have one without the other. For years I've been told those bugs don't exist, or that the behaviour is correct, or that what I'm asking for can't be done. Some of them I've since fixed myself in Backtalk. The braille keyboard now handles tabletop orientation. Braille keyboard echo now has its own setting. So much for "can't be done", and it raises the question of why a handful of blind volunteers can fix things the company that owns the code says are impossible.

Every one of those conversations had the same shape as this one. A script. A set of questions. A polite refusal to engage with the substance. A survey.

This time I didn't send a vague complaint. I didn't say "TalkBack is buggy, please fix". I sent the dispatcher state, the rejection reasons, the pointer id, the device id, the down time, the error messages, the timestamps, the probable code path and the list of everything I'd ruled out. I did the work an engineer would otherwise have to do, for free, on my own time, on a phone I could only half use. If a report like that can't get past the first line of Google's disability support without me being told to clear the cache of an app I'm not using, no report can. That's not a support channel. That's where bug reports from blind people go to be politely absorbed.

Why it matters

I'm a blind developer with a laptop, adb, a working knowledge of the Android input pipeline and an unreasonable amount of stubbornness. I can find the stuck pointer. I can explain it. I can write it up. I can, if I have to, live with rebooting my phone a couple of times a day while this sits unfixed.

Most blind Android users can't do any of that, and they shouldn't have to. Most will never open a terminal, never see a dumpsys dump, and never know a pointer is stuck. One day they'll put a palm on a big screen, or shift their grip on a foldable, and lose the ability to explore by touch. They'll assume it's their fault, or their screen reader's. They'll turn TalkBack off and on again, as Google tells them to, and it won't work. If they're lucky, someone will tell them to reboot, and it'll come back, and then it'll go again, and they'll never know why. If they're not, they'll be stuck with a phone they can only swipe through, item by item, with no idea where anything is.

And when they go to Google Disability Support, they'll be asked how the issue affects their ability to use their device, they'll be told to clear a cache, and they'll get a survey.

That's who this channel is supposed to be for.

I'll send them the results of the ritual, because apparently that's the toll. Turned TalkBack off and on: no change, as stated twice already, and I don't use TalkBack day to day. Checked for an Accessibility Suite update: irrelevant, the bug isn't in it. Cleared its cache: no change, it isn't involved. Rebooted: fixed, temporarily, as stated twice already. Then we'll see whether "forward this to our internal team" means anything, or whether it's the next box on the form.

In the meantime, if you're a blind Android user and touch exploration dies on you while swipes and taps keep working, it's not you, and it's not your screen reader. Reboot. Then tell Google anyway, through whatever channel they'll let you use, because the only thing that seems to move a company this big is volume, and I'd like them to have to read about this bug a few hundred more times.