HTC Desire Z: three black screens and a brightness of 30
Contents
I keep a ledger for nights like this one. The entry reads: three black screens in twenty-two minutes, three different exits, and a number attached to exactly one of them. The phone is an HTC Desire Z, the 2010 Android slider with a keyboard that I'm slimming down into a mesh-network node, running CyanogenMod 7, the community build of Android 2.3. I had just pulled most of its system apps and was writing my own home screen for it, and three times the screen came back lit and empty: backlight on, nothing drawn.
The number was a brightness of 30 out of 255, saved in Android's settings database, and the only thing I know of that writes a 30 is a button I had written that evening. It explains the second black screen. The first and the third ended without a cause I could name, and this post keeps all three, because the one with the number kept trying to take credit for the other two.
The debloat that booted fine#
The strip itself went well. 58 APKs moved in one pass from /system/app/ to /system/app-disabled/, out of about 80 the ROM ships (the list and what it cost later are in the debloat post), and the reboot after it came up clean. What it lacked was a home screen: ADWLauncher, CM7's launcher, had been on the list. ADWLauncher went back, and so did CMParts, CM7's settings app.
The next half hour went on a home screen. A search for a lighter launcher turned up nothing better than ADWLauncher, so I wrote MeshLauncher, a launcher of my own with three screens: stats on the left, the app grid in the middle, toggles on the right, brightness among them. The keyboard started crashing, so UserDictionaryProvider.apk went back as well, and the phone rebooted. That reboot is where the ledger starts, about half an hour after the strip.
Black screen 1: the lock screen nobody could see#
The system said a lock screen had focus. The phone showed a lit backlight and nothing else, and the power manager reported the screen on. I disabled the lock screen in the settings database and rebooted. Logcat, which still works over adb (Android's USB debug bridge), offered this:
E/libEGL ( 1336): eglSetSwapRectangleANDROID:1909 error 3008 (EGL_BAD_DISPLAY)
E/SurfaceFlinger( 1336): eglSwapBuffers: EGL error 0x3008 (EGL_BAD_DISPLAY)
eglSwapBuffers is the call that hands a finished frame on to the display, and here it failed with EGL_BAD_DISPLAY. SurfaceFlinger is Android's compositor, the process that assembles every app's window into the frame you see. A compositor that can't swap buffers draws nothing, and I had just moved most of the system apps out. It is the most quotable line in the log, which is how it got the job.
The next lines had a second suspect: WallpaperService failing on Unknown component for NexusWallpaper, a live wallpaper that lives in LiveWallpapers.apk, which was on my list. I deleted the wallpaper setting and rebooted. That boot logged this from the compositor:
I/SurfaceFlinger( 1336): Boot is finished (8903 ms)
The wallpaper failure was still in the log, so the wallpaper package went back:
adb shell mv /system/app-disabled/LiveWallpapers.apk /system/app/
adb reboot
The EGL lines printed again, and the lock screen had focus again, as invisible as before. I dumped the framebuffer, /dev/graphics/fb0, the memory the panel is drawn from, into a 3.1 MB file. I checked its size and never opened it. A file that big had to hold something, I decided, so the picture had to be there and the light had to be missing. 255 went into the backlight file in sysfs, 255 went into the saved screen_brightness setting, and the backlight file read back 255.
I looked at the phone. Nothing.
So brightness was cleared as a suspect, on evidence I had typed in myself. Next went the big hammer: every APK in /system/app-disabled/ moved back, and a reboot. This time a lock screen showed. I slid it open, got HTC's "Welcome to HTC Vision" setup wizard, switched the wizard off with pm disable, pressed Home, and MeshLauncher was on the screen. About six minutes, start to finish. Whether the restore, the reboot or my hand on the lock screen ended it, I never separated.
Black screen 2: brightness 30#
I tried the toggles screen, didn't like how its text moved when a toggle changed, and had it rebuilt as a grid of fixed tiles. Then the strip again, carefully this time: three batches, 9 Google packages, 12 CM extras and media apps, 14 more, 35 APKs in all, and one reboot to check.
Lock screen with focus, nothing on the glass. The EGL lines again. Everything back in place for the second time, a reboot, and still black, which at least took the 35 APKs off the list. By then I was calm in the specific way a person is calm when the undo button has stopped undoing anything. The display lines in that boot's log ended like this:
I/ActivityManager( 1336): Displayed com.android.setupwizard/.SetupWizardActivity: +4s99ms
I/SurfaceFlinger( 1336): Boot is finished (16487 ms)
The setup wizard had been displayed, according to the system, to nobody. So I wrote 255 to the backlight again and asked the database what brightness it had saved:
adb shell "echo 255 > /sys/class/leds/lcd-backlight/brightness"
adb shell "sqlite3 /data/data/com.android.providers.settings/databases/settings.db \"SELECT value FROM system WHERE name='screen_brightness';\""
30
Out of 255. Nothing I had typed that night wrote a 30. The brightness tile in MeshLauncher does:
private int[] levels = {30, 102, 200, 255};
public boolean getState() {
return Settings.System.getInt(context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, 102) > 150;
}
public void toggle() {
int cur = Settings.System.getInt(context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, 102);
int next = levels[0];
for (int i = 0; i < levels.length - 1; i++) {
if (cur <= levels[i]) { next = levels[i + 1]; break; }
}
if (cur >= levels[levels.length - 1]) next = levels[0];
Settings.System.putInt(context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, next);
}
The tile cycles the saved setting through four levels, and the press after 255 lands on 30. The setting was at 255 because I had put it there during the first black screen, and I had been pressing toggles on that screen about a minute before the strip, on the toggle row the tiles replaced, which ran the same cycle. The evidence that cleared brightness was its starting position. The tile is the only thing in my notes that writes a 30; the press itself I can't point at.
The fix wrote both places at once:
adb shell "sqlite3 /data/data/com.android.providers.settings/databases/settings.db \"UPDATE system SET value='200' WHERE name='screen_brightness';\""
adb shell "echo 200 > /sys/class/leds/lcd-backlight/brightness"
The setup wizard's welcome screen appeared, under two minutes after I had reported black. Whether the 255 written to sysfs eight seconds before the fix had already brought it back, I didn't check: I never looked at the phone between the two writes. The tile's levels became 80, 140, 200 and 255, and 30 left the cycle.
Black screen 3: 210 and a power button#
Under a minute after the welcome screen, the strip went back on with no reboot in between: 34 of the same 35, plus the wallpapers, the theme apps and a handful more, until 28 APKs were left. A launcher build went in, the phone rebooted, the log was free of crashes, and the screen was black again.
This time the backlight file read 210, and the power manager said the screen was on and bright. So brightness had nothing to do with this one. The framebuffer, read for real this time:
adb shell cat /dev/graphics/fb0 | head -c 1000 | xxd | head -10
00000000: 0000 00ff 0000 00ff 0000 00ff 0000 00ff ................
00000010: 0000 00ff 0000 00ff 0000 00ff 0000 00ff ................
00000020: 0000 00ff 0000 00ff 0000 00ff 0000 00ff ................
Seven more lines like that. Four bytes a pixel, the 8-8-8-8 surface SurfaceFlinger reports at boot: red, green and blue at zero, alpha at ff. The buffer wasn't empty; something had filled it with opaque black. Counted on this boot, EGL_BAD_DISPLAY matched two lines in the whole log, which is not what a compositor that fails on every frame leaves behind.
Then six minutes of software. Two power-key presses sent over adb, and the buffer stayed black. A full stop and start of the Android framework. LiveWallpapers.apk out, then back with Cyanbread, CM7's theme engine. A third restore of everything. The settings database deleted outright. After each, the framebuffer was still solid black. Then I pressed the real power button on the phone, and the welcome screen came up.
Two minutes after that, 51 APKs came out in one loop and 29 stayed, Cyanbread and LiveWallpapers.apk among them. That pass, and the better tool I could have used for it, are in the debloat post.
Three black screens, one cause on record#
Lined up, the three screens agree on less than I wanted them to. All three had the backlight on and a system that said it had drawn a window. Brightness explains one: in the first it was forced to 255 with no picture, and in the third it read 210. Removed APKs don't explain the second or the third, because putting every one of them back changed nothing. The first ended after a restore, a reboot and an unlock, in an order I never pulled apart, and the third ended at a button that adb had already pressed twice.
The GPU was suspected at all three and convicted at none. It is owed an apology, which it will not get, because it never knew it was a suspect. I got one cause in three, a WHERE clause, and two endings without one, and I'm filing them separately so the WHERE clause stops taking credit for the other two.
Filed under: android, debugging, hardware
Read next#
- Debloating Android 2.3 on the HTC Desire Z — the debloat that set this up, and the one APK that really was load-bearing.
- HTC Desire Z boot hang, an innocent cache wipe, and a reflash — the next time the phone refused to boot, for a real reason.
Sources#
- Android Settings.System SCREEN_BRIGHTNESS — brightness value range (0-255)