Browse Source

recovery: Provide sideload cancellation

Change-Id: I13f0c9ae5444652a2141442ef24258679a78d320
Tom Marshall 2 years ago
parent
commit
c81eba62db
9 changed files with 185 additions and 89 deletions
  1. 78
    36
      adb_install.cpp
  2. 3
    1
      adb_install.h
  3. 28
    18
      fuse_sdcard_provider.cpp
  4. 21
    17
      fuse_sideload.cpp
  5. 0
    2
      fuse_sideload.h
  6. 22
    4
      install.cpp
  7. 22
    11
      recovery.cpp
  8. 8
    0
      ui.cpp
  9. 3
    0
      ui.h

+ 78
- 36
adb_install.cpp View File

@@ -36,6 +36,8 @@
36 36
 #include "install.h"
37 37
 #include "ui.h"
38 38
 
39
+static pthread_t sideload_thread;
40
+
39 41
 static void set_usb_driver(bool enabled) {
40 42
   // USB configfs doesn't use /s/c/a/a/enable.
41 43
   if (android::base::GetBoolProperty("sys.usb.configfs", false)) {
@@ -70,64 +72,75 @@ static void maybe_restart_adbd() {
70 72
   }
71 73
 }
72 74
 
73
-int apply_from_adb(bool* wipe_cache, const char* install_file) {
74
-  modified_flash = true;
75
-
76
-  stop_adbd();
77
-  set_usb_driver(true);
75
+struct sideload_data {
76
+    bool*       wipe_cache;
77
+    const char* install_file;
78
+    bool        cancel;
79
+    int         result;
80
+};
78 81
 
79
-  ui->Print(
80
-      "\n\nNow send the package you want to apply\n"
81
-      "to the device with \"adb sideload <filename>\"...\n");
82
+static struct sideload_data sideload_data;
82 83
 
84
+static void *adb_sideload_thread(void*) {
83 85
   pid_t child;
84 86
   if ((child = fork()) == 0) {
85 87
     execl("/sbin/recovery", "recovery", "--adbd", nullptr);
86 88
     _exit(EXIT_FAILURE);
87 89
   }
88 90
 
91
+  time_t start_time = time(nullptr);
92
+  time_t now = start_time;
93
+
89 94
   // How long (in seconds) we wait for the host to start sending us a package, before timing out.
90 95
   static constexpr int ADB_INSTALL_TIMEOUT = 300;
91 96
 
92 97
   // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host connects and starts serving a
93 98
   // package. Poll for its appearance. (Note that inotify doesn't work with FUSE.)
94
-  int result = INSTALL_ERROR;
95
-  int status;
96
-  bool waited = false;
97
-  for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
98
-    if (waitpid(child, &status, WNOHANG) != 0) {
99
+  int result = INSTALL_NONE;
100
+  int status = -1;
101
+  while (now - start_time < ADB_INSTALL_TIMEOUT) {
102
+    // Exit if either:
103
+    //  - The adb child process dies, or
104
+    //  - The ui tells us to cancel
105
+    if (kill(child, 0) != 0) {
99 106
       result = INSTALL_ERROR;
100
-      waited = true;
107
+      break;
108
+    }
109
+    if (sideload_data.cancel) {
101 110
       break;
102 111
     }
103 112
 
104 113
     struct stat st;
105
-    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
106
-      if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
107
-        sleep(1);
108
-        continue;
109
-      } else {
110
-        ui->Print("\nTimed out waiting for package.\n\n");
111
-        result = INSTALL_ERROR;
112
-        kill(child, SIGKILL);
113
-        break;
114
-      }
114
+    status = stat(FUSE_SIDELOAD_HOST_PATHNAME, &st);
115
+    if (status == 0) {
116
+      break;
117
+    }
118
+    if (errno != ENOENT && errno != ENOTCONN) {
119
+      ui->Print("\nError %s waiting for package\n\n", strerror(errno));
120
+      result = INSTALL_ERROR;
121
+      break;
115 122
     }
116
-    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false, 0);
117
-    break;
123
+
124
+    sleep(1);
125
+    now = time(nullptr);
118 126
   }
119 127
 
120
-  if (!waited) {
121
-    // Calling stat() on this magic filename signals the minadbd subprocess to shut down.
122
-    struct stat st;
123
-    stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
128
+  if (status == 0) {
129
+    // Signal UI thread that we can no longer cancel
130
+    ui->CancelWaitKey();
124 131
 
125
-    // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on
126
-    // the device). For now you just have to 'adb sideload' a file that's not a valid package, like
127
-    // "/dev/null".
128
-    waitpid(child, &status, 0);
132
+    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME,
133
+                             sideload_data.wipe_cache,
134
+                             sideload_data.install_file,
135
+                             false, 0);
136
+
137
+    sideload_data.result = result;
129 138
   }
130 139
 
140
+  // Ensure adb exits
141
+  kill(child, SIGTERM);
142
+  waitpid(child, &status, 0);
143
+
131 144
   if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
132 145
     if (WEXITSTATUS(status) == 3) {
133 146
       ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
@@ -136,8 +149,37 @@ int apply_from_adb(bool* wipe_cache, const char* install_file) {
136 149
     }
137 150
   }
138 151
 
139
-  set_usb_driver(false);
152
+  return nullptr;
153
+}
154
+
155
+void
156
+start_sideload(bool* wipe_cache, const char* install_file) {
157
+  modified_flash = true;
158
+
159
+  stop_adbd();
160
+  set_usb_driver(true);
161
+
162
+  ui->Print("\n\nNow send the package you want to apply\n"
163
+            "to the device with \"adb sideload <filename>\"...\n");
164
+
165
+  sideload_data.wipe_cache = wipe_cache;
166
+  sideload_data.install_file = install_file;
167
+  sideload_data.cancel = false;
168
+  sideload_data.result = INSTALL_NONE;
169
+
170
+  pthread_create(&sideload_thread, nullptr, &adb_sideload_thread, nullptr);
171
+}
172
+
173
+void stop_sideload() {
174
+  sideload_data.cancel = true;
175
+}
176
+
177
+int wait_sideload() {
178
+  pthread_join(sideload_thread, nullptr);
179
+
180
+  ui->FlushKeys();
181
+
140 182
   maybe_restart_adbd();
141 183
 
142
-  return result;
184
+  return sideload_data.result;
143 185
 }

+ 3
- 1
adb_install.h View File

@@ -17,6 +17,8 @@
17 17
 #ifndef _ADB_INSTALL_H
18 18
 #define _ADB_INSTALL_H
19 19
 
20
-int apply_from_adb(bool* wipe_cache, const char* install_file);
20
+void start_sideload(bool* wipe_cache, const char* install_file);
21
+void stop_sideload();
22
+int  wait_sideload();
21 23
 
22 24
 #endif

+ 28
- 18
fuse_sdcard_provider.cpp View File

@@ -24,6 +24,7 @@
24 24
 #include <pthread.h>
25 25
 #include <sys/mount.h>
26 26
 #include <sys/stat.h>
27
+#include <sys/wait.h>
27 28
 #include <unistd.h>
28 29
 
29 30
 #include <functional>
@@ -56,7 +57,7 @@ static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer,
56 57
 }
57 58
 
58 59
 struct token {
59
-    pthread_t th;
60
+    pid_t pid;
60 61
     const char* path;
61 62
     int result;
62 63
 };
@@ -99,19 +100,30 @@ void* start_sdcard_fuse(const char* path) {
99 100
   token* t = new token;
100 101
 
101 102
   t->path = path;
102
-  pthread_create(&(t->th), NULL, run_sdcard_fuse, t);
103
-
104
-  struct stat st;
105
-  int i;
106
-  for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
107
-    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
108
-      if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
109
-        sleep(1);
110
-        continue;
111
-      } else {
112
-        return nullptr;
113
-      }
103
+  if ((t->pid = fork()) < 0) {
104
+    free(t);
105
+    return nullptr;
106
+  }
107
+  if (t->pid == 0) {
108
+    run_sdcard_fuse(t);
109
+    _exit(0);
110
+  }
111
+
112
+  time_t start_time = time(nullptr);
113
+  time_t now = start_time;
114
+
115
+  while (now - start_time < SDCARD_INSTALL_TIMEOUT) {
116
+    struct stat st;
117
+    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) == 0) {
118
+      break;
119
+    }
120
+    if (errno != ENOENT && errno != ENOTCONN) {
121
+      free(t);
122
+      t = nullptr;
123
+      break;
114 124
     }
125
+    sleep(1);
126
+    now = time(nullptr);
115 127
   }
116 128
 
117 129
   return t;
@@ -121,11 +133,9 @@ void finish_sdcard_fuse(void* cookie) {
121 133
   if (cookie == NULL) return;
122 134
   token* t = reinterpret_cast<token*>(cookie);
123 135
 
124
-  // Calling stat() on this magic filename signals the fuse
125
-  // filesystem to shut down.
126
-  struct stat st;
127
-  stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
136
+  kill(t->pid, SIGTERM);
137
+  int status;
138
+  waitpid(t->pid, &status, 0);
128 139
 
129
-  pthread_join(t->th, nullptr);
130 140
   delete t;
131 141
 }

+ 21
- 17
fuse_sideload.cpp View File

@@ -66,10 +66,8 @@
66 66
 #include <openssl/sha.h>
67 67
 
68 68
 static constexpr uint64_t PACKAGE_FILE_ID = FUSE_ROOT_ID + 1;
69
-static constexpr uint64_t EXIT_FLAG_ID = FUSE_ROOT_ID + 2;
70 69
 
71 70
 static constexpr int NO_STATUS = 1;
72
-static constexpr int NO_STATUS_EXIT = 2;
73 71
 
74 72
 using SHA256Digest = std::array<uint8_t, SHA256_DIGEST_LENGTH>;
75 73
 
@@ -171,14 +169,12 @@ static int handle_getattr(void* /* data */, const fuse_data* fd, const fuse_in_h
171 169
     fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
172 170
   } else if (hdr->nodeid == PACKAGE_FILE_ID) {
173 171
     fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
174
-  } else if (hdr->nodeid == EXIT_FLAG_ID) {
175
-    fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
176 172
   } else {
177 173
     return -ENOENT;
178 174
   }
179 175
 
180 176
   fuse_reply(fd, hdr->unique, &out, sizeof(out));
181
-  return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
177
+  return NO_STATUS;
182 178
 }
183 179
 
184 180
 static int handle_lookup(void* data, const fuse_data* fd, const fuse_in_header* hdr) {
@@ -193,20 +189,15 @@ static int handle_lookup(void* data, const fuse_data* fd, const fuse_in_header*
193 189
     out.nodeid = PACKAGE_FILE_ID;
194 190
     out.generation = PACKAGE_FILE_ID;
195 191
     fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
196
-  } else if (filename == FUSE_SIDELOAD_HOST_EXIT_FLAG) {
197
-    out.nodeid = EXIT_FLAG_ID;
198
-    out.generation = EXIT_FLAG_ID;
199
-    fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
200 192
   } else {
201 193
     return -ENOENT;
202 194
   }
203 195
 
204 196
   fuse_reply(fd, hdr->unique, &out, sizeof(out));
205
-  return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
197
+  return NO_STATUS;
206 198
 }
207 199
 
208 200
 static int handle_open(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) {
209
-  if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
210 201
   if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
211 202
 
212 203
   fuse_open_out out = {};
@@ -340,6 +331,12 @@ static int handle_read(void* data, fuse_data* fd, const fuse_in_header* hdr) {
340 331
   return NO_STATUS;
341 332
 }
342 333
 
334
+static volatile int terminated = 0;
335
+static void sig_term(int)
336
+{
337
+  terminated = 1;
338
+}
339
+
343 340
 int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
344 341
                       const char* mount_point) {
345 342
   // If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a
@@ -388,6 +385,8 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
388 385
     goto done;
389 386
   }
390 387
 
388
+  signal(SIGTERM, sig_term);
389
+
391 390
   fd.ffd.reset(open("/dev/fuse", O_RDWR));
392 391
   if (!fd.ffd) {
393 392
     perror("open /dev/fuse");
@@ -409,7 +408,17 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
409 408
   }
410 409
 
411 410
   uint8_t request_buffer[sizeof(fuse_in_header) + PATH_MAX * 8];
412
-  for (;;) {
411
+  while (!terminated) {
412
+    fd_set fds;
413
+    struct timeval tv;
414
+    FD_ZERO(&fds);
415
+    FD_SET(fd.ffd, &fds);
416
+    tv.tv_sec = 1;
417
+    tv.tv_usec = 0;
418
+    int rc = select(fd.ffd+1, &fds, nullptr, nullptr, &tv);
419
+    if (rc <= 0) {
420
+      continue;
421
+    }
413 422
     ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
414 423
     if (len == -1) {
415 424
       perror("read request");
@@ -464,11 +473,6 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
464 473
         break;
465 474
     }
466 475
 
467
-    if (result == NO_STATUS_EXIT) {
468
-      result = 0;
469
-      break;
470
-    }
471
-
472 476
     if (result != NO_STATUS) {
473 477
       fuse_out_header outhdr;
474 478
       outhdr.len = sizeof(outhdr);

+ 0
- 2
fuse_sideload.h View File

@@ -23,8 +23,6 @@
23 23
 static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload";
24 24
 static constexpr const char* FUSE_SIDELOAD_HOST_FILENAME = "package.zip";
25 25
 static constexpr const char* FUSE_SIDELOAD_HOST_PATHNAME = "/sideload/package.zip";
26
-static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit";
27
-static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit";
28 26
 
29 27
 struct provider_vtab {
30 28
   // read a block

+ 22
- 4
install.cpp View File

@@ -25,6 +25,7 @@
25 25
 #include <sys/stat.h>
26 26
 #include <sys/wait.h>
27 27
 #include <unistd.h>
28
+#include <setjmp.h>
28 29
 
29 30
 #include <algorithm>
30 31
 #include <atomic>
@@ -311,6 +312,12 @@ static void log_max_temperature(int* max_temperature, const std::atomic<bool>& l
311 312
   }
312 313
 }
313 314
 
315
+static jmp_buf jb;
316
+static void sig_bus(int)
317
+{
318
+    longjmp(jb, 1);
319
+}
320
+
314 321
 // If the package contains an update binary, extract it and run it.
315 322
 static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
316 323
                              std::vector<std::string>* log_buffer, int retry_count,
@@ -732,10 +739,21 @@ bool verify_package(const unsigned char* package_data, size_t package_size) {
732 739
   // Verify package.
733 740
   ui->Print("Verifying update package...\n");
734 741
   auto t0 = std::chrono::system_clock::now();
735
-  int err = verify_file(package_data, package_size, loadedKeys,
736
-                        std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
737
-  std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
738
-  ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
742
+  int err;
743
+  // Because we mmap() the update file which is backed by FUSE, we get
744
+  // SIGBUS when the host aborts the transfer.  We handle this by using
745
+  // setjmp/longjmp.
746
+  signal(SIGBUS, sig_bus);
747
+  if (setjmp(jb) == 0) {
748
+    err = verify_file(package_data, package_size, loadedKeys,
749
+                      std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
750
+    std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
751
+    ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
752
+  } else {
753
+    err = VERIFY_FAILURE;
754
+  }
755
+  signal(SIGBUS, SIG_DFL);
756
+
739 757
   if (err != VERIFY_SUCCESS) {
740 758
     LOG(ERROR) << "Signature verification failed";
741 759
     LOG(ERROR) << "error: " << kZipVerificationFailure;

+ 22
- 11
recovery.cpp View File

@@ -1,4 +1,4 @@
1
-/*
1
+ /*
2 2
  * Copyright (C) 2007 The Android Open Source Project
3 3
  *
4 4
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -1239,7 +1239,15 @@ refresh:
1239 1239
             break;
1240 1240
         }
1241 1241
         if (chosen == item_sideload) {
1242
-            status = apply_from_adb(wipe_cache, TEMPORARY_INSTALL_FILE);
1242
+            static const char* headers[] = { "ADB Sideload", nullptr };
1243
+            static const char* list[] = { "Cancel sideload", nullptr };
1244
+
1245
+            start_sideload(wipe_cache, TEMPORARY_INSTALL_FILE);
1246
+            int item = get_menu_selection(headers, list, 0, 0, device);
1247
+            if (item != Device::kNoAction) {
1248
+                stop_sideload();
1249
+            }
1250
+            status = wait_sideload();
1243 1251
         }
1244 1252
         else {
1245 1253
             status = apply_from_storage(device, volumes[chosen - 1], wipe_cache);
@@ -1316,14 +1324,16 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
1316 1324
             }
1317 1325
           }
1318 1326
 
1319
-          if (status != INSTALL_SUCCESS) {
1320
-            ui->SetBackground(RecoveryUI::ERROR);
1321
-            ui->Print("Installation aborted.\n");
1322
-            copy_logs();
1323
-          } else if (!ui->IsTextVisible()) {
1324
-            return Device::NO_ACTION;  // reboot if logs aren't visible
1325
-          } else {
1326
-            ui->Print("\nInstall complete.\n");
1327
+          if (status > 0 && status != INSTALL_NONE) {
1328
+            if (status != INSTALL_SUCCESS) {
1329
+              ui->SetBackground(RecoveryUI::ERROR);
1330
+              ui->Print("Installation aborted.\n");
1331
+              copy_logs();
1332
+            } else if (!ui->IsTextVisible()) {
1333
+              return Device::NO_ACTION;  // reboot if logs aren't visible
1334
+            } else {
1335
+              ui->Print("\nInstall complete.\n");
1336
+            }
1327 1337
           }
1328 1338
         }
1329 1339
         break;
@@ -1871,7 +1881,8 @@ int main(int argc, char **argv) {
1871 1881
     if (!sideload_auto_reboot) {
1872 1882
       ui->ShowText(true);
1873 1883
     }
1874
-    status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE);
1884
+    start_sideload(&should_wipe_cache, TEMPORARY_INSTALL_FILE);
1885
+    status = wait_sideload();
1875 1886
     if (status == INSTALL_SUCCESS && should_wipe_cache) {
1876 1887
       if (!wipe_cache(false, device)) {
1877 1888
         status = INSTALL_ERROR;

+ 8
- 0
ui.cpp View File

@@ -560,6 +560,14 @@ int RecoveryUI::WaitKey() {
560 560
   return key;
561 561
 }
562 562
 
563
+void RecoveryUI::CancelWaitKey()
564
+{
565
+    pthread_mutex_lock(&key_queue_mutex);
566
+    key_queue[key_queue_len++] = KEY_REFRESH;
567
+    pthread_cond_signal(&key_queue_cond);
568
+    pthread_mutex_unlock(&key_queue_mutex);
569
+}
570
+
563 571
 bool RecoveryUI::IsUsbConnected() {
564 572
   int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
565 573
   if (fd < 0) {

+ 3
- 0
ui.h View File

@@ -134,6 +134,9 @@ class RecoveryUI {
134 134
   // Waits for a key and return it. May return -1 after timeout.
135 135
   virtual int WaitKey();
136 136
 
137
+    // Cancel a WaitKey()
138
+    virtual void CancelWaitKey();
139
+
137 140
   virtual bool IsKeyPressed(int key);
138 141
   virtual bool IsLongPress();
139 142