Merge "Add EthernetDevice to manage ethernet connection."
diff --git a/Android.mk b/Android.mk
index d727ca2..5816749 100644
--- a/Android.mk
+++ b/Android.mk
@@ -18,9 +18,9 @@
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
 
-# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr} or a device-specific
-# module that defines make_device() and the exact RecoveryUI class for the target. It defaults to
-# librecovery_ui_default, which uses ScreenRecoveryUI.
+# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr,ethernet} or a
+# device-specific module that defines make_device() and the exact RecoveryUI class for the
+# target. It defaults to librecovery_ui_default, which uses ScreenRecoveryUI.
 TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
 
 # librecovery_ui_ext (shared library)
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2023349..a093008 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -52,6 +52,7 @@
   ui->ResetKeyInterruptStatus();
   ui->SetTitle(title_lines);
   ui->ShowText(true);
+  device->StartFastboot();
 
   // Reset to normal system boot so recovery won't cycle indefinitely.
   // TODO(b/112277594) Clear only if 'recovery' field of BCB is empty. If not,
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 30a1fc0..80cba61 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -471,6 +471,11 @@
     std::string usb_config =
         fastboot ? "fastboot" : IsRoDebuggable() || IsDeviceUnlocked() ? "adb" : "none";
     std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
+    if (fastboot) {
+      device->PreFastboot();
+    } else {
+      device->PreRecovery();
+    }
     if (usb_config != usb_state) {
       if (!SetUsbConfig("none")) {
         LOG(ERROR) << "Failed to clear USB config";
diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp
index 149ef8a..9dfee5f 100644
--- a/recovery_ui/Android.bp
+++ b/recovery_ui/Android.bp
@@ -22,6 +22,7 @@
 
     srcs: [
         "device.cpp",
+        "ethernet_ui.cpp",
         "screen_ui.cpp",
         "stub_ui.cpp",
         "ui.cpp",
@@ -90,3 +91,23 @@
 
     export_include_dirs: ["include"],
 }
+
+// The default device that uses EthernetRecoveryUI.
+cc_library_static {
+    name: "librecovery_ui_ethernet",
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "ethernet_device.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/recovery_ui/ethernet_device.cpp b/recovery_ui/ethernet_device.cpp
new file mode 100644
index 0000000..39ec65d
--- /dev/null
+++ b/recovery_ui/ethernet_device.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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 <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <linux/if.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "recovery_ui/device.h"
+#include "recovery_ui/ethernet_ui.h"
+
+class EthernetDevice : public Device {
+ public:
+  explicit EthernetDevice(EthernetRecoveryUI* ui);
+
+  void PreRecovery() override;
+  void PreFastboot() override;
+
+ private:
+  int SetInterfaceFlags(const unsigned set, const unsigned clr);
+  void SetTitleIPv6LinkLocalAddress(const bool interface_up);
+
+  android::base::unique_fd ctl_sock_;
+  static const std::string interface;
+};
+
+const std::string EthernetDevice::interface = "eth0";
+
+EthernetDevice::EthernetDevice(EthernetRecoveryUI* ui)
+    : Device(ui), ctl_sock_(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) {
+  if (ctl_sock_ < 0) {
+    PLOG(ERROR) << "Failed to open socket";
+  }
+}
+
+void EthernetDevice::PreRecovery() {
+  SetInterfaceFlags(0, IFF_UP);
+  SetTitleIPv6LinkLocalAddress(false);
+}
+
+void EthernetDevice::PreFastboot() {
+  android::base::SetProperty("fastbootd.protocol", "tcp");
+
+  if (SetInterfaceFlags(IFF_UP, 0) < 0) {
+    LOG(ERROR) << "Failed to bring up interface";
+    return;
+  }
+
+  SetTitleIPv6LinkLocalAddress(true);
+}
+
+int EthernetDevice::SetInterfaceFlags(const unsigned set, const unsigned clr) {
+  struct ifreq ifr;
+
+  if (ctl_sock_ < 0) {
+    return -1;
+  }
+
+  memset(&ifr, 0, sizeof(struct ifreq));
+  strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ - 1] = 0;
+
+  if (ioctl(ctl_sock_, SIOCGIFFLAGS, &ifr) < 0) {
+    PLOG(ERROR) << "Failed to get interface active flags";
+    return -1;
+  }
+  ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
+
+  if (ioctl(ctl_sock_, SIOCSIFFLAGS, &ifr) < 0) {
+    PLOG(ERROR) << "Failed to set interface active flags";
+    return -1;
+  }
+
+  return 0;
+}
+
+void EthernetDevice::SetTitleIPv6LinkLocalAddress(const bool interface_up) {
+  auto recovery_ui = reinterpret_cast<EthernetRecoveryUI*>(GetUI());
+  if (!interface_up) {
+    recovery_ui->SetIPv6LinkLocalAddress();
+    return;
+  }
+
+  struct ifaddrs* ifaddr;
+  if (getifaddrs(&ifaddr) == -1) {
+    PLOG(ERROR) << "Failed to get interface addresses";
+    recovery_ui->SetIPv6LinkLocalAddress();
+    return;
+  }
+
+  std::unique_ptr<struct ifaddrs, decltype(&freeifaddrs)> guard{ ifaddr, freeifaddrs };
+  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
+    if (ifa->ifa_addr->sa_family != AF_INET6 || interface != ifa->ifa_name) {
+      continue;
+    }
+
+    auto current_addr = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
+    if (!IN6_IS_ADDR_LINKLOCAL(&(current_addr->sin6_addr))) {
+      continue;
+    }
+
+    char addrstr[INET6_ADDRSTRLEN];
+    inet_ntop(AF_INET6, reinterpret_cast<const void*>(&current_addr->sin6_addr), addrstr,
+              INET6_ADDRSTRLEN);
+    LOG(INFO) << "Our IPv6 link-local address is " << addrstr;
+    recovery_ui->SetIPv6LinkLocalAddress(addrstr);
+    return;
+  }
+
+  recovery_ui->SetIPv6LinkLocalAddress();
+}
+
+// -----------------------------------------------------------------------------------------
+Device* make_device() {
+  return new EthernetDevice(new EthernetRecoveryUI);
+}
diff --git a/recovery_ui/ethernet_ui.cpp b/recovery_ui/ethernet_ui.cpp
new file mode 100644
index 0000000..535d407
--- /dev/null
+++ b/recovery_ui/ethernet_ui.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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 "recovery_ui/ethernet_ui.h"
+
+#include <android-base/logging.h>
+
+void EthernetRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
+  ScreenRecoveryUI::SetTitle(lines);
+
+  // Append IP address, if any
+  if (!address_.empty()) {
+    title_lines_.push_back("IPv6 link-local address - " + address_);
+  }
+}
+
+void EthernetRecoveryUI::SetIPv6LinkLocalAddress(const std::string& address) {
+  address_ = address;
+}
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index f4f9936..76166f0 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -79,10 +79,22 @@
     ui_.reset(ui);
   }
 
+  // Called before recovery mode started up, to perform whatever device-specific recovery mode
+  // preparation as needed.
+  virtual void PreRecovery() {}
+
   // Called when recovery starts up (after the UI has been obtained and initialized and after the
   // arguments have been parsed, but before anything else).
   virtual void StartRecovery() {}
 
+  // Called before fastboot mode is started up, to perform whatever device-specific fastboot mode
+  // preparation as needed.
+  virtual void PreFastboot() {}
+
+  // Called when fastboot starts up (after the UI has been obtained and initialized and after the
+  // arguments have been parsed, but before anything else).
+  virtual void StartFastboot() {}
+
   // Called from the main thread when recovery is at the main menu and waiting for input, and a key
   // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible;
   // recovery will be at the main menu with it invisible after an unsuccessful operation, such as
diff --git a/recovery_ui/include/recovery_ui/ethernet_ui.h b/recovery_ui/include/recovery_ui/ethernet_ui.h
new file mode 100644
index 0000000..f40c73f
--- /dev/null
+++ b/recovery_ui/include/recovery_ui/ethernet_ui.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef RECOVERY_ETHERNET_UI_H
+#define RECOVERY_ETHERNET_UI_H
+
+#include "screen_ui.h"
+
+class EthernetRecoveryUI : public ScreenRecoveryUI {
+ public:
+  EthernetRecoveryUI() {}
+  void SetTitle(const std::vector<std::string>& lines) override;
+
+  // For EthernetDevice
+  void SetIPv6LinkLocalAddress(const std::string& address = "");
+
+ private:
+  std::string address_;
+};
+
+#endif  // RECOVERY_ETHERNET_UI_H