1 //
2 // Copyright (C) 2019 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <getopt.h>
18 #include <stdio.h>
19 #include <sysexits.h>
20 #include <unistd.h>
21 
22 #include <chrono>
23 #include <condition_variable>
24 #include <functional>
25 #include <iostream>
26 #include <map>
27 #include <mutex>
28 #include <string>
29 #include <thread>
30 
31 #include <android-base/logging.h>
32 #include <android-base/parseint.h>
33 #include <android-base/properties.h>
34 #include <android-base/stringprintf.h>
35 #include <android-base/strings.h>
36 #include <android-base/unique_fd.h>
37 #include <android/gsi/IGsiService.h>
38 #include <cutils/android_reboot.h>
39 #include <libgsi/libgsi.h>
40 #include <libgsi/libgsid.h>
41 
42 using namespace android::gsi;
43 using namespace std::chrono_literals;
44 
45 using android::sp;
46 using android::base::Split;
47 using android::base::StringPrintf;
48 using CommandCallback = std::function<int(sp<IGsiService>, int, char**)>;
49 
50 static int Disable(sp<IGsiService> gsid, int argc, char** argv);
51 static int Enable(sp<IGsiService> gsid, int argc, char** argv);
52 static int Install(sp<IGsiService> gsid, int argc, char** argv);
53 static int CreatePartition(sp<IGsiService> gsid, int argc, char** argv);
54 static int Wipe(sp<IGsiService> gsid, int argc, char** argv);
55 static int WipeData(sp<IGsiService> gsid, int argc, char** argv);
56 static int Status(sp<IGsiService> gsid, int argc, char** argv);
57 static int Cancel(sp<IGsiService> gsid, int argc, char** argv);
58 
59 static const std::map<std::string, CommandCallback> kCommandMap = {
60         // clang-format off
61         {"disable", Disable},
62         {"enable", Enable},
63         {"install", Install},
64         {"create-partition", CreatePartition},
65         {"wipe", Wipe},
66         {"wipe-data", WipeData},
67         {"status", Status},
68         {"cancel", Cancel},
69         // clang-format on
70 };
71 
ErrorMessage(const android::binder::Status & status,int error_code=IGsiService::INSTALL_ERROR_GENERIC)72 static std::string ErrorMessage(const android::binder::Status& status,
73                                 int error_code = IGsiService::INSTALL_ERROR_GENERIC) {
74     if (!status.isOk()) {
75         return status.exceptionMessage().string();
76     }
77     return "error code " + std::to_string(error_code);
78 }
79 
80 class ProgressBar {
81   public:
ProgressBar(sp<IGsiService> gsid)82     explicit ProgressBar(sp<IGsiService> gsid) : gsid_(gsid) {}
83 
~ProgressBar()84     ~ProgressBar() { Stop(); }
85 
Display()86     void Display() {
87         Finish();
88         done_ = false;
89         last_update_ = {};
90         worker_ = std::make_unique<std::thread>([this]() { Worker(); });
91     }
92 
Stop()93     void Stop() {
94         if (!worker_) {
95             return;
96         }
97         SignalDone();
98         worker_->join();
99         worker_ = nullptr;
100     }
101 
Finish()102     void Finish() {
103         if (!worker_) {
104             return;
105         }
106         Stop();
107         FinishLastBar();
108     }
109 
110   private:
Worker()111     void Worker() {
112         std::unique_lock<std::mutex> lock(mutex_);
113         while (!done_) {
114             if (!UpdateProgress()) {
115                 return;
116             }
117             cv_.wait_for(lock, 500ms, [this] { return done_; });
118         }
119     }
120 
UpdateProgress()121     bool UpdateProgress() {
122         GsiProgress latest;
123         auto status = gsid_->getInstallProgress(&latest);
124         if (!status.isOk()) {
125             std::cout << std::endl;
126             return false;
127         }
128         if (latest.status == IGsiService::STATUS_NO_OPERATION) {
129             return true;
130         }
131         if (last_update_.step != latest.step) {
132             FinishLastBar();
133         }
134         Display(latest);
135         return true;
136     }
137 
FinishLastBar()138     void FinishLastBar() {
139         // If no bar was in progress, don't do anything.
140         if (last_update_.total_bytes == 0) {
141             return;
142         }
143         // Ensure we finish the display at 100%.
144         last_update_.bytes_processed = last_update_.total_bytes;
145         Display(last_update_);
146         std::cout << std::endl;
147     }
148 
Display(const GsiProgress & progress)149     void Display(const GsiProgress& progress) {
150         if (progress.total_bytes == 0) {
151             return;
152         }
153 
154         static constexpr int kColumns = 80;
155         static constexpr char kRedColor[] = "\x1b[31m";
156         static constexpr char kGreenColor[] = "\x1b[32m";
157         static constexpr char kResetColor[] = "\x1b[0m";
158 
159         int percentage = (progress.bytes_processed * 100) / progress.total_bytes;
160         int64_t bytes_per_col = progress.total_bytes / kColumns;
161         uint32_t fill_count = progress.bytes_processed / bytes_per_col;
162         uint32_t dash_count = kColumns - fill_count;
163         std::string fills = std::string(fill_count, '=');
164         std::string dashes = std::string(dash_count, '-');
165 
166         // Give the end of the bar some flare.
167         if (!fills.empty() && !dashes.empty()) {
168             fills[fills.size() - 1] = '>';
169         }
170 
171         fprintf(stdout, "\r%-15s%6d%% ", progress.step.c_str(), percentage);
172         fprintf(stdout, "%s[%s%s%s", kGreenColor, fills.c_str(), kRedColor, dashes.c_str());
173         fprintf(stdout, "%s]%s", kGreenColor, kResetColor);
174         fflush(stdout);
175 
176         last_update_ = progress;
177     }
178 
SignalDone()179     void SignalDone() {
180         std::lock_guard<std::mutex> guard(mutex_);
181         done_ = true;
182         cv_.notify_all();
183     }
184 
185   private:
186     sp<IGsiService> gsid_;
187     std::unique_ptr<std::thread> worker_;
188     std::condition_variable cv_;
189     std::mutex mutex_;
190     GsiProgress last_update_;
191     bool done_ = false;
192 };
193 
Install(sp<IGsiService> gsid,int argc,char ** argv)194 static int Install(sp<IGsiService> gsid, int argc, char** argv) {
195     constexpr const char* kDefaultPartition = "system";
196     struct option options[] = {
197             {"install-dir", required_argument, nullptr, 'i'},
198             {"gsi-size", required_argument, nullptr, 's'},
199             {"no-reboot", no_argument, nullptr, 'n'},
200             {"userdata-size", required_argument, nullptr, 'u'},
201             {"partition-name", required_argument, nullptr, 'p'},
202             {"wipe", no_argument, nullptr, 'w'},
203             {nullptr, 0, nullptr, 0},
204     };
205 
206     int64_t gsiSize = 0;
207     int64_t userdataSize = 0;
208     bool wipeUserdata = false;
209     bool reboot = true;
210     std::string installDir = "";
211     std::string partition = kDefaultPartition;
212     if (getuid() != 0) {
213         std::cerr << "must be root to install a GSI" << std::endl;
214         return EX_NOPERM;
215     }
216 
217     int rv, index;
218     while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
219         switch (rv) {
220             case 'p':
221                 partition = optarg;
222                 break;
223             case 's':
224                 if (!android::base::ParseInt(optarg, &gsiSize) || gsiSize <= 0) {
225                     std::cerr << "Could not parse image size: " << optarg << std::endl;
226                     return EX_USAGE;
227                 }
228                 break;
229             case 'u':
230                 if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) {
231                     std::cerr << "Could not parse image size: " << optarg << std::endl;
232                     return EX_USAGE;
233                 }
234                 break;
235             case 'i':
236                 installDir = optarg;
237                 break;
238             case 'w':
239                 wipeUserdata = true;
240                 break;
241             case 'n':
242                 reboot = false;
243                 break;
244         }
245     }
246 
247     if (gsiSize <= 0) {
248         std::cerr << "Must specify --gsi-size." << std::endl;
249         return EX_USAGE;
250     }
251 
252     bool running_gsi = false;
253     gsid->isGsiRunning(&running_gsi);
254     if (running_gsi) {
255         std::cerr << "Cannot install a GSI within a live GSI." << std::endl;
256         std::cerr << "Use gsi_tool disable or wipe and reboot first." << std::endl;
257         return EX_SOFTWARE;
258     }
259 
260     android::base::unique_fd input(dup(STDIN_FILENO));
261     if (input < 0) {
262         std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
263         return EX_SOFTWARE;
264     }
265     // Note: the progress bar needs to be re-started in between each call.
266     ProgressBar progress(gsid);
267     progress.Display();
268     int error;
269     auto status = gsid->openInstall(installDir, &error);
270     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
271         std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error) << "\n";
272         return EX_SOFTWARE;
273     }
274     if (partition == kDefaultPartition) {
275         auto status = gsid->createPartition("userdata", userdataSize, false, &error);
276         if (!status.isOk() || error != IGsiService::INSTALL_OK) {
277             std::cerr << "Could not start live image install: " << ErrorMessage(status, error)
278                       << "\n";
279             return EX_SOFTWARE;
280         }
281         status = gsid->closePartition(&error);
282         if (!status.isOk() || error != IGsiService::INSTALL_OK) {
283             std::cerr << "Could not closePartition(userdata): " << ErrorMessage(status, error)
284                       << std::endl;
285             return EX_SOFTWARE;
286         }
287     }
288 
289     status = gsid->createPartition(partition, gsiSize, true, &error);
290     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
291         std::cerr << "Could not start live image install: " << ErrorMessage(status, error) << "\n";
292         return EX_SOFTWARE;
293     }
294     android::os::ParcelFileDescriptor stream(std::move(input));
295 
296     bool ok = false;
297     progress.Display();
298     status = gsid->commitGsiChunkFromStream(stream, gsiSize, &ok);
299     if (!ok) {
300         std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n";
301         return EX_SOFTWARE;
302     }
303 
304     status = gsid->closePartition(&error);
305     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
306         std::cerr << "Could not closePartition(" << partition
307                   << "): " << ErrorMessage(status, error) << std::endl;
308         return EX_SOFTWARE;
309     }
310 
311     status = gsid->closeInstall(&error);
312     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
313         std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error) << "\n";
314         return EX_SOFTWARE;
315     }
316     progress.Finish();
317     std::string dsuSlot;
318     status = gsid->getActiveDsuSlot(&dsuSlot);
319     if (!status.isOk()) {
320         std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
321         return EX_SOFTWARE;
322     }
323     status = gsid->enableGsi(true, dsuSlot, &error);
324     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
325         std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
326         return EX_SOFTWARE;
327     }
328 
329     if (reboot) {
330         if (!android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,adb")) {
331             std::cerr << "Failed to reboot automatically" << std::endl;
332             return EX_SOFTWARE;
333         }
334     } else {
335         std::cout << "Please reboot to use the GSI." << std::endl;
336     }
337     return 0;
338 }
339 
340 // Experimental API
CreatePartition(sp<IGsiService> gsid,int argc,char ** argv)341 static int CreatePartition(sp<IGsiService> gsid, int argc, char** argv) {
342     std::string installDir;
343     std::string partitionName;
344     bool readOnly = true;
345     int64_t partitionSize = 0;
346 
347     struct option options[] = {
348             {"install-dir", required_argument, nullptr, 'i'},
349             {"partition-name", required_argument, nullptr, 'p'},
350             {"readwrite", no_argument, nullptr, 'r'},
351             {"size", required_argument, nullptr, 's'},
352             {nullptr, 0, nullptr, 0},
353     };
354 
355     int rv = 0;
356     while ((rv = getopt_long_only(argc, argv, "", options, nullptr)) != -1) {
357         switch (rv) {
358             case 'i':
359                 installDir = optarg;
360                 break;
361             case 'p':
362                 partitionName = optarg;
363                 break;
364             case 'r':
365                 readOnly = false;
366                 break;
367             case 's':
368                 if (!android::base::ParseInt(optarg, &partitionSize)) {
369                     std::cerr << "Could not parse partition size: " << optarg << std::endl;
370                     return EX_USAGE;
371                 }
372                 break;
373             default:
374                 return EX_USAGE;
375         }
376     }
377 
378     if (getuid() != 0) {
379         std::cerr << "must be root to install a DSU" << std::endl;
380         return EX_NOPERM;
381     }
382 
383     bool gsiRunning = false;
384     auto status = gsid->isGsiRunning(&gsiRunning);
385     if (!status.isOk()) {
386         std::cerr << "Could not get DSU running status: " << ErrorMessage(status) << std::endl;
387         return EX_SOFTWARE;
388     }
389     if (gsiRunning) {
390         std::cerr << "Could not install DSU within an active DSU." << std::endl;
391         return EX_SOFTWARE;
392     }
393 
394     if (partitionSize <= 0) {
395         std::cerr << "Partition size must be greater than zero: " << partitionSize << std::endl;
396         return EX_USAGE;
397     }
398 
399     // Note: the progress bar needs to be re-started in between each call.
400     ProgressBar progress(gsid);
401     progress.Display();
402 
403     int error;
404     status = gsid->openInstall(installDir, &error);
405     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
406         std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error)
407                   << std::endl;
408         return EX_SOFTWARE;
409     }
410 
411     status = gsid->createPartition(partitionName, partitionSize, readOnly, &error);
412     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
413         std::cerr << "Could not create DSU partition: " << ErrorMessage(status, error) << std::endl;
414         return EX_SOFTWARE;
415     }
416 
417     if (readOnly) {
418         android::base::unique_fd input(dup(STDIN_FILENO));
419         if (input < 0) {
420             std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
421             return EX_SOFTWARE;
422         }
423         android::os::ParcelFileDescriptor stream(std::move(input));
424 
425         bool ok = false;
426         status = gsid->commitGsiChunkFromStream(stream, partitionSize, &ok);
427         if (!ok) {
428             std::cerr << "Could not commit data from stdin: " << ErrorMessage(status) << std::endl;
429             return EX_SOFTWARE;
430         }
431     }
432 
433     status = gsid->closePartition(&error);
434     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
435         std::cerr << "Could not close DSU partition:" << ErrorMessage(status, error) << std::endl;
436         return EX_SOFTWARE;
437     }
438 
439     status = gsid->closeInstall(&error);
440     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
441         std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error)
442                   << std::endl;
443         return EX_SOFTWARE;
444     }
445 
446     progress.Finish();
447 
448     std::string dsuSlot;
449     status = gsid->getActiveDsuSlot(&dsuSlot);
450     if (!status.isOk()) {
451         std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << std::endl;
452         return EX_SOFTWARE;
453     }
454 
455     // Immediately enable DSU after a partition is installed to ensure the installation status file
456     // is created.
457     status = gsid->enableGsi(/* one_shot = */ true, dsuSlot, &error);
458     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
459         std::cerr << "Could not make DSU bootable: " << ErrorMessage(status, error) << std::endl;
460         return EX_SOFTWARE;
461     }
462 
463     std::cout << "Enabled DSU slot: " << dsuSlot << std::endl;
464     std::cout << "Please reboot to use the DSU." << std::endl;
465     return 0;
466 }
467 
Wipe(sp<IGsiService> gsid,int argc,char **)468 static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) {
469     if (argc > 1) {
470         std::cerr << "Unrecognized arguments to wipe." << std::endl;
471         return EX_USAGE;
472     }
473     bool ok;
474     auto status = gsid->removeGsi(&ok);
475     if (!status.isOk() || !ok) {
476         std::cerr << "Could not remove GSI install: " << ErrorMessage(status) << "\n";
477         return EX_SOFTWARE;
478     }
479 
480     bool running = false;
481     if (gsid->isGsiRunning(&running).isOk() && running) {
482         std::cout << "Live image install will be removed next reboot." << std::endl;
483     } else {
484         std::cout << "Live image install successfully removed." << std::endl;
485     }
486     return 0;
487 }
488 
WipeData(sp<IGsiService> gsid,int argc,char **)489 static int WipeData(sp<IGsiService> gsid, int argc, char** /* argv */) {
490     if (argc > 1) {
491         std::cerr << "Unrecognized arguments to wipe-data.\n";
492         return EX_USAGE;
493     }
494 
495     bool running;
496     auto status = gsid->isGsiRunning(&running);
497     if (!status.isOk()) {
498         std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
499         return EX_SOFTWARE;
500     }
501     if (running) {
502         std::cerr << "Cannot wipe GSI userdata while running a GSI.\n";
503         return EX_USAGE;
504     }
505 
506     bool installed;
507     status = gsid->isGsiInstalled(&installed);
508     if (!status.isOk()) {
509         std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
510         return EX_SOFTWARE;
511     }
512     if (!installed) {
513         std::cerr << "No GSI is installed.\n";
514         return EX_USAGE;
515     }
516 
517     int error;
518     status = gsid->zeroPartition("userdata" + std::string(kDsuPostfix), &error);
519     if (!status.isOk() || error) {
520         std::cerr << "Could not wipe GSI userdata: " << ErrorMessage(status, error) << "\n";
521         return EX_SOFTWARE;
522     }
523     return 0;
524 }
525 
Status(sp<IGsiService> gsid,int argc,char **)526 static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) {
527     if (argc > 1) {
528         std::cerr << "Unrecognized arguments to status." << std::endl;
529         return EX_USAGE;
530     }
531     bool running;
532     auto status = gsid->isGsiRunning(&running);
533     if (!status.isOk()) {
534         std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
535         return EX_SOFTWARE;
536     } else if (running) {
537         std::cout << "running" << std::endl;
538     }
539     bool installed;
540     status = gsid->isGsiInstalled(&installed);
541     if (!status.isOk()) {
542         std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
543         return EX_SOFTWARE;
544     } else if (installed) {
545         std::cout << "installed" << std::endl;
546     }
547     bool enabled;
548     status = gsid->isGsiEnabled(&enabled);
549     if (!status.isOk()) {
550         std::cerr << status.exceptionMessage().string() << std::endl;
551         return EX_SOFTWARE;
552     } else if (running || installed) {
553         std::cout << (enabled ? "enabled" : "disabled") << std::endl;
554     } else {
555         std::cout << "normal" << std::endl;
556     }
557     if (getuid() != 0) {
558         return 0;
559     }
560 
561     std::vector<std::string> dsu_slots;
562     status = gsid->getInstalledDsuSlots(&dsu_slots);
563     if (!status.isOk()) {
564         std::cerr << status.exceptionMessage().string() << std::endl;
565         return EX_SOFTWARE;
566     }
567     int n = 0;
568     for (auto&& dsu_slot : dsu_slots) {
569         std::cout << "[" << n++ << "] " << dsu_slot << std::endl;
570         sp<IImageService> image_service = nullptr;
571         status = gsid->openImageService("dsu/" + dsu_slot + "/", &image_service);
572         if (!status.isOk()) {
573             std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
574             return EX_SOFTWARE;
575         }
576         std::vector<std::string> images;
577         status = image_service->getAllBackingImages(&images);
578         if (!status.isOk()) {
579             std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
580             return EX_SOFTWARE;
581         }
582         for (auto&& image : images) {
583             std::cout << "installed: " << image << std::endl;
584             AvbPublicKey public_key;
585             int err = 0;
586             status = image_service->getAvbPublicKey(image, &public_key, &err);
587             std::cout << "AVB public key (sha1): ";
588             if (!public_key.bytes.empty()) {
589                 for (auto b : public_key.sha1) {
590                     std::cout << StringPrintf("%02x", b & 255);
591                 }
592                 std::cout << std::endl;
593             } else {
594                 std::cout << "[NONE]" << std::endl;
595             }
596         }
597     }
598     return 0;
599 }
600 
Cancel(sp<IGsiService> gsid,int,char **)601 static int Cancel(sp<IGsiService> gsid, int /* argc */, char** /* argv */) {
602     bool cancelled = false;
603     auto status = gsid->cancelGsiInstall(&cancelled);
604     if (!status.isOk()) {
605         std::cerr << status.exceptionMessage().string() << std::endl;
606         return EX_SOFTWARE;
607     }
608     if (!cancelled) {
609         std::cout << "Fail to cancel the installation." << std::endl;
610         return EX_SOFTWARE;
611     }
612     return 0;
613 }
614 
Enable(sp<IGsiService> gsid,int argc,char ** argv)615 static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
616     bool one_shot = false;
617     std::string dsuSlot = {};
618     struct option options[] = {
619             {"single-boot", no_argument, nullptr, 's'},
620             {"dsuslot", required_argument, nullptr, 'd'},
621             {nullptr, 0, nullptr, 0},
622     };
623     int rv, index;
624     while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
625         switch (rv) {
626             case 's':
627                 one_shot = true;
628                 break;
629             case 'd':
630                 dsuSlot = optarg;
631                 break;
632             default:
633                 std::cerr << "Unrecognized argument to enable\n";
634                 return EX_USAGE;
635         }
636     }
637 
638     bool installed = false;
639     gsid->isGsiInstalled(&installed);
640     if (!installed) {
641         std::cerr << "Could not find GSI install to re-enable" << std::endl;
642         return EX_SOFTWARE;
643     }
644 
645     bool installing = false;
646     gsid->isGsiInstallInProgress(&installing);
647     if (installing) {
648         std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
649         return EX_SOFTWARE;
650     }
651     if (dsuSlot.empty()) {
652         auto status = gsid->getActiveDsuSlot(&dsuSlot);
653         if (!status.isOk()) {
654             std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
655             return EX_SOFTWARE;
656         }
657     }
658     int error;
659     auto status = gsid->enableGsi(one_shot, dsuSlot, &error);
660     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
661         std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
662         return EX_SOFTWARE;
663     }
664     std::cout << "Live image install successfully enabled." << std::endl;
665     return 0;
666 }
667 
Disable(sp<IGsiService> gsid,int argc,char **)668 static int Disable(sp<IGsiService> gsid, int argc, char** /* argv */) {
669     if (argc > 1) {
670         std::cerr << "Unrecognized arguments to disable." << std::endl;
671         return EX_USAGE;
672     }
673 
674     bool installing = false;
675     gsid->isGsiInstallInProgress(&installing);
676     if (installing) {
677         std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
678         return EX_SOFTWARE;
679     }
680 
681     bool ok = false;
682     gsid->disableGsi(&ok);
683     if (!ok) {
684         std::cerr << "Error disabling GSI" << std::endl;
685         return EX_SOFTWARE;
686     }
687     std::cout << "Live image install successfully disabled." << std::endl;
688     return 0;
689 }
690 
usage(int,char * argv[])691 static int usage(int /* argc */, char* argv[]) {
692     fprintf(stderr,
693             "%s - command-line tool for installing GSI images.\n"
694             "\n"
695             "Usage:\n"
696             "  %s <disable|install|wipe|status> [options]\n"
697             "\n"
698             "  disable      Disable the currently installed GSI.\n"
699             "  enable       [-s, --single-boot]\n"
700             "               [-d, --dsuslot slotname]\n"
701             "               Enable a previously disabled GSI.\n"
702             "  install      Install a new GSI. Specify the image size with\n"
703             "               --gsi-size and the desired userdata size with\n"
704             "               --userdata-size (the latter defaults to 8GiB)\n"
705             "               --wipe (remove old gsi userdata first)\n"
706             "  wipe         Completely remove a GSI and its associated data\n"
707             "  wipe-data    Ensure the GSI's userdata will be formatted\n"
708             "  cancel       Cancel the installation\n"
709             "  status       Show status\n",
710             argv[0], argv[0]);
711     return EX_USAGE;
712 }
713 
main(int argc,char ** argv)714 int main(int argc, char** argv) {
715     android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
716 
717     android::sp<IGsiService> service = GetGsiService();
718     if (!service) {
719         return EX_SOFTWARE;
720     }
721 
722     if (1 >= argc) {
723         std::cerr << "Expected command." << std::endl;
724         return EX_USAGE;
725     }
726 
727     std::string command = argv[1];
728 
729     auto iter = kCommandMap.find(command);
730     if (iter == kCommandMap.end()) {
731         std::cerr << "Unrecognized command: " << command << std::endl;
732         return usage(argc, argv);
733     }
734 
735     int rc = iter->second(service, argc - 1, argv + 1);
736     return rc;
737 }
738