Move adbd authentication into its own library.

As part of moving adbd into a module, split authentication out into a
library with a stable ABI that lives on the system image.

Bug: http://b/137798163
Test: for i in `seq 1 100000`; do echo $i; adb wait-for-device shell "su 0 stop; su 0 start; sleep 10"; adb disconnect; done
Change-Id: I05e3679598e33c83f8cc4c17208f4f753cbfd5e1
diff --git a/Android.bp b/Android.bp
index bf4cf5d..9829c7f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7,6 +7,7 @@
 }
 
 subdirs = [
+    "adbd_auth",
     "cmds/*",
     "headers",
     "libs/*",
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp
new file mode 100644
index 0000000..f68ab5e
--- /dev/null
+++ b/libs/adbd_auth/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+    name: "libadbd_auth",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wthread-safety",
+        "-Werror",
+    ],
+    srcs: ["adbd_auth.cpp"],
+    export_include_dirs: ["include"],
+
+    version_script: "libadbd_auth.map.txt",
+    stubs: {
+        symbol_file: "libadbd_auth.map.txt",
+    },
+
+    host_supported: true,
+    recovery_available: true,
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+}
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
new file mode 100644
index 0000000..6479109
--- /dev/null
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION
+
+#include "include/adbd_auth.h"
+
+#include <inttypes.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/uio.h>
+
+#include <chrono>
+#include <deque>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <unordered_map>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+using android::base::unique_fd;
+
+struct AdbdAuthPacketAuthenticated {
+    std::string public_key;
+};
+
+struct AdbdAuthPacketDisconnected {
+    std::string public_key;
+};
+
+struct AdbdAuthPacketRequestAuthorization {
+    std::string public_key;
+};
+
+using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, AdbdAuthPacketDisconnected,
+                                    AdbdAuthPacketRequestAuthorization>;
+
+struct AdbdAuthContext {
+    static constexpr uint64_t kEpollConstSocket = 0;
+    static constexpr uint64_t kEpollConstEventFd = 1;
+    static constexpr uint64_t kEpollConstFramework = 2;
+
+public:
+    explicit AdbdAuthContext(AdbdAuthCallbacksV1* callbacks) : next_id_(0), callbacks_(*callbacks) {
+        epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+        if (epoll_fd_ == -1) {
+            PLOG(FATAL) << "failed to create epoll fd";
+        }
+
+        event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+        if (event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        sock_fd_.reset(android_get_control_socket("adbd"));
+        if (sock_fd_ == -1) {
+            PLOG(ERROR) << "failed to get adbd authentication socket";
+        } else {
+            if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
+                PLOG(FATAL) << "failed to make adbd authentication socket cloexec";
+            }
+
+            if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) {
+                PLOG(FATAL) << "failed to make adbd authentication socket nonblocking";
+            }
+
+            if (listen(sock_fd_.get(), 4) != 0) {
+                PLOG(FATAL) << "failed to listen on adbd authentication socket";
+            }
+        }
+    }
+
+    AdbdAuthContext(const AdbdAuthContext& copy) = delete;
+    AdbdAuthContext(AdbdAuthContext&& move) = delete;
+    AdbdAuthContext& operator=(const AdbdAuthContext& copy) = delete;
+    AdbdAuthContext& operator=(AdbdAuthContext&& move) = delete;
+
+    uint64_t NextId() { return next_id_++; }
+
+    void DispatchPendingPrompt() REQUIRES(mutex_) {
+        if (dispatched_prompt_) {
+            LOG(INFO) << "adbd_auth: prompt currently pending, skipping";
+            return;
+        }
+
+        if (pending_prompts_.empty()) {
+            LOG(INFO) << "adbd_auth: no prompts to send";
+            return;
+        }
+
+        LOG(INFO) << "adbd_auth: prompting user for adb authentication";
+        auto [id, public_key, arg] = std::move(pending_prompts_.front());
+        pending_prompts_.pop_front();
+
+        this->output_queue_.emplace_back(
+                AdbdAuthPacketRequestAuthorization{.public_key = public_key});
+
+        Interrupt();
+        dispatched_prompt_ = std::make_tuple(id, public_key, arg);
+    }
+
+    void UpdateFrameworkWritable() REQUIRES(mutex_) {
+        // This might result in redundant calls to EPOLL_CTL_MOD if, for example, we get notified
+        // at the same time as a framework connection, but that's unlikely and this doesn't need to
+        // be fast anyway.
+        if (framework_fd_ != -1) {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            if (!output_queue_.empty()) {
+                LOG(INFO) << "marking framework writable";
+                event.events |= EPOLLOUT;
+            }
+            event.data.u64 = kEpollConstFramework;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, framework_fd_.get(), &event));
+        }
+    }
+
+    void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) {
+        LOG(INFO) << "received new framework fd " << new_fd.get()
+                  << " (current = " << framework_fd_.get() << ")";
+
+        // If we already had a framework fd, clean up after ourselves.
+        if (framework_fd_ != -1) {
+            output_queue_.clear();
+            dispatched_prompt_.reset();
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, framework_fd_.get(), nullptr));
+            framework_fd_.reset();
+        }
+
+        if (new_fd != -1) {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            if (!output_queue_.empty()) {
+                LOG(INFO) << "marking framework writable";
+                event.events |= EPOLLOUT;
+            }
+            event.data.u64 = kEpollConstFramework;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, new_fd.get(), &event));
+            framework_fd_ = std::move(new_fd);
+        }
+    }
+
+    void HandlePacket(std::string_view packet) REQUIRES(mutex_) {
+        LOG(INFO) << "received packet: " << packet;
+
+        if (packet.length() < 2) {
+          LOG(ERROR) << "received packet of invalid length";
+          ReplaceFrameworkFd(unique_fd());
+        }
+
+        if (packet[0] == 'O' && packet[1] == 'K') {
+          CHECK(this->dispatched_prompt_.has_value());
+          auto& [id, key, arg] = *this->dispatched_prompt_;
+          keys_.emplace(id, std::move(key));
+
+          this->callbacks_.key_authorized(arg, id);
+          this->dispatched_prompt_ = std::nullopt;
+        } else if (packet[0] == 'N' && packet[1] == 'O') {
+          CHECK_EQ(2UL, packet.length());
+          // TODO: Do we want a callback if the key is denied?
+          this->dispatched_prompt_ = std::nullopt;
+          DispatchPendingPrompt();
+        } else {
+          LOG(ERROR) << "unhandled packet: " << packet;
+          ReplaceFrameworkFd(unique_fd());
+        }
+    }
+
+    bool SendPacket() REQUIRES(mutex_) {
+        if (output_queue_.empty()) {
+            return false;
+        }
+
+        CHECK_NE(-1, framework_fd_.get());
+
+        auto& packet = output_queue_.front();
+        struct iovec iovs[2];
+        if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) {
+            iovs[0].iov_base = const_cast<char*>("CK");
+            iovs[0].iov_len = 2;
+            iovs[1].iov_base = p->public_key.data();
+            iovs[1].iov_len = p->public_key.size();
+        } else if (auto* p = std::get_if<AdbdAuthPacketDisconnected>(&packet)) {
+            iovs[0].iov_base = const_cast<char*>("DC");
+            iovs[0].iov_len = 2;
+            iovs[1].iov_base = p->public_key.data();
+            iovs[1].iov_len = p->public_key.size();
+        } else if (auto* p = std::get_if<AdbdAuthPacketRequestAuthorization>(&packet)) {
+            iovs[0].iov_base = const_cast<char*>("PK");
+            iovs[0].iov_len = 2;
+            iovs[1].iov_base = p->public_key.data();
+            iovs[1].iov_len = p->public_key.size();
+        } else {
+            LOG(FATAL) << "unhandled packet type?";
+        }
+
+        output_queue_.pop_front();
+
+        ssize_t rc = writev(framework_fd_.get(), iovs, 2);
+        if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
+            PLOG(ERROR) << "failed to write to framework fd";
+            ReplaceFrameworkFd(unique_fd());
+            return false;
+        }
+
+        return true;
+    }
+
+    void Run() {
+        if (sock_fd_ == -1) {
+            LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts";
+        } else {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            event.data.u64 = kEpollConstSocket;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, sock_fd_.get(), &event));
+        }
+
+        {
+            struct epoll_event event;
+            event.events = EPOLLIN;
+            event.data.u64 = kEpollConstEventFd;
+            CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
+        }
+
+        while (true) {
+            struct epoll_event events[3];
+            int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1));
+            if (rc == -1) {
+                PLOG(FATAL) << "epoll_wait failed";
+            } else if (rc == 0) {
+                LOG(FATAL) << "epoll_wait returned 0";
+            }
+
+            bool restart = false;
+            for (int i = 0; i < rc; ++i) {
+                if (restart) {
+                    break;
+                }
+
+                struct epoll_event& event = events[i];
+                switch (event.data.u64) {
+                    case kEpollConstSocket: {
+                        unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr,
+                                                           SOCK_CLOEXEC | SOCK_NONBLOCK));
+                        if (new_framework_fd == -1) {
+                            PLOG(FATAL) << "failed to accept framework fd";
+                        }
+
+                        LOG(INFO) << "adbd_auth: received a new framework connection";
+                        std::lock_guard<std::mutex> lock(mutex_);
+                        ReplaceFrameworkFd(std::move(new_framework_fd));
+
+                        // Stop iterating over events: one of the later ones might be the old
+                        // framework fd.
+                        restart = false;
+                        break;
+                    }
+
+                    case kEpollConstEventFd: {
+                        // We were woken up to write something.
+                        uint64_t dummy;
+                        int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
+                        if (rc != 8) {
+                            PLOG(FATAL) << "failed to read from eventfd (rc = " << rc << ")";
+                        }
+
+                        std::lock_guard<std::mutex> lock(mutex_);
+                        UpdateFrameworkWritable();
+                        break;
+                    }
+
+                    case kEpollConstFramework: {
+                        char buf[4096];
+                        if (event.events & EPOLLIN) {
+                            int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf)));
+                            if (rc == -1) {
+                                LOG(FATAL) << "failed to read from framework fd";
+                            } else if (rc == 0) {
+                                LOG(INFO) << "hit EOF on framework fd";
+                                std::lock_guard<std::mutex> lock(mutex_);
+                                ReplaceFrameworkFd(unique_fd());
+                            } else {
+                                std::lock_guard<std::mutex> lock(mutex_);
+                                HandlePacket(std::string_view(buf, rc));
+                            }
+                        }
+
+                        if (event.events & EPOLLOUT) {
+                            std::lock_guard<std::mutex> lock(mutex_);
+                            while (SendPacket()) {
+                                continue;
+                            }
+                            UpdateFrameworkWritable();
+                        }
+
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"};
+    void IteratePublicKeys(bool (*callback)(const char*, size_t, void*), void* arg) {
+        for (const auto& path : key_paths) {
+            if (access(path, R_OK) == 0) {
+                LOG(INFO) << "Loading keys from " << path;
+                std::string content;
+                if (!android::base::ReadFileToString(path, &content)) {
+                    PLOG(ERROR) << "Couldn't read " << path;
+                    continue;
+                }
+                for (const auto& line : android::base::Split(content, "\n")) {
+                    if (!callback(line.data(), line.size(), arg)) {
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    uint64_t PromptUser(std::string_view public_key, void* arg) EXCLUDES(mutex_) {
+        uint64_t id = NextId();
+
+        std::lock_guard<std::mutex> lock(mutex_);
+        pending_prompts_.emplace_back(id, public_key, arg);
+        DispatchPendingPrompt();
+        return id;
+    }
+
+    uint64_t NotifyAuthenticated(std::string_view public_key) EXCLUDES(mutex_) {
+        uint64_t id = NextId();
+        std::lock_guard<std::mutex> lock(mutex_);
+        keys_.emplace(id, public_key);
+        output_queue_.emplace_back(
+                AdbdAuthPacketDisconnected{.public_key = std::string(public_key)});
+        return id;
+    }
+
+    void NotifyDisconnected(uint64_t id) EXCLUDES(mutex_) {
+        std::lock_guard<std::mutex> lock(mutex_);
+        auto it = keys_.find(id);
+        if (it == keys_.end()) {
+            LOG(DEBUG) << "couldn't find public key to notify disconnection, skipping";
+            return;
+        }
+        output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)});
+        keys_.erase(it);
+    }
+
+    // Interrupt the worker thread to do some work.
+    void Interrupt() {
+        uint64_t value = 1;
+        ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
+        if (rc == -1) {
+            PLOG(FATAL) << "write to eventfd failed";
+        } else if (rc != sizeof(value)) {
+            LOG(FATAL) << "write to eventfd returned short (" << rc << ")";
+        }
+    }
+
+    unique_fd epoll_fd_;
+    unique_fd event_fd_;
+    unique_fd sock_fd_;
+    unique_fd framework_fd_;
+
+    std::atomic<uint64_t> next_id_;
+    AdbdAuthCallbacksV1 callbacks_;
+
+    std::mutex mutex_;
+    std::unordered_map<uint64_t, std::string> keys_ GUARDED_BY(mutex_);
+
+    // We keep two separate queues: one to handle backpressure from the socket (output_queue_)
+    // and one to make sure we only dispatch one authrequest at a time (pending_prompts_).
+    std::deque<AdbdAuthPacket> output_queue_;
+
+    std::optional<std::tuple<uint64_t, std::string, void*>> dispatched_prompt_ GUARDED_BY(mutex_);
+    std::deque<std::tuple<uint64_t, std::string, void*>> pending_prompts_ GUARDED_BY(mutex_);
+};
+
+AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) {
+    if (callbacks->version != 1) {
+      LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version;
+      return nullptr;
+    }
+
+    return new AdbdAuthContext(&callbacks->callbacks.v1);
+}
+
+void adbd_auth_delete(AdbdAuthContext* ctx) {
+    delete ctx;
+}
+
+void adbd_auth_run(AdbdAuthContext* ctx) {
+    return ctx->Run();
+}
+
+void adbd_auth_get_public_keys(AdbdAuthContext* ctx,
+                               bool (*callback)(const char* public_key, size_t len, void* arg),
+                               void* arg) {
+    ctx->IteratePublicKeys(callback, arg);
+}
+
+uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len) {
+    return ctx->NotifyAuthenticated(std::string_view(public_key, len));
+}
+
+void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) {
+    return ctx->NotifyDisconnected(id);
+}
+
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len,
+                               void* arg) {
+    ctx->PromptUser(std::string_view(public_key, len), arg);
+}
+
+bool adbd_auth_supports_feature(AdbdAuthFeature) {
+    return false;
+}
diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h
new file mode 100644
index 0000000..b7c1cb8
--- /dev/null
+++ b/libs/adbd_auth/include/adbd_auth.h
@@ -0,0 +1,65 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+extern "C" {
+
+struct AdbdAuthCallbacksV1 {
+    // Callback for a successful user authorization.
+    void (*key_authorized)(void* arg, uint64_t id);
+};
+
+struct AdbdAuthCallbacks {
+    uint32_t version;
+    union {
+        AdbdAuthCallbacksV1 v1;
+    } callbacks;
+};
+
+struct AdbdAuthContext;
+
+AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks);
+void adbd_auth_delete(AdbdAuthContext* ctx);
+
+void adbd_auth_run(AdbdAuthContext* ctx);
+
+// Iterate through the list of authorized public keys.
+// Return false from the callback to stop iteration.
+void adbd_auth_get_public_keys(AdbdAuthContext* ctx,
+                               bool (*callback)(const char* public_key, size_t len, void* arg),
+                               void* arg);
+
+// Let system_server know that a key has been successfully used for authentication.
+uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len);
+
+// Let system_server know that a connection has been closed.
+void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id);
+
+// Prompt the user to authorize a public key.
+// When this happens, a callback will be run on the auth thread with the result.
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* arg);
+
+enum AdbdAuthFeature {
+};
+
+bool adbd_auth_supports_feature(AdbdAuthFeature f);
+
+}
diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt
new file mode 100644
index 0000000..d01233c
--- /dev/null
+++ b/libs/adbd_auth/libadbd_auth.map.txt
@@ -0,0 +1,13 @@
+LIBADBD_AUTH {
+  global:
+    adbd_auth_new; # apex
+    adbd_auth_delete; # apex
+    adbd_auth_run; # apex
+    adbd_auth_get_public_keys; #apex
+    adbd_auth_notify_auth; # apex
+    adbd_auth_notify_disconnect; # apex
+    adbd_auth_prompt_user; # apex
+    adbd_auth_supports_feature; # apex
+  local:
+    *;
+};