Dynamically load device-specific recovery UI lib.

We used to statically link the device-specific recovery UI extension
(`TARGET_RECOVERY_UI_LIB`) into `recovery`. Such a logic can't be easily
migrated to Soong, as modules specified by `TARGET_RECOVERY_UI_LIB` may
not be built with Soong.

Instead of porting all the device-specific codes over, this CL builds
and installs the UI lib as a shared library with Android.mk. `recovery`
dlopen(3)'s and dlsym(3)'s `make_device` to invoke the device-specific
UI lib on start.

Note that in order to make dlopen(3) actually working, we have to switch
`recovery` to be dynamically linked (we will make the move later
anyway).

Bug: 110380063
Test: Build and boot into marlin recovery image. Check that
      device-specific recovery UI is successfully loaded.
Change-Id: Ia9861c7559a95f3f50676534540c0cb87cae4574
diff --git a/recovery_main.cpp b/recovery_main.cpp
index c79d7d8..9a9890d 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -329,7 +330,32 @@
 
   printf("locale is [%s]\n", locale.c_str());
 
-  Device* device = make_device();
+  static constexpr const char* kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so";
+  // Intentionally not calling dlclose(3) to avoid potential gotchas (e.g. `make_device` may have
+  // handed out pointers to code or static [or thread-local] data and doesn't collect them all back
+  // in on dlclose).
+  void* librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW);
+
+  using MakeDeviceType = decltype(&make_device);
+  MakeDeviceType make_device_func = nullptr;
+  if (librecovery_ui_ext == nullptr) {
+    printf("Failed to dlopen %s: %s\n", kDefaultLibRecoveryUIExt, dlerror());
+  } else {
+    reinterpret_cast<void*&>(make_device_func) = dlsym(librecovery_ui_ext, "make_device");
+    if (make_device_func == nullptr) {
+      printf("Failed to dlsym make_device: %s\n", dlerror());
+    }
+  }
+
+  Device* device;
+  if (make_device_func == nullptr) {
+    printf("Falling back to the default make_device() instead\n");
+    device = make_device();
+  } else {
+    printf("Loading make_device from %s\n", kDefaultLibRecoveryUIExt);
+    device = (*make_device_func)();
+  }
+
   if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
     printf("Quiescent recovery mode.\n");
     device->ResetUI(new StubRecoveryUI());