adb: add "adb unroot" to restart adb in non-root mode

Change-Id: Ice6b94a71a62648ac073d129914a07372411fb25
diff --git a/adb.c b/adb.c
index 4258a01..bfc0336 100644
--- a/adb.c
+++ b/adb.c
@@ -1261,35 +1261,36 @@
     }
 }
 
-static int should_drop_privileges() {
-#ifndef ALLOW_ADBD_ROOT
-    return 1;
-#else /* ALLOW_ADBD_ROOT */
-    int secure = 0;
+static bool should_drop_privileges() {
+#if defined(ALLOW_ADBD_ROOT)
     char value[PROPERTY_VALUE_MAX];
 
-   /* run adbd in secure mode if ro.secure is set and
-    ** we are not in the emulator
-    */
+    // The emulator is never secure, so don't drop privileges there.
+    // TODO: this seems like a bug --- shouldn't the emulator behave like a device?
     property_get("ro.kernel.qemu", value, "");
-    if (strcmp(value, "1") != 0) {
-        property_get("ro.secure", value, "1");
-        if (strcmp(value, "1") == 0) {
-            // don't run as root if ro.secure is set...
-            secure = 1;
-
-            // ... except we allow running as root in userdebug builds if the
-            // service.adb.root property has been set by the "adb root" command
-            property_get("ro.debuggable", value, "");
-            if (strcmp(value, "1") == 0) {
-                property_get("service.adb.root", value, "");
-                if (strcmp(value, "1") == 0) {
-                    secure = 0;
-                }
-            }
-        }
+    if (strcmp(value, "1") == 0) {
+        return false;
     }
-    return secure;
+
+    // Don't run as root if ro.secure is set...
+    property_get("ro.secure", value, "1");
+    bool ro_secure = (strcmp(value, "1") == 0);
+
+    // ... except we allow running as root in userdebug builds if the
+    // service.adb.root property has been set by the "adb root" command
+    property_get("ro.debuggable", value, "");
+    bool ro_debuggable = (strcmp(value, "1") == 0);
+
+    property_get("service.adb.root", value, "");
+    bool adb_root = (strcmp(value, "1") == 0);
+    bool adb_unroot = (strcmp(value, "0") == 0);
+    if (adb_unroot) {
+        return true; // The user explicitly wants us to drop privileges.
+    }
+
+    return ro_secure || !ro_debuggable;
+#else
+    return true; // "adb root" not allowed, always drop privileges.
 #endif /* ALLOW_ADBD_ROOT */
 }
 #endif /* !ADB_HOST */
diff --git a/commandline.c b/commandline.c
index a06885b..830f290 100644
--- a/commandline.c
+++ b/commandline.c
@@ -210,6 +210,7 @@
         "  adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n"
         "  adb reboot-bootloader        - reboots the device into the bootloader\n"
         "  adb root                     - restarts the adbd daemon with root permissions\n"
+        "  adb unroot                   - restarts the adbd daemon without root permissions\n"
         "  adb usb                      - restarts the adbd daemon listening on USB\n"
         "  adb tcpip <port>             - restarts the adbd daemon listening on TCP on the specified port\n"
         "networking:\n"
@@ -1473,6 +1474,7 @@
              !strcmp(argv[0], "tcpip") ||
              !strcmp(argv[0], "usb") ||
              !strcmp(argv[0], "root") ||
+             !strcmp(argv[0], "unroot") ||
              !strcmp(argv[0], "disable-verity") ||
              !strcmp(argv[0], "enable-verity")) {
         char command[100];
diff --git a/services.c b/services.c
index e4ce0bc..bd210a8 100644
--- a/services.c
+++ b/services.c
@@ -82,6 +82,22 @@
     }
 }
 
+void restart_unroot_service(int fd, void *cookie)
+{
+    char buf[100];
+
+    if (getuid() != 0) {
+        snprintf(buf, sizeof(buf), "adbd not running as root\n");
+        writex(fd, buf, strlen(buf));
+        adb_close(fd);
+    } else {
+        property_set("service.adb.root", "0");
+        snprintf(buf, sizeof(buf), "restarting adbd as non root\n");
+        writex(fd, buf, strlen(buf));
+        adb_close(fd);
+    }
+}
+
 void restart_tcp_service(int fd, void *cookie)
 {
     char buf[100];
@@ -437,6 +453,8 @@
         ret = create_service_thread(reboot_service, arg);
     } else if(!strncmp(name, "root:", 5)) {
         ret = create_service_thread(restart_root_service, NULL);
+    } else if(!strncmp(name, "unroot:", 7)) {
+        ret = create_service_thread(restart_unroot_service, NULL);
     } else if(!strncmp(name, "backup:", 7)) {
         char* arg = strdup(name + 7);
         if (arg == NULL) return -1;
diff --git a/sockets.c b/sockets.c
index faa9564..6cdde97 100644
--- a/sockets.c
+++ b/sockets.c
@@ -430,12 +430,6 @@
 
 asocket *create_local_service_socket(const char *name)
 {
-    asocket *s;
-    int fd;
-#if !ADB_HOST
-    char debug[PROPERTY_VALUE_MAX];
-#endif
-
 #if !ADB_HOST
     if (!strcmp(name,"jdwp")) {
         return create_jdwp_service_socket();
@@ -444,18 +438,19 @@
         return create_jdwp_tracker_service_socket();
     }
 #endif
-    fd = service_to_fd(name);
+    int fd = service_to_fd(name);
     if(fd < 0) return 0;
 
-    s = create_local_socket(fd);
+    asocket* s = create_local_socket(fd);
     D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
 
 #if !ADB_HOST
+    char debug[PROPERTY_VALUE_MAX];
     if (!strncmp(name, "root:", 5))
         property_get("ro.debuggable", debug, "");
 
-    if ((!strncmp(name, "root:", 5) && getuid() != 0
-        && strcmp(debug, "1") == 0)
+    if ((!strncmp(name, "root:", 5) && getuid() != 0 && strcmp(debug, "1") == 0)
+        || (!strncmp(name, "unroot:", 7) && getuid() == 0)
         || !strncmp(name, "usb:", 4)
         || !strncmp(name, "tcpip:", 6)) {
         D("LS(%d): enabling exit_on_close\n", s->id);
diff --git a/tests/test_adb.py b/tests/test_adb.py
old mode 100644
new mode 100755
index b0ae07f..4b3baf3
--- a/tests/test_adb.py
+++ b/tests/test_adb.py
@@ -8,11 +8,11 @@
 import os
 import random
 import re
+import shlex
 import subprocess
+import sys
 import tempfile
 import unittest
-import sys
-import shlex
 
 
 def trace(cmd):
@@ -181,6 +181,12 @@
     def usb(self):
         return call_checked(self.adb_cmd + "usb")
 
+    def root(self):
+        return call_checked(self.adb_cmd + "root")
+
+    def unroot(self):
+        return call_checked(self.adb_cmd + "unroot")
+
     def forward_remove(self, local):
         return call_checked(self.adb_cmd + "forward --remove {}".format(local))
 
@@ -233,6 +239,17 @@
                 version_num = True
         self.assertTrue(version_num)
 
+    def test_root_unroot(self):
+        """Make sure that adb root and adb unroot work, using id(1)."""
+        for device in get_device_list():
+            adb = AdbWrapper(device)
+            adb.root()
+            adb.wait()
+            self.assertEqual("root", adb.shell("id -un").strip())
+            adb.unroot()
+            adb.wait()
+            self.assertEqual("shell", adb.shell("id -un").strip())
+
 
 class AdbFile(unittest.TestCase):
     SCRATCH_DIR = "/data/local/tmp"