Add a stub recovery UI.

This allows recovery to work on devices without screen.
The stub recovery UI does nothing except print to stdout.

Test: write 'recovery\n--wipe_data\n--reason=wipe_data_from_ota\n'
      to misc and boot to recovery on a device without screen.
Bug: 33175036

Change-Id: Icde698aa2e2e29f4b3d0532dfd3c6a939ac2bc63
diff --git a/recovery.cpp b/recovery.cpp
index e777c46..9152bb1 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -68,6 +68,7 @@
 #include "roots.h"
 #include "rotate_logs.h"
 #include "screen_ui.h"
+#include "stub_ui.h"
 #include "ui.h"
 
 static const struct option OPTIONS[] = {
@@ -1495,8 +1496,11 @@
     Device* device = make_device();
     ui = device->GetUI();
 
+    if (!ui->Init()) {
+      printf("Failed to initialize UI, use stub UI instead.");
+      ui = new StubRecoveryUI();
+    }
     ui->SetLocale(locale.c_str());
-    ui->Init();
     // Set background string to "installing security update" for security update,
     // otherwise set it to "installing system update".
     ui->SetSystemUpdateText(security_update);
diff --git a/screen_ui.cpp b/screen_ui.cpp
index a7b03c5..5b9e5a5 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -448,17 +448,22 @@
     Redraw();
 }
 
-void ScreenRecoveryUI::InitTextParams() {
-    gr_init();
+bool ScreenRecoveryUI::InitTextParams() {
+    if (gr_init() < 0) {
+      return false;
+    }
 
     gr_font_size(gr_sys_font(), &char_width_, &char_height_);
     text_rows_ = gr_fb_height() / char_height_;
     text_cols_ = gr_fb_width() / char_width_;
+    return true;
 }
 
-void ScreenRecoveryUI::Init() {
+bool ScreenRecoveryUI::Init() {
     RecoveryUI::Init();
-    InitTextParams();
+    if (!InitTextParams()) {
+      return false;
+    }
 
     density_ = static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f;
 
@@ -493,6 +498,8 @@
     LoadAnimation();
 
     pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
+
+    return true;
 }
 
 void ScreenRecoveryUI::LoadAnimation() {
diff --git a/screen_ui.h b/screen_ui.h
index de7b644..38e2f07 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -29,7 +29,7 @@
   public:
     ScreenRecoveryUI();
 
-    void Init();
+    bool Init() override;
     void SetLocale(const char* locale);
 
     // overall recovery state ("background image")
@@ -137,7 +137,7 @@
     pthread_mutex_t updateMutex;
     bool rtl_locale;
 
-    virtual void InitTextParams();
+    virtual bool InitTextParams();
 
     virtual void draw_background_locked();
     virtual void draw_foreground_locked();
diff --git a/stub_ui.h b/stub_ui.h
new file mode 100644
index 0000000..1219b28
--- /dev/null
+++ b/stub_ui.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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_STUB_UI_H
+#define RECOVERY_STUB_UI_H
+
+#include "ui.h"
+
+// Stub implementation of RecoveryUI for devices without screen.
+class StubRecoveryUI : public RecoveryUI {
+ public:
+  StubRecoveryUI() = default;
+
+  void SetLocale(const char* locale) override {}
+
+  void SetBackground(Icon icon) override {}
+  void SetSystemUpdateText(bool security_update) override {}
+
+  // progress indicator
+  void SetProgressType(ProgressType type) override {}
+  void ShowProgress(float portion, float seconds) override {}
+  void SetProgress(float fraction) override {}
+
+  void SetStage(int current, int max) override {}
+
+  // text log
+  void ShowText(bool visible) override {}
+  bool IsTextVisible() override {
+    return false;
+  }
+  bool WasTextEverVisible() override {
+    return false;
+  }
+
+  // printing messages
+  void Print(const char* fmt, ...) override {
+    va_list ap;
+    va_start(ap, fmt);
+    vprintf(fmt, ap);
+    va_end(ap);
+  }
+  void PrintOnScreenOnly(const char* fmt, ...) override {}
+  void ShowFile(const char* filename) override {}
+
+  // menu display
+  void StartMenu(const char* const* headers, const char* const* items,
+                 int initial_selection) override {}
+  int SelectMenu(int sel) override {
+    return sel;
+  }
+  void EndMenu() override {}
+};
+
+#endif  // RECOVERY_STUB_UI_H
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index 60a78f5..33aadb3 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -40,7 +40,7 @@
 RecoveryUI* ui = NULL;
 
 class MockUI : public RecoveryUI {
-    void Init() { }
+    bool Init() { return true; }
     void SetStage(int, int) { }
     void SetLocale(const char*) { }
     void SetBackground(Icon /*icon*/) { }
diff --git a/ui.cpp b/ui.cpp
index 78b6e4f..2d80c38 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -80,12 +80,13 @@
     return nullptr;
 }
 
-void RecoveryUI::Init() {
+bool RecoveryUI::Init() {
     ev_init(InputCallback, this);
 
     ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
 
     pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
+    return true;
 }
 
 int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
diff --git a/ui.h b/ui.h
index 82d95a3..be95a4e 100644
--- a/ui.h
+++ b/ui.h
@@ -28,8 +28,8 @@
 
     virtual ~RecoveryUI() { }
 
-    // Initialize the object; called before anything else.
-    virtual void Init();
+    // Initialize the object; called before anything else. Returns true on success.
+    virtual bool Init();
     // Show a stage indicator.  Call immediately after Init().
     virtual void SetStage(int current, int max) = 0;
 
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 0918ac4..11e5a71 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -190,8 +190,10 @@
     gr_flip();
 }
 
-void WearRecoveryUI::InitTextParams() {
-    ScreenRecoveryUI::InitTextParams();
+bool WearRecoveryUI::InitTextParams() {
+    if (!ScreenRecoveryUI::InitTextParams()) {
+        return false;
+    }
 
     text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_;
 
@@ -199,15 +201,19 @@
     if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
 
     visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_;
+    return true;
 }
 
-void WearRecoveryUI::Init() {
-    ScreenRecoveryUI::Init();
+bool WearRecoveryUI::Init() {
+    if (!ScreenRecoveryUI::Init()) {
+        return false;
+    }
 
     LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
     backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
     LoadBitmap("icon_error", &backgroundIcon[ERROR]);
     backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
+    return true;
 }
 
 void WearRecoveryUI::SetStage(int current, int max)
diff --git a/wear_ui.h b/wear_ui.h
index 9351d41..5ac6f49 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -23,7 +23,7 @@
   public:
     WearRecoveryUI();
 
-    void Init() override;
+    bool Init() override;
 
     void SetStage(int current, int max) override;
 
@@ -52,7 +52,7 @@
 
     int GetProgressBaseline() override;
 
-    void InitTextParams() override;
+    bool InitTextParams() override;
 
     void update_progress_locked() override;