Hridya Valsaraju | fcfbd96 | 2018-10-12 14:35:37 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 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 "fastboot/Fastboot.h" |
| 18 | |
Hridya Valsaraju | fcfbd96 | 2018-10-12 14:35:37 -0700 | [diff] [blame] | 19 | #include <android-base/file.h> |
| 20 | #include <android-base/logging.h> |
| 21 | #include <android-base/strings.h> |
| 22 | #include <android-base/unique_fd.h> |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 23 | #include <dlfcn.h> |
| 24 | |
| 25 | #include <map> |
| 26 | #include <string> |
| 27 | #include <unordered_map> |
| 28 | #include <vector> |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 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 |
josephjang | 0536ed3 | 2020-09-17 12:05:17 +0800 | [diff] [blame] | 36 | #include <app_nugget.h> |
| 37 | #include <nos/NuggetClient.h> |
| 38 | #include <nos/debug.h> |
Hridya Valsaraju | fcfbd96 | 2018-10-12 14:35:37 -0700 | [diff] [blame] | 39 | |
| 40 | namespace android { |
| 41 | namespace hardware { |
| 42 | namespace fastboot { |
Jing-yan, Jang | fa39fec | 2020-11-03 07:06:19 +0000 | [diff] [blame] | 43 | namespace V1_1 { |
Hridya Valsaraju | fcfbd96 | 2018-10-12 14:35:37 -0700 | [diff] [blame] | 44 | namespace implementation { |
| 45 | |
| 46 | constexpr const char* BRIGHTNESS_FILE = "/sys/class/backlight/panel0-backlight/brightness"; |
| 47 | constexpr int DISPLAY_BRIGHTNESS_DIM_THRESHOLD = 20; |
| 48 | |
| 49 | using OEMCommandHandler = std::function<Result(const std::vector<std::string>&)>; |
| 50 | |
| 51 | Return<void> Fastboot::getPartitionType(const ::android::hardware::hidl_string& /* partitionName */, |
| 52 | getPartitionType_cb _hidl_cb) { |
| 53 | // For bluecross devices, all partitions need to return raw. |
| 54 | _hidl_cb(FileSystemType::RAW, { Status::SUCCESS, "" }); |
| 55 | return Void(); |
| 56 | } |
| 57 | |
| 58 | Return<void> Fastboot::getVariant(getVariant_cb _hidl_cb) { |
| 59 | _hidl_cb("MSM USF", {Status::SUCCESS, "" }); |
| 60 | return Void(); |
| 61 | } |
| 62 | |
| 63 | Return<void> Fastboot::getOffModeChargeState(getOffModeChargeState_cb _hidl_cb) { |
| 64 | constexpr const char* kDevinfoPath = "/dev/block/by-name/devinfo"; |
| 65 | constexpr int kDevInfoOffModeChargeOffset = 15; |
| 66 | |
| 67 | uint8_t off_mode_charge_status = 0; |
| 68 | android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(kDevinfoPath, |
| 69 | O_RDONLY | O_BINARY))); |
| 70 | if (!android::base::ReadFullyAtOffset(fd, &off_mode_charge_status, 1 /* byte count */, |
| 71 | kDevInfoOffModeChargeOffset)) { |
| 72 | _hidl_cb(false, |
| 73 | { Status::FAILURE_UNKNOWN, "Unable to read off-mode-charge state" }); |
| 74 | } else { |
| 75 | _hidl_cb(off_mode_charge_status != 0, { Status::SUCCESS, "" }); |
| 76 | } |
| 77 | |
| 78 | return Void(); |
| 79 | } |
| 80 | |
| 81 | Return<void> Fastboot::getBatteryVoltageFlashingThreshold( |
| 82 | getBatteryVoltageFlashingThreshold_cb _hidl_cb) { |
| 83 | constexpr int kMinVoltageForFlashing = 3500; |
| 84 | _hidl_cb(kMinVoltageForFlashing, { Status::SUCCESS, "" }); |
| 85 | return Void(); |
| 86 | } |
| 87 | |
| 88 | Result SetBrightnessLevel(const std::vector<std::string>& args) { |
| 89 | if (!args.size()) { |
| 90 | return { Status::INVALID_ARGUMENT, "Brightness level unspecified" }; |
| 91 | } |
| 92 | |
| 93 | auto level = std::stoi(args[0]); |
| 94 | |
| 95 | if (level < 0 || level > 100) { |
| 96 | return { Status::INVALID_ARGUMENT, "Brighness level must be between 0 and 100" }; |
| 97 | } |
| 98 | |
| 99 | // Avoid screen being dimmed too much. |
| 100 | if (level < DISPLAY_BRIGHTNESS_DIM_THRESHOLD) { |
| 101 | level = DISPLAY_BRIGHTNESS_DIM_THRESHOLD; |
| 102 | } |
| 103 | |
| 104 | if (android::base::WriteStringToFile(std::to_string(level), BRIGHTNESS_FILE)) { |
| 105 | return { Status::SUCCESS, "" }; |
| 106 | } |
| 107 | |
| 108 | return { Status::FAILURE_UNKNOWN, "Unable to set display brightness" }; |
| 109 | } |
| 110 | |
| 111 | Return<void> Fastboot::doOemCommand(const ::android::hardware::hidl_string& oemCmdArgs, |
| 112 | doOemCommand_cb _hidl_cb) { |
| 113 | const std::unordered_map<std::string, OEMCommandHandler> kOEMCmdMap = { |
| 114 | {FB_OEM_SET_BRIGHTNESS, SetBrightnessLevel}, |
Hridya Valsaraju | fcfbd96 | 2018-10-12 14:35:37 -0700 | [diff] [blame] | 115 | }; |
| 116 | |
| 117 | auto args = android::base::Split(oemCmdArgs, " "); |
| 118 | if (args.size() < 2) { |
| 119 | _hidl_cb({ Status::INVALID_ARGUMENT, "Invalid OEM command" }); |
| 120 | return Void(); |
| 121 | } |
| 122 | |
| 123 | // args[0] will be "oem", args[1] will be the command name |
| 124 | auto cmd_handler = kOEMCmdMap.find(args[1]); |
| 125 | if (cmd_handler != kOEMCmdMap.end()) { |
| 126 | _hidl_cb(cmd_handler->second(std::vector<std::string>(args.begin() + 2, args.end()))); |
| 127 | } else { |
| 128 | _hidl_cb({ Status::FAILURE_UNKNOWN, "Unknown OEM command" }); |
| 129 | } |
| 130 | |
| 131 | return Void(); |
| 132 | } |
| 133 | |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 134 | static android::fs_mgr::Fstab fstab; |
| 135 | enum WipeVolumeStatus { |
| 136 | WIPE_OK = 0, |
| 137 | VOL_FSTAB, |
| 138 | VOL_UNKNOWN, |
| 139 | VOL_MOUNTED, |
| 140 | VOL_BLK_DEV_OPEN, |
| 141 | WIPE_ERROR_MAX = 0xffffffff, |
| 142 | }; |
| 143 | std::map<enum WipeVolumeStatus, std::string> wipe_vol_ret_msg{ |
| 144 | {WIPE_OK, ""}, |
| 145 | {VOL_FSTAB, "Unknown FS table"}, |
| 146 | {VOL_UNKNOWN, "Unknown volume"}, |
| 147 | {VOL_MOUNTED, "Fail to unmount volume"}, |
| 148 | {VOL_BLK_DEV_OPEN, "Fail to open block device"}, |
| 149 | {WIPE_ERROR_MAX, "Unknown wipe error"}}; |
| 150 | |
| 151 | enum WipeVolumeStatus wipe_volume(const std::string &volume) { |
| 152 | if (!android::fs_mgr::ReadDefaultFstab(&fstab)) { |
| 153 | return VOL_FSTAB; |
| 154 | } |
| 155 | const fs_mgr::FstabEntry *v = android::fs_mgr::GetEntryForPath(&fstab, volume); |
| 156 | if (v == nullptr) { |
| 157 | return VOL_UNKNOWN; |
| 158 | } |
| 159 | if (android::fs_mgr::EnsurePathUnmounted(&fstab, volume) != true) { |
| 160 | return VOL_MOUNTED; |
| 161 | } |
| 162 | |
| 163 | int fd = open(v->blk_device.c_str(), O_WRONLY | O_CREAT, 0644); |
| 164 | if (fd == -1) { |
| 165 | return VOL_BLK_DEV_OPEN; |
| 166 | } |
| 167 | wipe_block_device(fd, get_block_device_size(fd)); |
| 168 | close(fd); |
| 169 | |
| 170 | return WIPE_OK; |
| 171 | } |
| 172 | |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 173 | // Attempt to reuse a WipeKeys function that might be found in the recovery |
| 174 | // library in order to clear any digital car keys on the secure element. |
| 175 | bool WipeDigitalCarKeys(void) { |
| 176 | static constexpr const char *kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so"; |
| 177 | void *librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW); |
| 178 | if (librecovery_ui_ext == nullptr) { |
| 179 | // Dynamic library not found. Returning true since this likely |
| 180 | // means target does not support DCK. |
| 181 | return true; |
| 182 | } |
| 183 | |
| 184 | bool *(*WipeKeysFunc)(void *const); |
| 185 | reinterpret_cast<void *&>(WipeKeysFunc) = dlsym(librecovery_ui_ext, "WipeKeys"); |
| 186 | if (WipeKeysFunc == nullptr) { |
| 187 | // No WipeKeys implementation found. Returning true since this likely |
| 188 | // means target does not support DCK. |
| 189 | return true; |
| 190 | } |
| 191 | |
| 192 | return (*WipeKeysFunc)(nullptr); |
| 193 | } |
| 194 | |
Jing-yan, Jang | fa39fec | 2020-11-03 07:06:19 +0000 | [diff] [blame] | 195 | Return<void> Fastboot::doOemSpecificErase(V1_1::IFastboot::doOemSpecificErase_cb _hidl_cb) { |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 196 | // Erase metadata partition along with userdata partition. |
| 197 | // Keep erasing Titan M even if failing on this case. |
| 198 | auto wipe_status = wipe_volume("/metadata"); |
| 199 | |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 200 | bool dck_wipe_success = WipeDigitalCarKeys(); |
| 201 | |
Jing-yan, Jang | fa39fec | 2020-11-03 07:06:19 +0000 | [diff] [blame] | 202 | // Connect to Titan M |
| 203 | ::nos::NuggetClient client; |
| 204 | client.Open(); |
| 205 | if (!client.IsOpen()) { |
| 206 | _hidl_cb({ Status::FAILURE_UNKNOWN, "open Titan M fail" }); |
| 207 | return Void(); |
| 208 | } |
| 209 | |
| 210 | // Tell Titan M to wipe user data |
| 211 | const uint32_t magicValue = htole32(ERASE_CONFIRMATION); |
| 212 | std::vector<uint8_t> magic(sizeof(magicValue)); |
| 213 | memcpy(magic.data(), &magicValue, sizeof(magicValue)); |
| 214 | const uint8_t retry_count = 5; |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 215 | uint32_t nugget_status; |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 216 | for (uint8_t i = 0; i < retry_count; i++) { |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 217 | nugget_status = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr); |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 218 | if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK && dck_wipe_success) { |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 219 | _hidl_cb({Status::SUCCESS, wipe_vol_ret_msg[wipe_status]}); |
Jing-yan, Jang | fa39fec | 2020-11-03 07:06:19 +0000 | [diff] [blame] | 220 | return Void(); |
| 221 | } |
| 222 | } |
| 223 | |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 224 | // Return exactly what happened |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 225 | if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK && !dck_wipe_success) { |
| 226 | _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata, Titan M user data, and DCK"}); |
| 227 | } else if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) { |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 228 | _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data"}); |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 229 | } else if (nugget_status != APP_SUCCESS && !dck_wipe_success) { |
| 230 | _hidl_cb({Status::FAILURE_UNKNOWN, "Titan M user data and DCK wipe failed"}); |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 231 | } else if (nugget_status != APP_SUCCESS) { |
| 232 | _hidl_cb({Status::FAILURE_UNKNOWN, "Titan M user data wipe failed"}); |
Jeffrey Carlyle | 7b9a578 | 2022-07-27 21:39:25 +0000 | [diff] [blame] | 233 | } else if (wipe_status != WIPE_OK && !dck_wipe_success) { |
| 234 | _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata and DCK"}); |
| 235 | } else if (!dck_wipe_success) { |
| 236 | _hidl_cb({Status::FAILURE_UNKNOWN, "DCK wipe failed"}); |
Tommy Chiu | db652e5 | 2021-10-08 16:09:03 +0800 | [diff] [blame] | 237 | } else { |
| 238 | if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end()) |
| 239 | _hidl_cb({Status::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status]}); |
| 240 | else // Should not reach here, but handle it anyway |
| 241 | _hidl_cb({Status::FAILURE_UNKNOWN, "Unknown failure"}); |
| 242 | } |
| 243 | |
Jing-yan, Jang | fa39fec | 2020-11-03 07:06:19 +0000 | [diff] [blame] | 244 | return Void(); |
| 245 | } |
| 246 | |
Hridya Valsaraju | fcfbd96 | 2018-10-12 14:35:37 -0700 | [diff] [blame] | 247 | Fastboot::Fastboot() {} |
| 248 | |
| 249 | // Methods from ::android::hidl::base::V1_0::IBase follow. |
| 250 | |
| 251 | extern "C" IFastboot* HIDL_FETCH_IFastboot(const char* /* name */) { |
| 252 | return new Fastboot(); |
| 253 | } |
| 254 | |
| 255 | } // namespace implementation |
Jing-yan, Jang | fa39fec | 2020-11-03 07:06:19 +0000 | [diff] [blame] | 256 | } // namespace V1_1 |
Hridya Valsaraju | fcfbd96 | 2018-10-12 14:35:37 -0700 | [diff] [blame] | 257 | } // namespace fastboot |
| 258 | } // namespace hardware |
| 259 | } // namespace android |