I do a lot of low-level Android development. Most devs would use the Android debug bridge (adb) to view logs and otherwise interact with a phone when building an app. adb is also useful for doing a sizeable chunk of Android framework development. Major portions of the framework can be treated like apps, and you can interactively debug native daemons with gdb/gdbserver.
When one gets down to core components such as init, vold, adbd, or the Linux kernel, adb doesn't cut it. A bug in one of these can prevent adb access from becoming available, period. I need something that's online essentially from when the device boots through its entire uptime.
Down low, my weapon of choice is a serial port (also called a serial console or UART). These components are easy to configure and use, so they're often the first system introspection mechanism available to developers. Like with adb, you can view logs, open an interactive console, or debug executables, but you can also access sophisticated functions like kgdb, which allows you to interactively debug the Linux kernel.
I may casully mention commandline shenanigans. The lappy runs Ubuntu, so take that as a point of reference. Also know that enabling the serial port could significantly increase boot times, as serial speeds are slow, and your bootloader and kernel could be printing full debug logs.
Table of Contents and Major Takeaways
Table of Contents
- Enabling the serial port
- Finding the serial port
Building a dual-function adapter
- Bill of materials
- Google Pixel devices (at least the 2, 3, 3aXL, 5a5G) have a development serial port routed out their USB-C port. Pixel 2's is 1.8V and configured 115200 8N1 by default. It would make sense for other Pixels to have this same functionality, but I haven't tested them.
- The serial port must be enabled in fastboot, and the device must be unlocked for the port to be enabled (and, I assume, disabled).
- With some easy-to-find components, you can make a dual-function adapter that provides you with simulatenous USB and serial access to your Pixel. I've included a bill of materials and netlist for my adapter for the reader's convenience.
- Not all USB-C cables will work with this adapter. The cable must have USB 2.0 signals routed as well as SBU1/SBU2, CC1 (CC), and CC2 (VCONN).
- My stock Pixel 2 USB-C cable did not work. My external hard drive adapter's USB cable worked, and active and passive Thuderbolt 3 cables should also work, though I'd need to do some research to make sure TB3 cables were safe. (I mean, I tested them to positive results, and they were not a splode, but - better safe than sorry.)
Enabling the Serial Port
My development target for this post was the Google Pixel 2, though I've also used this method on the Pixel 3, Pixel 3a XL, and Pixel 5a 5G. The first thing I needed to do was make sure the serial port was enabled so I'd have something to find. To enable the serial port, I probably needed to start the phone in fastboot mode by powering it on while holding VOLDN.
The fastboot UI showed me that the console was available but that it was disabled ([Console: DISABLED]). This was good - low-level debug access should not be enabled on production devices, as it can be used to leak sensitive information or allow unauthenticated access to the device. I couldn't see any UI options for enabling it, so I needed to use the fastboot commandline utility. The generic commandline help was ... unhelpful, and fastboot oem and fastboot oem help didn't provide any insight. Time for some digging.
fastboot is hosted by the phone's bootloader, so any commands I'd need to use would have to be present as strings in the code. I downloaded the full system image:
unpacked the bootloader:
and did some light reverse engineering. I'll do a separate post on this, since it's a bit involved.
I found the commands fastboot oem uart and fastboot oem uart config. Fortunately, I was not allowed to modify the console configuration. I say "fortunately," since this phone was in a locked state. Allowing the console state to be modified while the device is locked would also be a security vulnerability.
I enabled OEM unlocking in the developer options and unlocked the phone using fastboot. fastboot oem uart enable enabled the console, though the UI still read [Console: DISABLED]. Rebooting the phone back to fastboot mode refreshed the UI, and it read [Console: ENABLED]. I'm not sure if this was necessary or if the UI was just stale.
Finding the Serial Port
So where, physically, was the console exposed on the phone? Google Nexus devices had a special headphone jack circuit. Flagship Pixels don't have headphone jacks, period, much less special ones, grumble grumble, so without prying the back off and poking the PCB directly, the only access I had was through the USB port or the SIM slot. Getting in through the SIM slot would be a royal pain, so I focused on USB.
The Pixel 2 has a USB-C connector, but it's a USB 2.0 device. This leaves a whole bunch of free I/O on that connector that could be used for the console, or the console could be present as a USB endpoint. lsusb didn't show anything relevant while the phone was in fastboot mode. Chromebooks can be debugged with a special cable called a SuzyQable, but, tl;dr, it doesn't do what we want on the Pixel 2. (It does other fun stuff, at least on the Pixel 3a XL, but it doesn't expose the console.) I needed to build a USB-C breakout so I could probe the connections with my oscilloscope.
I soldered two rows of headers onto a female USB-C breakout board. A female board is more convenient than a male one, since it lets you connect your phone and the breakout with a USB-C cable. The male board must be mated with the phone directly, which makes picking up the phone and using it challenging. USB-C cables are reversable, though, so I would need to mark my cable to ensure I mated it in the same orientations at my phone and adapter. (USB docs refer to this orientation as "unflipped".)
This reversible nature is another vote in favor of the female breakout board - depending on what "unflipped" is on the phone, plugging it into a male board might require changing the wiring to flip the serial signals. Otherwise, the phone would need to be upside down (screen facing the desk) for the serial connection to work. I plugged the breakout into a breadboard, clipped a ground to one of the marked pins, and used my scope to play hide and seek with the console.
Rebooting phones usually makes them spit out logs. I poked each pin with my scope probe and used the fastboot UI to reboot the phone once I had a good connection. Nothing happened - all of the I/Os stayed at 0V. Boo!
I was skeptical that I'd need a full USB connection, but I'd likely have to feed Vcc and GND to the phone to trigger it do to anything. I rigged up my benchtop power supply to the breakout and set it to 5V. I also wired up the breakout as described in Reference 1, Table 3-13 USB Type-C to USB 2.0 Standard-A Cable Assembly Wiring. Once I found the console signals, my plan was to also connect the USB 2.0 signals so both functions could be available concurrently.
The new adapter powered the phone, but nothing showed up on any of the "extra" cables. Just to make sure, I buzzed out the pins on the breakout to the far end of my USB-C cable. It's a good thing I tried this before giving up and cracking the phone's case open. The cable didn't have every conductor, which I confirmed by cutting it in half and doing my Count von Count impression. I was using the USB-C cable that came with the Pixel 2. As I said before, the Pixel 2 is a USB 2.0 device, so the included cable didn't need to be an expensive one with all the wires. It had just enough to do its 2.0 thing.
I grabbed another USB-C cable from a big box retailer that said it supported 100W power delivery but otherwise didn't specify if it was a fully-connected cable. Turns out it also didn't pass through all of the signals on my breakout. Finally, I tried both a C-to-C cable packaged with an external hard drive adapter and an active Thunderbolt 3 (TB3) cable. Each provided all the signals on the breakout in some way, shape, or form, though the fact that the TB3 cable was active meant that some signals didn't buzz out directly to the breakout. I deferred researching if using my active (or a passive) TB3 cable was safe and instead stuck with the drive adapter cable, but I suspect that a passive TB3 cable is one's best bet when it comes to consistently finding something that passes through every USB-C signal.
I marked the cable so I could always make sure it was unflipped and repeated my discovery process, where I struck gold on SBU1. The phone was transmitting 1.8V 115200 8N1 UART-style serial. It stood to reason that I could use SBU2 to transmit to the phone.
The Pixel 2 and Pixel 5a 5G have different unflipped USB orientations. I marked my cable with an X on one side of each connector. The Pixel 2 needs the X facing up (or down) on both the adapter and phone. The Pixel 5a 5G needs the X facing opposite directions on the adapter and phone.
Building a Dual-Function Adapter
Once I found the signals, I connected them to a USB-to-UART adapter and connected the unflipped USB 2.0 signals to a breakout cable. Both interfaces could be used at the same time, and SBU2 was, indeed, TX to the phone. Ideally, I'd design a board with USB-to-UART and USB hub chips on it so this would be a single-plug solution like the SuzyQable, but that's a project for a different day.
For now, this'll do.
Bill of Materials
I don't specifically endorse these components or suppliers. This is just what I happened to have kicking around or what was available for quick shipping. Feel free to sub at will.
That having been said, I haven't found anything better for P1.
J2 is nice, since it's adjustable-voltage. For ergonomics, though, I needed to use a USB extender with it, which wasn't great. You can get voltage-specific models from FTDI that have cables built in or, to make this a bit less inconvenient, do as I did and install a straight header instead of a right angle one. You'd still probably need the range extender, but the board laid flat is easier to deal with than sticking straight up. The vendor also provided a set of wires, but they had female terminations. Most, e.g., breakaway headers that you could use to adapt this to a breadboard are shorter on one side for soldering into through holes. The short sides don't always mate well (or at all), so were I to use the provided cables, I'd snip the ends and install pins.
Qty 1 - P1
USB Type C Female Receptacle Breakout Board v2.0
Qty 1 - J1
Adapter Cable, USB 2.0, IDC 5 Male (single row) to USB A Male
Qty 1 - J2
DSD TECH SH-U09C2 USB to TTL Adapter Built-in FTDI FT232RL IC for Debugging and Programming
Qty 1 - R1
56kOhm 5% through hole resistor
20PCS 2.54mm Breakaway PCB Board 40Pin Male and Female Header Connector for Arduino Shield
Elenco 350 Piece Pre-formed Jumper Wire Kit
Breadboard Solderless Prototype PCB Board – ALLUS BB-009 (3pcs) 400 Pin with 4 Power Rails and Double Sided Tape for Raspberry Pi and Arduino
In my picture above, pins J1.1 and J2.1 are towards the bottom of the picture. Both have single rows of pins, so I figured naming them like connectors was fine.
- USB Type-C Cable and Connector Specification, Release 2.0, August 2019
Copyright (c) 2020, Joseph Tanen. All rights reserved.