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/Android.mk b/Android.mk
index 0540bdb..9542080 100644
--- a/Android.mk
+++ b/Android.mk
@@ -28,6 +28,64 @@
-Werror \
-DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+# librecovery_ui_ext (shared library)
+# ===================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := librecovery_ui_ext
+
+# LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds.
+LOCAL_MULTILIB := first
+
+ifeq ($(TARGET_IS_64_BIT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib64
+else
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES := \
+ $(TARGET_RECOVERY_UI_LIB)
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+ librecovery_ui
+
+include $(BUILD_SHARED_LIBRARY)
+
+# librecovery_ui (shared library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ device.cpp \
+ screen_ui.cpp \
+ ui.cpp \
+ vr_ui.cpp \
+ wear_ui.cpp
+
+LOCAL_MODULE := librecovery_ui
+
+LOCAL_CFLAGS := $(recovery_common_cflags)
+
+LOCAL_MULTILIB := first
+
+ifeq ($(TARGET_IS_64_BIT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib64
+else
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+ libminui \
+ libotautil \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libpng \
+ libz \
+
+include $(BUILD_SHARED_LIBRARY)
+
# librecovery_ui (static library)
# ===============================
include $(CLEAR_VARS)
@@ -40,21 +98,23 @@
LOCAL_MODULE := librecovery_ui
+LOCAL_CFLAGS := $(recovery_common_cflags)
+
LOCAL_STATIC_LIBRARIES := \
libminui \
libotautil \
- libbase
-LOCAL_CFLAGS := $(recovery_common_cflags)
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libpng \
+ libz \
include $(BUILD_STATIC_LIBRARY)
librecovery_static_libraries := \
- $(TARGET_RECOVERY_UI_LIB) \
libbootloader_message \
libfusesideload \
libminadbd \
- librecovery_ui \
libminui \
libverifier \
libotautil \
@@ -112,8 +172,6 @@
LOCAL_MODULE := recovery
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
# Cannot link with LLD: undefined symbol: UsbNoPermissionsLongHelpText
@@ -124,8 +182,12 @@
LOCAL_STATIC_LIBRARIES := \
librecovery \
+ librecovery_ui_default \
$(librecovery_static_libraries)
+LOCAL_SHARED_LIBRARIES := \
+ librecovery_ui \
+
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
LOCAL_REQUIRED_MODULES := \
@@ -154,6 +216,17 @@
recovery-refresh
endif
+LOCAL_REQUIRED_MODULES += \
+ librecovery_ui_ext
+
+# TODO(b/110380063): Explicitly install the following shared libraries to recovery, until `recovery`
+# module is built with Soong (with `recovery: true` flag).
+LOCAL_REQUIRED_MODULES += \
+ libbase.recovery \
+ liblog.recovery \
+ libpng.recovery \
+ libz.recovery \
+
include $(BUILD_EXECUTABLE)
include \
diff --git a/device.h b/device.h
index cbecc43..a6ad627 100644
--- a/device.h
+++ b/device.h
@@ -119,8 +119,12 @@
std::unique_ptr<RecoveryUI> ui_;
};
+// Disable name mangling, as this function will be loaded via dlsym(3).
+extern "C" {
+
// The device-specific library must define this function (or the default one will be used, if there
// is no device-specific library). It returns the Device object that recovery should use.
Device* make_device();
+}
#endif // _DEVICE_H
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());
diff --git a/tests/Android.mk b/tests/Android.mk
index e68e77e..58ef3a2 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -155,10 +155,10 @@
librecovery_static_libraries := \
librecovery \
- $(TARGET_RECOVERY_UI_LIB) \
libbootloader_message \
libfusesideload \
libminadbd \
+ librecovery_ui_default \
librecovery_ui \
libminui \
libverifier \