Search code examples
androidremote-accessupdating

Remote app update and restart for unattended field-deployed android phones?


We will be deploying hundreds of off-the-shelf android phones to far-flung field locations for unattended operation. They will be running an app we've developed to receive, process, and relay data from nearby Bluetooth sensors to a central server as part of a scientific research project. The devices will have internet access; we have two-way communications with the phones/app.

We will need to update our app over time, if not for bug fixes then for new functionality. It would be tremendously useful -- essential, really -- to be able to remotely force updates-with-restart (or -reboot) without user interaction; it will be prohibitively expensive in time, effort, and money to visit each station and manually complete updates.

What programming approaches or 3rd-party applications or technology can make this possible? Ideally we'd like to avoid rooting the phones, but if that's the only way then we would certainly do so.

It seems from many posts over the years to SO and elsewhere (e.g., https://stackoverflow.com/a/22134318) that it is not (or has not) been possible with out-of-the-box, unrooted android -- is this (still) true? We're looking for pointers to code, products, or how-to's to make this possible -- the simpler the better, but all suggestions welcome!


Solution

  • Near as I can tell, it's (still) not possible to do an entirely unattended app update on an off-the-shelf android device, so I looked into what's possible with a rooted device. A day of searching, reading, and experimenting has yielded a workable approach that gives us all the remote control we need and more. A rough outline of the steps and settings involved might help others with similar needs:

    • root the device (specifics vary from device to device, so you'll have to search for how to do it for your particular one)
    • configure (or re-configure, depending on the effects of the rooting procedure) the device settings (e.g., wifi) to get it minimally functional
    • go into Google Settings > Security and uncheck the "Improve harmful app detection" and "Scan device for security threats" options (otherwise, the phone will require an on-screen confirmation when remotely installing an app)
    • make sure that Developer Options are enabled; I read that the location may vary with android versions or device manufacturers, but one common location is Settings > Phone > (scroll to bottom) Build number -- tap 7 times on the Build number item to enable the Developer options
    • enable USB debugging in Settings > Developer Options (even though we will never make a USB connection to the phone and will do all our debugging over TCP; this option seems to behave more like "enable adb debugging" whether by USB or TCP)
    • install an ssh server or sshd app (there are many; I chose SSH Server for now and configured it (added 1 server and 1 user for that server)
    • from a terminal window on a computer or other device, connect to the phone with ssh using whatever port you used for the server you set up in SSH Server:
       $ ssh 192.168.1.217 -p 34567     # e.g., a local network IP address
    
    • configure the device for adb over TCP by issuing the following commands (doing it this way, there is no need to connect first over USB):
       $ su
       $ setprop service.adb.tcp.port 5555
       $ stop adbd
       $ start adbd
       $ exit
    
       Note that the first time you issue the su command, the device
       will prompt for an on-screen tap to permit the SSH Server 
       app to run with elevated privileges -- this is a one-time 
       configuration that you must perform before you deploy to the field! 
       Note also that you specify which port adb will use -- you could 
       use something other than the default 5555.
    
    • optionally, place a text file of adb authorization keys onto the device (/data/misc/adb/adb_keys -- requires su) to pre-authorize one or more computers from which you want to be able to connect and manage the remote devices -- see further discussion below
    • from Android Studio's terminal tab (or from any terminal with adb installed), use adb to connect to the device, ideally using the device's public IP address (more on this below):
       $ adb connect 93.184.216.34     # a public IP address
       # or, if you aren't set up yet for public access, a local address
       $ adb connect 192.168.1.217     # a local network IP address
    
       Note that you will get another on-screen prompt, this time
       to permit this computer to connect unless one of the
       following is true:
       a) you have previously connected to this device at this IP
          address from this computer and you check the option to
          permanently allow this connection, or
       b) you created an adb_keys file containing the key for the
          computer you are currently connecting from
       If you get the prompt, you must confirm it on-screen with
       a finger tap; if you select the option to permanently allow
       this connection, the /data/misc/adb/adb_keys file will be
       created and/or updated with the key for this connection
    

    Since the goal is to configure the phone such that it never requires any on-screen user interaction, you need to ensure that you deploy it to the field with an adb_keys file containing the key it will see when you connect to it remotely. The simplest way to do this is to connect once, manually, with the device from the computer you will use for remote management and using the IP address that the phone will have when deployed, because the key is different when coming through a local network than it is across the internet (I would love to have anyone more knowledgeable than myself clarify all of this -- I'm just reporting on what I've seen/learned today putting all of this together).

    If you anticipate managing the phone(s) from more than one computer, you should determine the keys for all such computers in advance and place them in a file that you install during setup into /data/misc/adb/adb_keys. This will "pre-authorize" adb connections from all computers whose keys match one in the file.

    That's it for setup. To use this setup to actually manage app updates is pretty straightforward and only requires adb. The following is a minimal list of adb commands you can use to manage the remote device(s) -- type adb --help or search the web for more on the many things you can do with adb (note that most but not all of the commands are 'adb shell' commands, either for the activity manager ('adb shell am') or the package manager ('adb shell pm'):

     # terminate your currently-running app
     $ adb shell am force-stop com.example.appname
    
     # uninstall your app completely
     $ adb shell pm uninstall com.example.appname
     # or, to keep the app's data and cache use the -k option
     $ adb shell pm uninstall -k com.example.appname
    
     # install your app from your local computer to the remote device
     $ adb install /local/path/to/the/app.apk
    
     # start your app by invoking it's main/startup activity
     $ adb shell am start -n com.example.appname.Main
    

    I have only done this for one phone; I will repeat it in the coming days for more phones and revise this answer if/as needed. While the setup for each phone is somewhat involved and will take a number of minutes, once configured it looks as though the updates for our hundreds of phones can be fully automated as a simple script of adb commands.