1 /*
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 #include "include/fastboot/Fastboot_aidl.h"
17
18 #include <android-base/file.h>
19 #include <android-base/logging.h>
20 #include <android-base/strings.h>
21 #include <android-base/unique_fd.h>
22 #include <dlfcn.h>
23 #include <endian.h>
24
25 #include <map>
26 #include <string>
27 #include <unordered_map>
28 #include <vector>
29
30 // FS headers
31 #include <ext4_utils/wipe.h>
32 #include <fs_mgr.h>
33 #include <fs_mgr/roots.h>
34
35 // Nugget headers
36 #include <app_nugget.h>
37 #include <nos/NuggetClient.h>
38 #include <nos/debug.h>
39
40 using ndk::ScopedAStatus;
41
42 namespace aidl {
43 namespace android {
44 namespace hardware {
45 namespace fastboot {
46
47 constexpr const char *BRIGHTNESS_FILE = "/sys/class/backlight/panel0-backlight/brightness";
48 constexpr int DISPLAY_BRIGHTNESS_DIM_THRESHOLD = 20;
49
50 using OEMCommandHandler =
51 std::function<ScopedAStatus(const std::vector<std::string> &, std::string *)>;
52
getPartitionType(const std::string & in_partitionName,FileSystemType * _aidl_return)53 ScopedAStatus Fastboot::getPartitionType(const std::string &in_partitionName,
54 FileSystemType *_aidl_return) {
55 if (in_partitionName.empty()) {
56 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
57 "Invalid partition name");
58 }
59 // For bluecross devices, all partitions need to return raw.
60 *_aidl_return = FileSystemType::RAW;
61 return ScopedAStatus::ok();
62 }
63
getVariant(std::string * _aidl_return)64 ScopedAStatus Fastboot::getVariant(std::string *_aidl_return) {
65 *_aidl_return = "MSM USF";
66 return ScopedAStatus::ok();
67 }
68
getOffModeChargeState(bool * _aidl_return)69 ScopedAStatus Fastboot::getOffModeChargeState(bool *_aidl_return) {
70 constexpr const char *kDevinfoPath = "/dev/block/by-name/devinfo";
71 constexpr int kDevInfoOffModeChargeOffset = 15;
72
73 uint8_t off_mode_charge_status = 0;
74 ::android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(kDevinfoPath, O_RDONLY | O_BINARY)));
75 if (fd < 0) {
76 std::string message = "Unable to open devinfo " + std::to_string(errno);
77 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
78 message.c_str());
79 }
80 auto ret = ::android::base::ReadFullyAtOffset(fd, &off_mode_charge_status, 1 /* byte count */,
81 kDevInfoOffModeChargeOffset);
82 if (!ret) {
83 std::string message = "Reading devifo failed errno:" + std::to_string(errno) +
84 " Unable to read off-mode-charge state";
85 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
86 message.c_str());
87 } else {
88 *_aidl_return = (off_mode_charge_status != 0);
89 }
90
91 return ScopedAStatus::ok();
92 }
93
getBatteryVoltageFlashingThreshold(int32_t * _aidl_return)94 ScopedAStatus Fastboot::getBatteryVoltageFlashingThreshold(int32_t *_aidl_return) {
95 constexpr int kMinVoltageForFlashing = 3500;
96 *_aidl_return = kMinVoltageForFlashing;
97 return ScopedAStatus::ok();
98 }
99
SetBrightnessLevel(const std::vector<std::string> & args,std::string * _aidl_return)100 ScopedAStatus SetBrightnessLevel(const std::vector<std::string> &args, std::string *_aidl_return) {
101 if (!args.size()) {
102 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
103 "Brightness level unspecified");
104 }
105
106 auto level = std::stoi(args[0]);
107
108 if (level < 0 || level > 100) {
109 return ScopedAStatus::fromExceptionCodeWithMessage(
110 EX_ILLEGAL_ARGUMENT, "Brighness level must be between 0 and 100");
111 }
112
113 // Avoid screen being dimmed too much.
114 if (level < DISPLAY_BRIGHTNESS_DIM_THRESHOLD) {
115 level = DISPLAY_BRIGHTNESS_DIM_THRESHOLD;
116 }
117
118 if (::android::base::WriteStringToFile(std::to_string(level), BRIGHTNESS_FILE)) {
119 *_aidl_return = "";
120 return ScopedAStatus::ok();
121 }
122 std::string message = "Writing to brightness file failed errno: " + std::to_string(errno) +
123 " Unable to set display brightness";
124
125 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
126 message.c_str());
127 }
128
doOemCommand(const std::string & in_oemCmd,std::string * _aidl_return)129 ScopedAStatus Fastboot::doOemCommand(const std::string &in_oemCmd, std::string *_aidl_return) {
130 const std::unordered_map<std::string, OEMCommandHandler> kOEMCmdMap = {
131 {FB_OEM_SET_BRIGHTNESS, SetBrightnessLevel},
132 };
133
134 auto args = ::android::base::Split(in_oemCmd, " ");
135 if (args.size() < 2) {
136 return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
137 "Invalid OEM command");
138 }
139
140 // args[0] will be "oem", args[1] will be the command name
141 auto cmd_handler = kOEMCmdMap.find(args[1]);
142 if (cmd_handler != kOEMCmdMap.end()) {
143 return cmd_handler->second(std::vector<std::string>(args.begin() + 2, args.end()),
144 _aidl_return);
145 } else {
146 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
147 "Unknown OEM Command");
148 }
149
150 return ScopedAStatus::ok();
151 }
152
153 static ::android::fs_mgr::Fstab fstab;
154 enum WipeVolumeStatus {
155 WIPE_OK = 0,
156 VOL_FSTAB,
157 VOL_UNKNOWN,
158 VOL_MOUNTED,
159 VOL_BLK_DEV_OPEN,
160 WIPE_ERROR_MAX = 0xffffffff,
161 };
162 std::map<enum WipeVolumeStatus, std::string> wipe_vol_ret_msg{
163 {WIPE_OK, ""},
164 {VOL_FSTAB, "Unknown FS table"},
165 {VOL_UNKNOWN, "Unknown volume"},
166 {VOL_MOUNTED, "Fail to unmount volume"},
167 {VOL_BLK_DEV_OPEN, "Fail to open block device"},
168 {WIPE_ERROR_MAX, "Unknown wipe error"}};
169
wipe_volume(const std::string & volume)170 enum WipeVolumeStatus wipe_volume(const std::string &volume) {
171 if (!::android::fs_mgr::ReadDefaultFstab(&fstab)) {
172 return VOL_FSTAB;
173 }
174 const ::android::fs_mgr::FstabEntry *v = ::android::fs_mgr::GetEntryForPath(&fstab, volume);
175 if (v == nullptr) {
176 return VOL_UNKNOWN;
177 }
178 if (::android::fs_mgr::EnsurePathUnmounted(&fstab, volume) != true) {
179 return VOL_MOUNTED;
180 }
181
182 int fd = open(v->blk_device.c_str(), O_WRONLY | O_CREAT, 0644);
183 if (fd == -1) {
184 return VOL_BLK_DEV_OPEN;
185 }
186 wipe_block_device(fd, get_block_device_size(fd));
187 close(fd);
188
189 return WIPE_OK;
190 }
191
192 // Attempt to reuse a WipeKeys function that might be found in the recovery
193 // library in order to clear any digital car keys on the secure element.
WipeDigitalCarKeys(void)194 bool WipeDigitalCarKeys(void) {
195 static constexpr const char *kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so";
196 void *librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW);
197 if (librecovery_ui_ext == nullptr) {
198 // Dynamic library not found. Returning true since this likely
199 // means target does not support DCK.
200 return true;
201 }
202
203 bool *(*WipeKeysFunc)(void *const);
204 reinterpret_cast<void *&>(WipeKeysFunc) = dlsym(librecovery_ui_ext, "WipeKeys");
205 if (WipeKeysFunc == nullptr) {
206 // No WipeKeys implementation found. Returning true since this likely
207 // means target does not support DCK.
208 return true;
209 }
210
211 return (*WipeKeysFunc)(nullptr);
212 }
213
doOemSpecificErase()214 ScopedAStatus Fastboot::doOemSpecificErase() {
215 // Erase metadata partition along with userdata partition.
216 // Keep erasing Titan M even if failing on this case.
217 auto wipe_status = wipe_volume("/metadata");
218
219 bool dck_wipe_success = WipeDigitalCarKeys();
220
221 // Connect to Titan M
222 ::nos::NuggetClient client;
223 client.Open();
224 if (!client.IsOpen()) {
225 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
226 "open Titan M fail");
227 }
228
229 // Tell Titan M to wipe user data
230 const uint32_t magicValue = htole32(ERASE_CONFIRMATION);
231 std::vector<uint8_t> magic(sizeof(magicValue));
232 memcpy(magic.data(), &magicValue, sizeof(magicValue));
233 const uint8_t retry_count = 5;
234 uint32_t nugget_status;
235 for (uint8_t i = 0; i < retry_count; i++) {
236 nugget_status = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr);
237 if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK) {
238 return ScopedAStatus::ok();
239 }
240 }
241
242 // Return exactly what happened
243 if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK && !dck_wipe_success) {
244 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
245 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata, Titan M user data, and DCK");
246 } else if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
247 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
248 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data");
249 } else if (nugget_status != APP_SUCCESS && !dck_wipe_success) {
250 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
251 BnFastboot::FAILURE_UNKNOWN, "Titan M user data and DCK wipe failed");
252 } else if (nugget_status != APP_SUCCESS) {
253 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
254 "Titan M user data wipe failed");
255 } else if (wipe_status != WIPE_OK && !dck_wipe_success) {
256 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
257 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and DCK");
258 } else if (!dck_wipe_success) {
259 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
260 "DCK wipe failed");
261 } else {
262 if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
263 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
264 BnFastboot::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status].c_str());
265 else // Should not reach here, but handle it anyway
266 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
267 "Unknown failure");
268 }
269
270 // Return exactly what happened
271 if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
272 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
273 BnFastboot::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data");
274
275 } else if (nugget_status != APP_SUCCESS) {
276 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
277 "Titan M user data wipe failed");
278 } else {
279 if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
280 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
281 BnFastboot::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status].c_str());
282 else // Should not reach here, but handle it anyway
283 return ScopedAStatus::fromServiceSpecificErrorWithMessage(BnFastboot::FAILURE_UNKNOWN,
284 "Unknown failure");
285 }
286
287 return ScopedAStatus::ok();
288 }
289
290 } // namespace fastboot
291 } // namespace hardware
292 } // namespace android
293 } // namespace aidl
294