blob: 7aab5b420c90ae9aabed65d93d841ea19434fcdd [file] [log] [blame]
Kelvin Zhangb66d2a02022-06-15 13:10:58 -07001/*
2 * Copyright (C) 2022 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 <BootControlClient.h>
18
19#include <aidl/android/hardware/boot/IBootControl.h>
20#include <android-base/logging.h>
Daniel Zheng28b6b2b2024-10-08 12:39:17 -070021#include <android/binder_ibinder.h>
Kelvin Zhangb66d2a02022-06-15 13:10:58 -070022#include <android/binder_manager.h>
23#include <android/hardware/boot/1.0/IBootControl.h>
24#include <android/hardware/boot/1.1/IBootControl.h>
25#include <android/hardware/boot/1.2/IBootControl.h>
26#include "utils/StrongPointer.h"
27
28#define CONCAT(x, y) x##y
29
30#define LOG_NDK_STATUS(x) \
31 do { \
32 const auto CONCAT(status, __COUNTER__) = x; \
33 if (!CONCAT(status, __COUNTER__).isOk()) { \
34 LOG(ERROR) << #x << " failed " << CONCAT(status, __COUNTER__).getDescription(); \
35 } \
36 } while (0)
37
38using aidl::android::hardware::boot::MergeStatus;
39
Daniel Zheng917cca52024-10-21 12:46:55 -070040#define TEST_OP(_x, _y, op) \
41 do { \
42 const auto& x = _x; \
43 const auto& y = _y; \
44 if (!(x op y)) { \
45 LOG(ERROR) << #_x " " #op " " #_y << " failed: " << x << " " #op " " << y; \
46 return {}; \
47 } \
48 } while (0)
49#define TEST_NE(_x, _y) TEST_OP(_x, _y, !=)
50
Kelvin Zhangb66d2a02022-06-15 13:10:58 -070051std::ostream& operator<<(std::ostream& os, MergeStatus status) {
52 switch (status) {
53 case MergeStatus::NONE:
54 os << "MergeStatus::NONE";
55 break;
56 case MergeStatus::UNKNOWN:
57 os << "MergeStatus::UNKNOWN";
58 break;
59 case MergeStatus::SNAPSHOTTED:
60 os << "MergeStatus::SNAPSHOTTED";
61 break;
62 case MergeStatus::MERGING:
63 os << "MergeStatus::MERGING";
64 break;
65 case MergeStatus::CANCELLED:
66 os << "MergeStatus::CANCELLED";
67 break;
68 default:
69 os << static_cast<int>(status);
70 break;
71 }
72 return os;
73}
74
75namespace android::hal {
76class BootControlClientAidl final : public BootControlClient {
77 using IBootControl = ::aidl::android::hardware::boot::IBootControl;
78
79 public:
Daniel Zheng28b6b2b2024-10-08 12:39:17 -070080 explicit BootControlClientAidl(std::shared_ptr<IBootControl> module)
81 : module_(module),
82 boot_control_death_recipient(AIBinder_DeathRecipient_new(onBootControlServiceDied)) {
Daniel Zheng57cd7462024-10-22 15:46:29 -070083 AIBinder_DeathRecipient_setOnUnlinked(boot_control_death_recipient, onCallbackUnlinked);
Daniel Zhengd5d0c552024-10-16 14:01:23 -070084 binder_status_t status =
85 AIBinder_linkToDeath(module->asBinder().get(), boot_control_death_recipient, this);
Daniel Zheng28b6b2b2024-10-08 12:39:17 -070086 if (status != STATUS_OK) {
87 LOG(ERROR) << "Could not link to binder death";
88 return;
89 }
90 }
Kelvin Zhangb66d2a02022-06-15 13:10:58 -070091
92 BootControlVersion GetVersion() const override { return BootControlVersion::BOOTCTL_AIDL; }
93
Daniel Zheng01d5a1d2024-10-16 14:09:50 -070094 void onBootControlServiceDied() {
95 LOG(ERROR) << "boot control service AIDL died. Attempting to reconnect...";
96 const auto instance_name =
97 std::string(::aidl::android::hardware::boot::IBootControl::descriptor) + "/default";
98 if (AServiceManager_isDeclared(instance_name.c_str())) {
99 module_ = ::aidl::android::hardware::boot::IBootControl::fromBinder(
100 ndk::SpAIBinder(AServiceManager_waitForService(instance_name.c_str())));
101 if (module_ == nullptr) {
102 LOG(ERROR) << "AIDL " << instance_name
103 << " is declared but waitForService returned nullptr when trying to "
104 "reconnect boot control service";
105 return;
106 }
107 LOG(INFO) << "Reconnected to AIDL version of IBootControl";
108 binder_status_t status = AIBinder_linkToDeath(module_->asBinder().get(),
109 boot_control_death_recipient, this);
110 if (status != STATUS_OK) {
111 LOG(ERROR) << "Could not link to binder death";
112 return;
113 }
114
115 } else {
116 LOG(ERROR) << "Failed to get service manager for: " << instance_name;
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700117 }
118 }
119
Daniel Zhengcba98612024-10-10 09:54:33 -0700120 int32_t GetNumSlots() const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700121 int32_t ret = -1;
Daniel Zheng917cca52024-10-21 12:46:55 -0700122 if (!module_) {
123 LOG(ERROR) << "bootctl module not set";
124 return ret;
125 }
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700126 LOG_NDK_STATUS(module_->getNumberSlots(&ret));
127 return ret;
128 }
129
Daniel Zhengcba98612024-10-10 09:54:33 -0700130 int32_t GetCurrentSlot() const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700131 int32_t ret = -1;
Daniel Zheng917cca52024-10-21 12:46:55 -0700132 if (!module_) {
133 LOG(ERROR) << "bootctl module not set";
134 return ret;
135 }
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700136 LOG_NDK_STATUS(module_->getCurrentSlot(&ret));
137 return ret;
138 }
Daniel Zheng917cca52024-10-21 12:46:55 -0700139
Daniel Zhengcba98612024-10-10 09:54:33 -0700140 MergeStatus getSnapshotMergeStatus() const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700141 MergeStatus status = MergeStatus::UNKNOWN;
Daniel Zheng917cca52024-10-21 12:46:55 -0700142 if (!module_) {
143 LOG(ERROR) << "bootctl module not set";
144 return status;
145 }
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700146 LOG_NDK_STATUS(module_->getSnapshotMergeStatus(&status));
147 return status;
148 }
Daniel Zheng917cca52024-10-21 12:46:55 -0700149
Daniel Zhengcba98612024-10-10 09:54:33 -0700150 std::string GetSuffix(int32_t slot) const override {
Daniel Zheng917cca52024-10-21 12:46:55 -0700151 TEST_NE(module_, nullptr);
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700152 std::string ret;
153 const auto status = module_->getSuffix(slot, &ret);
154 if (!status.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700155 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
156 << status.getDescription();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700157 return {};
158 }
159 return ret;
160 }
161
Daniel Zhengcba98612024-10-10 09:54:33 -0700162 std::optional<bool> IsSlotBootable(int32_t slot) const override {
Daniel Zheng917cca52024-10-21 12:46:55 -0700163 TEST_NE(module_, nullptr);
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700164 bool ret = false;
165 const auto status = module_->isSlotBootable(slot, &ret);
166 if (!status.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700167 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
168 << status.getDescription();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700169 return {};
170 }
171 return ret;
172 }
173
Daniel Zhengcba98612024-10-10 09:54:33 -0700174 CommandResult MarkSlotUnbootable(int32_t slot) override {
Daniel Zheng917cca52024-10-21 12:46:55 -0700175 TEST_NE(module_, nullptr);
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700176 const auto status = module_->setSlotAsUnbootable(slot);
177 if (!status.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700178 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
179 << status.getDescription();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700180 }
181 return {.success = status.isOk(), .errMsg = status.getDescription()};
182 }
183
Daniel Zhengcba98612024-10-10 09:54:33 -0700184 CommandResult SetActiveBootSlot(int slot) override {
Daniel Zheng917cca52024-10-21 12:46:55 -0700185 TEST_NE(module_, nullptr);
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700186 const auto status = module_->setActiveBootSlot(slot);
187 if (!status.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700188 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
189 << status.getDescription();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700190 }
191 return {.success = status.isOk(), .errMsg = status.getDescription()};
192 }
Daniel Zheng917cca52024-10-21 12:46:55 -0700193
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700194 int GetActiveBootSlot() const {
195 int ret = -1;
Daniel Zheng917cca52024-10-21 12:46:55 -0700196 if (!module_) {
197 LOG(ERROR) << "bootctl module not set";
198 return ret;
199 }
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700200 LOG_NDK_STATUS(module_->getActiveBootSlot(&ret));
201 return ret;
202 }
203
204 // Check if |slot| is marked boot successfully.
Daniel Zhengcba98612024-10-10 09:54:33 -0700205 std::optional<bool> IsSlotMarkedSuccessful(int slot) const override {
Daniel Zheng917cca52024-10-21 12:46:55 -0700206 TEST_NE(module_, nullptr);
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700207 bool ret = false;
208 const auto status = module_->isSlotMarkedSuccessful(slot, &ret);
209 if (!status.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700210 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed "
211 << status.getDescription();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700212 return {};
213 }
214 return ret;
215 }
216
Daniel Zhengcba98612024-10-10 09:54:33 -0700217 CommandResult MarkBootSuccessful() override {
Daniel Zheng917cca52024-10-21 12:46:55 -0700218 TEST_NE(module_, nullptr);
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700219 const auto status = module_->markBootSuccessful();
220 if (!status.isOk()) {
221 LOG(ERROR) << __FUNCTION__ << " failed " << status.getDescription();
222 }
223 return {.success = status.isOk(), .errMsg = status.getDescription()};
224 }
225
Daniel Zhengcba98612024-10-10 09:54:33 -0700226 CommandResult SetSnapshotMergeStatus(
227 aidl::android::hardware::boot::MergeStatus merge_status) override {
Daniel Zheng917cca52024-10-21 12:46:55 -0700228 TEST_NE(module_, nullptr);
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700229 const auto status = module_->setSnapshotMergeStatus(merge_status);
230 if (!status.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700231 LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")" << " failed "
232 << status.getDescription();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700233 }
234 return {.success = status.isOk(), .errMsg = status.getDescription()};
235 }
236
237 private:
Daniel Zheng01d5a1d2024-10-16 14:09:50 -0700238 std::shared_ptr<IBootControl> module_;
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700239 AIBinder_DeathRecipient* boot_control_death_recipient;
Daniel Zheng57cd7462024-10-22 15:46:29 -0700240 static void onCallbackUnlinked(void* /*client*/) {
241 // this is an empty function needed to suppress the "AIBinder_linkToDeath is being called
242 // with a non-null cookie and no onUnlink callback set. This might not be intended.
243 // AIBinder_DeathRecipient_setOnUnlinked should be called first." warning
244 }
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700245 static void onBootControlServiceDied(void* client) {
246 BootControlClientAidl* self = static_cast<BootControlClientAidl*>(client);
247 self->onBootControlServiceDied();
248 }
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700249};
250
251using namespace android::hardware::boot;
252
253class BootControlClientHIDL final : public BootControlClient {
254 public:
255 BootControlClientHIDL(android::sp<V1_0::IBootControl> module_v1,
256 android::sp<V1_1::IBootControl> module_v1_1,
257 android::sp<V1_2::IBootControl> module_v1_2)
258 : module_v1_(module_v1), module_v1_1_(module_v1_1), module_v1_2_(module_v1_2) {
259 CHECK(module_v1_ != nullptr);
260 }
261 BootControlVersion GetVersion() const override {
262 if (module_v1_2_ != nullptr) {
263 return BootControlVersion::BOOTCTL_V1_2;
264 } else if (module_v1_1_ != nullptr) {
265 return BootControlVersion::BOOTCTL_V1_1;
266 } else {
267 return BootControlVersion::BOOTCTL_V1_0;
268 }
269 }
Daniel Zhengcba98612024-10-10 09:54:33 -0700270 int32_t GetNumSlots() const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700271 const auto ret = module_v1_->getNumberSlots();
272 if (!ret.isOk()) {
273 LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
274 }
275 return ret.withDefault(-1);
276 }
277
Daniel Zhengcba98612024-10-10 09:54:33 -0700278 int32_t GetCurrentSlot() const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700279 const auto ret = module_v1_->getCurrentSlot();
280 if (!ret.isOk()) {
281 LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
282 }
283 return ret.withDefault(-1);
284 }
285
Daniel Zhengcba98612024-10-10 09:54:33 -0700286 std::string GetSuffix(int32_t slot) const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700287 std::string suffix;
288 const auto ret = module_v1_->getSuffix(
289 slot,
290 [&](const ::android::hardware::hidl_string& slotSuffix) { suffix = slotSuffix; });
291 if (!ret.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700292 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700293 }
294 return suffix;
295 }
296
Daniel Zhengcba98612024-10-10 09:54:33 -0700297 std::optional<bool> IsSlotBootable(int32_t slot) const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700298 const auto ret = module_v1_->isSlotBootable(slot);
299 if (!ret.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700300 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700301 return {};
302 }
303 const auto bool_result = ret.withDefault(V1_0::BoolResult::INVALID_SLOT);
304 if (bool_result == V1_0::BoolResult::INVALID_SLOT) {
305 return {};
306 }
307 return bool_result == V1_0::BoolResult::TRUE;
308 }
309
Daniel Zhengcba98612024-10-10 09:54:33 -0700310 CommandResult MarkSlotUnbootable(int32_t slot) override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700311 CommandResult result;
312 const auto ret =
313 module_v1_->setSlotAsUnbootable(slot, [&](const V1_0::CommandResult& error) {
314 result.success = error.success;
315 result.errMsg = error.errMsg;
316 });
317 if (!ret.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700318 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700319 }
320 return result;
321 }
322
Daniel Zhengcba98612024-10-10 09:54:33 -0700323 CommandResult SetActiveBootSlot(int32_t slot) override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700324 CommandResult result;
325 const auto ret = module_v1_->setActiveBootSlot(slot, [&](const V1_0::CommandResult& error) {
326 result.success = error.success;
327 result.errMsg = error.errMsg;
328 });
329 if (!ret.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700330 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700331 }
332 return result;
333 }
334
Daniel Zhengcba98612024-10-10 09:54:33 -0700335 CommandResult MarkBootSuccessful() override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700336 CommandResult result;
337 const auto ret = module_v1_->markBootSuccessful([&](const V1_0::CommandResult& error) {
338 result.success = error.success;
339 result.errMsg = error.errMsg;
340 });
341 if (!ret.isOk()) {
342 LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
343 }
344 return result;
345 }
346
Daniel Zhengcba98612024-10-10 09:54:33 -0700347 std::optional<bool> IsSlotMarkedSuccessful(int32_t slot) const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700348 const auto ret = module_v1_->isSlotMarkedSuccessful(slot);
349 if (!ret.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700350 LOG(ERROR) << __FUNCTION__ << "(" << slot << ")" << " failed " << ret.description();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700351 return {};
352 }
353 const auto bool_result = ret.withDefault(V1_0::BoolResult::INVALID_SLOT);
354 if (bool_result == V1_0::BoolResult::INVALID_SLOT) {
355 return {};
356 }
357 return bool_result == V1_0::BoolResult::TRUE;
358 }
359
Daniel Zhengcba98612024-10-10 09:54:33 -0700360 MergeStatus getSnapshotMergeStatus() const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700361 if (module_v1_1_ == nullptr) {
362 LOG(ERROR) << __FUNCTION__ << " is unsupported, requires at least boot v1.1";
363 return MergeStatus::UNKNOWN;
364 }
365 const auto ret = module_v1_1_->getSnapshotMergeStatus();
366 if (!ret.isOk()) {
367 LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
368 }
369 return static_cast<MergeStatus>(
370 ret.withDefault(static_cast<V1_1::MergeStatus>(MergeStatus::UNKNOWN)));
371 }
372
Daniel Zhengcba98612024-10-10 09:54:33 -0700373 CommandResult SetSnapshotMergeStatus(MergeStatus merge_status) override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700374 if (module_v1_1_ == nullptr) {
375 return {.success = false,
376 .errMsg = "setSnapshotMergeStatus is unsupported, requires at least boot v1.1"};
377 }
378 const auto ret =
379 module_v1_1_->setSnapshotMergeStatus(static_cast<V1_1::MergeStatus>(merge_status));
380 if (!ret.isOk()) {
Daniel Zheng28b6b2b2024-10-08 12:39:17 -0700381 LOG(ERROR) << __FUNCTION__ << "(" << merge_status << ")" << " failed "
382 << ret.description();
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700383 }
384 return {.success = ret.isOk(), .errMsg = ret.description()};
385 }
386
Daniel Zhengcba98612024-10-10 09:54:33 -0700387 int32_t GetActiveBootSlot() const override {
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700388 if (module_v1_2_ == nullptr) {
389 LOG(ERROR) << __FUNCTION__ << " is unsupported, requires at least boot v1.2";
390 return -1;
391 }
392 const auto ret = module_v1_2_->getActiveBootSlot();
393 if (!ret.isOk()) {
394 LOG(ERROR) << __FUNCTION__ << " failed " << ret.description();
395 }
396 return ret.withDefault(-1);
397 }
398
399 private:
400 android::sp<V1_0::IBootControl> module_v1_;
401 android::sp<V1_1::IBootControl> module_v1_1_;
402 android::sp<V1_2::IBootControl> module_v1_2_;
403};
404
405std::unique_ptr<BootControlClient> BootControlClient::WaitForService() {
406 const auto instance_name =
407 std::string(::aidl::android::hardware::boot::IBootControl::descriptor) + "/default";
Kelvin Zhang781b4ff2022-06-27 16:29:53 -0700408 if (AServiceManager_isDeclared(instance_name.c_str())) {
409 auto module = ::aidl::android::hardware::boot::IBootControl::fromBinder(
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700410 ndk::SpAIBinder(AServiceManager_waitForService(instance_name.c_str())));
Kelvin Zhang781b4ff2022-06-27 16:29:53 -0700411 if (module == nullptr) {
412 LOG(ERROR) << "AIDL " << instance_name
413 << " is declared but waitForService returned nullptr.";
414 return nullptr;
415 }
Kelvin Zhangb66d2a02022-06-15 13:10:58 -0700416 LOG(INFO) << "Using AIDL version of IBootControl";
417 return std::make_unique<BootControlClientAidl>(module);
418 }
419 LOG(INFO) << "AIDL IBootControl not available, falling back to HIDL.";
420
421 android::sp<V1_0::IBootControl> v1_0_module;
422 android::sp<V1_1::IBootControl> v1_1_module;
423 android::sp<V1_2::IBootControl> v1_2_module;
424 v1_0_module = V1_0::IBootControl::getService();
425 if (v1_0_module == nullptr) {
426 LOG(ERROR) << "Error getting bootctrl v1.0 module.";
427 return nullptr;
428 }
429 v1_1_module = V1_1::IBootControl::castFrom(v1_0_module);
430 v1_2_module = V1_2::IBootControl::castFrom(v1_0_module);
431 if (v1_2_module != nullptr) {
432 LOG(INFO) << "Using HIDL version 1.2 of IBootControl";
433 } else if (v1_1_module != nullptr) {
434 LOG(INFO) << "Using HIDL version 1.1 of IBootControl";
435 } else {
436 LOG(INFO) << "Using HIDL version 1.0 of IBootControl";
437 }
438
439 return std::make_unique<BootControlClientHIDL>(v1_0_module, v1_1_module, v1_2_module);
440}
441
442} // namespace android::hal