Merge "adb: better error messages on deduplicated filesystems."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 3bf281c..76ca19a 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -365,8 +365,8 @@
         switch (p->msg.arg0) {
 #if ADB_HOST
             case ADB_AUTH_TOKEN:
-                if (t->GetConnectionState() == kCsOffline) {
-                    t->SetConnectionState(kCsUnauthorized);
+                if (t->GetConnectionState() != kCsAuthorizing) {
+                    t->SetConnectionState(kCsAuthorizing);
                 }
                 send_auth_response(p->payload.data(), p->msg.data_length, t);
                 break;
@@ -1103,14 +1103,11 @@
     if (!strcmp(service, "reconnect-offline")) {
         std::string response;
         close_usb_devices([&response](const atransport* transport) {
-            switch (transport->GetConnectionState()) {
-                case kCsOffline:
-                case kCsUnauthorized:
-                    response += "reconnecting " + transport->serial_name() + "\n";
-                    return true;
-                default:
-                    return false;
+            if (!ConnectionStateIsOnline(transport->GetConnectionState())) {
+                response += "reconnecting " + transport->serial_name() + "\n";
+                return true;
             }
+            return false;
         });
         if (!response.empty()) {
             response.resize(response.size() - 1);
diff --git a/adb/adb.h b/adb/adb.h
index 1e58ee1..ede55da 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -95,16 +95,33 @@
 
 enum ConnectionState {
     kCsAny = -1,
-    kCsOffline = 0,
+
+    kCsConnecting = 0,  // Haven't received a response from the device yet.
+    kCsAuthorizing,     // Authorizing with keys from ADB_VENDOR_KEYS.
+    kCsUnauthorized,    // ADB_VENDOR_KEYS exhausted, fell back to user prompt.
+    kCsNoPerm,          // Insufficient permissions to communicate with the device.
+    kCsOffline,
+
     kCsBootloader,
     kCsDevice,
     kCsHost,
     kCsRecovery,
-    kCsNoPerm,  // Insufficient permissions to communicate with the device.
     kCsSideload,
-    kCsUnauthorized,
 };
 
+inline bool ConnectionStateIsOnline(ConnectionState state) {
+    switch (state) {
+        case kCsBootloader:
+        case kCsDevice:
+        case kCsHost:
+        case kCsRecovery:
+        case kCsSideload:
+            return true;
+        default:
+            return false;
+    }
+}
+
 void print_packet(const char* label, apacket* p);
 
 // These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index ade2623..0f4dd33 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -464,6 +464,7 @@
     std::shared_ptr<RSA> key = t->NextKey();
     if (key == nullptr) {
         // No more private keys to try, send the public key.
+        t->SetConnectionState(kCsUnauthorized);
         send_auth_publickey(t);
         return;
     }
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 7bc0165..e567ff4 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -750,7 +750,7 @@
     if (!s->transport) {
         SendFail(s->peer->fd, "device offline (no transport)");
         goto fail;
-    } else if (s->transport->GetConnectionState() == kCsOffline) {
+    } else if (!ConnectionStateIsOnline(s->transport->GetConnectionState())) {
         /* if there's no remote we fail the connection
          ** right here and terminate it
          */
diff --git a/adb/transport.cpp b/adb/transport.cpp
index fa7cc8c..be7f8fe 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -708,22 +708,41 @@
     }
     lock.unlock();
 
-    // Don't return unauthorized devices; the caller can't do anything with them.
-    if (result && result->GetConnectionState() == kCsUnauthorized && !accept_any_state) {
-        *error_out = "device unauthorized.\n";
-        char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
-        *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
-        *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
-        *error_out += "\n";
-        *error_out += "Try 'adb kill-server' if that seems wrong.\n";
-        *error_out += "Otherwise check for a confirmation dialog on your device.";
-        result = nullptr;
-    }
+    if (result && !accept_any_state) {
+        // The caller requires an active transport.
+        // Make sure that we're actually connected.
+        ConnectionState state = result->GetConnectionState();
+        switch (state) {
+            case kCsConnecting:
+                *error_out = "device still connecting";
+                result = nullptr;
+                break;
 
-    // Don't return offline devices; the caller can't do anything with them.
-    if (result && result->GetConnectionState() == kCsOffline && !accept_any_state) {
-        *error_out = "device offline";
-        result = nullptr;
+            case kCsAuthorizing:
+                *error_out = "device still authorizing";
+                result = nullptr;
+                break;
+
+            case kCsUnauthorized: {
+                *error_out = "device unauthorized.\n";
+                char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+                *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+                *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+                *error_out += "\n";
+                *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+                *error_out += "Otherwise check for a confirmation dialog on your device.";
+                result = nullptr;
+                break;
+            }
+
+            case kCsOffline:
+                *error_out = "device offline";
+                result = nullptr;
+                break;
+
+            default:
+                break;
+        }
     }
 
     if (result) {
@@ -802,6 +821,10 @@
             return "sideload";
         case kCsUnauthorized:
             return "unauthorized";
+        case kCsAuthorizing:
+            return "authorizing";
+        case kCsConnecting:
+            return "connecting";
         default:
             return "unknown";
     }
@@ -1080,7 +1103,7 @@
 
 void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
                             unsigned writeable) {
-    atransport* t = new atransport((writeable ? kCsOffline : kCsNoPerm));
+    atransport* t = new atransport((writeable ? kCsConnecting : kCsNoPerm));
 
     D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb);
diff --git a/adb/transport.h b/adb/transport.h
index ebc186b..e1cbc09 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -198,7 +198,7 @@
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport(ConnectionState state = kCsOffline)
+    atransport(ConnectionState state = kCsConnecting)
         : id(NextTransportId()),
           connection_state_(state),
           connection_waitable_(std::make_shared<ConnectionWaitable>()),
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9b64be7..dfb7a6a 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -80,8 +80,13 @@
     return value;                                                  \
   }()
 
+// Backtrace frame dump could contain:
+//   #01 pc 0001cded  /data/tmp/debuggerd_test32 (raise_debugger_signal+80)
+// or
+//   #01 pc 00022a09  /data/tmp/debuggerd_test32 (offset 0x12000) (raise_debugger_signal+80)
 #define ASSERT_BACKTRACE_FRAME(result, frame_name) \
-  ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ \S+ \()" frame_name R"(\+)");
+  ASSERT_MATCH(result,                             \
+               R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
 
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
                                  InterceptStatus* status, DebuggerdDumpType intercept_type) {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8bd92cc..17d34e1 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -240,6 +240,29 @@
     return Success();
 }
 
+static Result<Success> do_interface_restart(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
+    if (!svc) return Error() << "interface " << args[1] << " not found";
+    svc->Restart();
+    return Success();
+}
+
+static Result<Success> do_interface_start(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
+    if (!svc) return Error() << "interface " << args[1] << " not found";
+    if (auto result = svc->Start(); !result) {
+        return Error() << "Could not start interface: " << result.error();
+    }
+    return Success();
+}
+
+static Result<Success> do_interface_stop(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
+    if (!svc) return Error() << "interface " << args[1] << " not found";
+    svc->Stop();
+    return Success();
+}
+
 // mkdir <path> [mode] [owner] [group]
 static Result<Success> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
@@ -1050,6 +1073,9 @@
         {"init_user0",              {0,     0,    {false,  do_init_user0}}},
         {"insmod",                  {1,     kMax, {true,   do_insmod}}},
         {"installkey",              {1,     1,    {false,  do_installkey}}},
+        {"interface_restart",       {1,     1,    {false,  do_interface_restart}}},
+        {"interface_start",         {1,     1,    {false,  do_interface_start}}},
+        {"interface_stop",          {1,     1,    {false,  do_interface_stop}}},
         {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
         {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
         {"loglevel",                {1,     1,    {false,  do_loglevel}}},
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index ad48602..bb241af 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -35,11 +35,6 @@
 
 std::string GetProperty(const std::string& key, const std::string& default_value);
 bool GetBoolProperty(const std::string& key, bool default_value);
-template <typename T>
-T GetIntProperty(const std::string&, T default_value, T = std::numeric_limits<T>::min(),
-                 T = std::numeric_limits<T>::max()) {
-    return default_value;
-}
 
 }  // namespace base
 }  // namespace android
diff --git a/init/init.cpp b/init/init.cpp
index 54bbb4b..645184b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -297,40 +297,29 @@
 
     const ControlMessageFunction& function = it->second;
 
-    if (function.target == ControlTarget::SERVICE) {
-        Service* svc = ServiceList::GetInstance().FindService(name);
-        if (svc == nullptr) {
-            LOG(ERROR) << "No such service '" << name << "' for ctl." << msg;
-            return;
-        }
-        if (auto result = function.action(svc); !result) {
-            LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": "
-                       << result.error();
-        }
+    Service* svc = nullptr;
 
+    switch (function.target) {
+        case ControlTarget::SERVICE:
+            svc = ServiceList::GetInstance().FindService(name);
+            break;
+        case ControlTarget::INTERFACE:
+            svc = ServiceList::GetInstance().FindInterface(name);
+            break;
+        default:
+            LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
+                       << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
+            return;
+    }
+
+    if (svc == nullptr) {
+        LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
         return;
     }
 
-    if (function.target == ControlTarget::INTERFACE) {
-        for (const auto& svc : ServiceList::GetInstance()) {
-            if (svc->interfaces().count(name) == 0) {
-                continue;
-            }
-
-            if (auto result = function.action(svc.get()); !result) {
-                LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name()
-                           << " with interface " << name << ": " << result.error();
-            }
-
-            return;
-        }
-
-        LOG(ERROR) << "Could not find service hosting interface " << name;
-        return;
+    if (auto result = function.action(svc); !result) {
+        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
     }
-
-    LOG(ERROR) << "Invalid function target from static map key '" << msg
-               << "': " << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
 }
 
 static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index f55d2c4..293736d 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -20,12 +20,14 @@
 #include <fcntl.h>
 #include <linux/input.h>
 #include <sys/cdefs.h>
+#include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <algorithm>
 #include <functional>
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -118,6 +120,8 @@
 
 constexpr char kDevicePath[] = "/dev/input";
 
+std::map<std::string, int> keychord_registration;
+
 void HandleKeychord(int id) {
     // Only handle keychords if adb is enabled.
     std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
@@ -215,6 +219,7 @@
 }
 
 void GeteventOpenDevice(const std::string& device) {
+    if (keychord_registration.count(device)) return;
     auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
     if (fd == -1) {
         PLOG(ERROR) << "Can not open " << device;
@@ -222,21 +227,74 @@
     }
     if (!KeychordGeteventEnable(fd)) {
         ::close(fd);
+    } else {
+        keychord_registration.emplace(device, fd);
+    }
+}
+
+void GeteventCloseDevice(const std::string& device) {
+    auto it = keychord_registration.find(device);
+    if (it == keychord_registration.end()) return;
+    auto fd = (*it).second;
+    unregister_epoll_handler(fd);
+    keychord_registration.erase(it);
+    ::close(fd);
+}
+
+int inotify_fd = -1;
+
+void InotifyHandler() {
+    unsigned char buf[512];
+
+    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd, buf, sizeof(buf)));
+    if (res < 0) {
+        PLOG(WARNING) << "could not get event";
+        return;
+    }
+
+    auto event_buf = buf;
+    while (static_cast<size_t>(res) >= sizeof(inotify_event)) {
+        auto event = reinterpret_cast<inotify_event*>(event_buf);
+        auto event_size = sizeof(inotify_event) + event->len;
+        if (static_cast<size_t>(res) < event_size) break;
+        if (event->len) {
+            std::string devname(kDevicePath);
+            devname += '/';
+            devname += event->name;
+            if (event->mask & IN_CREATE) {
+                GeteventOpenDevice(devname);
+            } else {
+                GeteventCloseDevice(devname);
+            }
+        }
+        res -= event_size;
+        event_buf += event_size;
     }
 }
 
 void GeteventOpenDevice() {
-    std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
-    if (!device) return;
-
-    dirent* entry;
-    while ((entry = readdir(device.get()))) {
-        if (entry->d_name[0] == '.') continue;
-        std::string devname(kDevicePath);
-        devname += '/';
-        devname += entry->d_name;
-        GeteventOpenDevice(devname);
+    inotify_fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+    if (inotify_fd < 0) {
+        PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
+    } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) < 0) {
+        PLOG(WARNING) << "Could not add watch for " << kDevicePath;
+        ::close(inotify_fd);
+        inotify_fd = -1;
     }
+
+    std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
+    if (device) {
+        dirent* entry;
+        while ((entry = readdir(device.get()))) {
+            if (entry->d_name[0] == '.') continue;
+            std::string devname(kDevicePath);
+            devname += '/';
+            devname += entry->d_name;
+            GeteventOpenDevice(devname);
+        }
+    }
+
+    if (inotify_fd >= 0) register_epoll_handler(inotify_fd, InotifyHandler);
 }
 
 void AddServiceKeycodes(Service* svc) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c3100a5..47e45ef 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -65,7 +65,6 @@
 
 using namespace std::literals;
 
-using android::base::GetIntProperty;
 using android::base::ReadFileToString;
 using android::base::Split;
 using android::base::StartsWith;
diff --git a/init/service.h b/init/service.h
index cf38f69..9cb35b8 100644
--- a/init/service.h
+++ b/init/service.h
@@ -244,6 +244,16 @@
         return nullptr;
     }
 
+    Service* FindInterface(const std::string& interface_name) {
+        for (const auto& svc : services_) {
+            if (svc->interfaces().count(interface_name) > 0) {
+                return svc.get();
+            }
+        }
+
+        return nullptr;
+    }
+
     void DumpState() const;
 
     auto begin() const { return services_.begin(); }
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index fdb4641..267d530 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -30,8 +30,6 @@
 #include "util.h"
 
 #if defined(__ANDROID__)
-#include <android-base/properties.h>
-
 #include "property_service.h"
 #include "selinux.h"
 #else
@@ -39,7 +37,6 @@
 #endif
 
 using android::base::GetExecutablePath;
-using android::base::GetIntProperty;
 using android::base::Join;
 using android::base::Socketpair;
 using android::base::Split;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 10afe33..4c05a1b 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -87,8 +87,8 @@
 
   ISzAlloc alloc;
   CXzUnpacker state;
-  alloc.Alloc = [](void*, size_t size) { return malloc(size); };
-  alloc.Free = [](void*, void* ptr) { return free(ptr); };
+  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
 
   XzUnpacker_Construct(&state, &alloc);
 
@@ -106,7 +106,7 @@
       dst_remaining += 2 * gnu_debugdata_size_;
     }
     return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
-                                 &src_remaining, CODER_FINISH_ANY, &status);
+                                 &src_remaining, true, CODER_FINISH_ANY, &status);
     src_offset += src_remaining;
     dst_offset += dst_remaining;
   } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);