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
19 #include <string>
20 #include <unordered_map>
21 #include <vector>
22 #include <map>
23
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <android-base/strings.h>
27 #include <android-base/unique_fd.h>
28
29 // FS headers
30 #include <ext4_utils/wipe.h>
31 #include <fs_mgr.h>
32 #include <fs_mgr/roots.h>
33
34 // Nugget headers
35 #include <app_nugget.h>
36 #include <nos/NuggetClient.h>
37 #include <nos/debug.h>
38
39 namespace android {
40 namespace hardware {
41 namespace fastboot {
42 namespace V1_1 {
43 namespace implementation {
44
45 constexpr const char* BRIGHTNESS_FILE = "/sys/class/backlight/panel0-backlight/brightness";
46 constexpr int DISPLAY_BRIGHTNESS_DIM_THRESHOLD = 20;
47
48 using OEMCommandHandler = std::function<Result(const std::vector<std::string>&)>;
49
getPartitionType(const::android::hardware::hidl_string &,getPartitionType_cb _hidl_cb)50 Return<void> Fastboot::getPartitionType(const ::android::hardware::hidl_string& /* partitionName */,
51 getPartitionType_cb _hidl_cb) {
52 // For bluecross devices, all partitions need to return raw.
53 _hidl_cb(FileSystemType::RAW, { Status::SUCCESS, "" });
54 return Void();
55 }
56
getVariant(getVariant_cb _hidl_cb)57 Return<void> Fastboot::getVariant(getVariant_cb _hidl_cb) {
58 _hidl_cb("MSM USF", {Status::SUCCESS, "" });
59 return Void();
60 }
61
getOffModeChargeState(getOffModeChargeState_cb _hidl_cb)62 Return<void> Fastboot::getOffModeChargeState(getOffModeChargeState_cb _hidl_cb) {
63 constexpr const char* kDevinfoPath = "/dev/block/by-name/devinfo";
64 constexpr int kDevInfoOffModeChargeOffset = 15;
65
66 uint8_t off_mode_charge_status = 0;
67 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(kDevinfoPath,
68 O_RDONLY | O_BINARY)));
69 if (!android::base::ReadFullyAtOffset(fd, &off_mode_charge_status, 1 /* byte count */,
70 kDevInfoOffModeChargeOffset)) {
71 _hidl_cb(false,
72 { Status::FAILURE_UNKNOWN, "Unable to read off-mode-charge state" });
73 } else {
74 _hidl_cb(off_mode_charge_status != 0, { Status::SUCCESS, "" });
75 }
76
77 return Void();
78 }
79
getBatteryVoltageFlashingThreshold(getBatteryVoltageFlashingThreshold_cb _hidl_cb)80 Return<void> Fastboot::getBatteryVoltageFlashingThreshold(
81 getBatteryVoltageFlashingThreshold_cb _hidl_cb) {
82 constexpr int kMinVoltageForFlashing = 3500;
83 _hidl_cb(kMinVoltageForFlashing, { Status::SUCCESS, "" });
84 return Void();
85 }
86
SetBrightnessLevel(const std::vector<std::string> & args)87 Result SetBrightnessLevel(const std::vector<std::string>& args) {
88 if (!args.size()) {
89 return { Status::INVALID_ARGUMENT, "Brightness level unspecified" };
90 }
91
92 auto level = std::stoi(args[0]);
93
94 if (level < 0 || level > 100) {
95 return { Status::INVALID_ARGUMENT, "Brighness level must be between 0 and 100" };
96 }
97
98 // Avoid screen being dimmed too much.
99 if (level < DISPLAY_BRIGHTNESS_DIM_THRESHOLD) {
100 level = DISPLAY_BRIGHTNESS_DIM_THRESHOLD;
101 }
102
103 if (android::base::WriteStringToFile(std::to_string(level), BRIGHTNESS_FILE)) {
104 return { Status::SUCCESS, "" };
105 }
106
107 return { Status::FAILURE_UNKNOWN, "Unable to set display brightness" };
108 }
109
doOemCommand(const::android::hardware::hidl_string & oemCmdArgs,doOemCommand_cb _hidl_cb)110 Return<void> Fastboot::doOemCommand(const ::android::hardware::hidl_string& oemCmdArgs,
111 doOemCommand_cb _hidl_cb) {
112 const std::unordered_map<std::string, OEMCommandHandler> kOEMCmdMap = {
113 {FB_OEM_SET_BRIGHTNESS, SetBrightnessLevel},
114 };
115
116 auto args = android::base::Split(oemCmdArgs, " ");
117 if (args.size() < 2) {
118 _hidl_cb({ Status::INVALID_ARGUMENT, "Invalid OEM command" });
119 return Void();
120 }
121
122 // args[0] will be "oem", args[1] will be the command name
123 auto cmd_handler = kOEMCmdMap.find(args[1]);
124 if (cmd_handler != kOEMCmdMap.end()) {
125 _hidl_cb(cmd_handler->second(std::vector<std::string>(args.begin() + 2, args.end())));
126 } else {
127 _hidl_cb({ Status::FAILURE_UNKNOWN, "Unknown OEM command" });
128 }
129
130 return Void();
131 }
132
133 static android::fs_mgr::Fstab fstab;
134 enum WipeVolumeStatus {
135 WIPE_OK = 0,
136 VOL_FSTAB,
137 VOL_UNKNOWN,
138 VOL_MOUNTED,
139 VOL_BLK_DEV_OPEN,
140 WIPE_ERROR_MAX = 0xffffffff,
141 };
142 std::map<enum WipeVolumeStatus, std::string> wipe_vol_ret_msg{
143 {WIPE_OK, ""},
144 {VOL_FSTAB, "Unknown FS table"},
145 {VOL_UNKNOWN, "Unknown volume"},
146 {VOL_MOUNTED, "Fail to unmount volume"},
147 {VOL_BLK_DEV_OPEN, "Fail to open block device"},
148 {WIPE_ERROR_MAX, "Unknown wipe error"}};
149
wipe_volume(const std::string & volume)150 enum WipeVolumeStatus wipe_volume(const std::string &volume) {
151 if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
152 return VOL_FSTAB;
153 }
154 const fs_mgr::FstabEntry *v = android::fs_mgr::GetEntryForPath(&fstab, volume);
155 if (v == nullptr) {
156 return VOL_UNKNOWN;
157 }
158 if (android::fs_mgr::EnsurePathUnmounted(&fstab, volume) != true) {
159 return VOL_MOUNTED;
160 }
161
162 int fd = open(v->blk_device.c_str(), O_WRONLY | O_CREAT, 0644);
163 if (fd == -1) {
164 return VOL_BLK_DEV_OPEN;
165 }
166 wipe_block_device(fd, get_block_device_size(fd));
167 close(fd);
168
169 return WIPE_OK;
170 }
171
doOemSpecificErase(V1_1::IFastboot::doOemSpecificErase_cb _hidl_cb)172 Return<void> Fastboot::doOemSpecificErase(V1_1::IFastboot::doOemSpecificErase_cb _hidl_cb) {
173 // Erase metadata partition along with userdata partition.
174 // Keep erasing Titan M even if failing on this case.
175 auto wipe_status = wipe_volume("/metadata");
176
177 // Connect to Titan M
178 ::nos::NuggetClient client;
179 client.Open();
180 if (!client.IsOpen()) {
181 _hidl_cb({ Status::FAILURE_UNKNOWN, "open Titan M fail" });
182 return Void();
183 }
184
185 // Tell Titan M to wipe user data
186 const uint32_t magicValue = htole32(ERASE_CONFIRMATION);
187 std::vector<uint8_t> magic(sizeof(magicValue));
188 memcpy(magic.data(), &magicValue, sizeof(magicValue));
189 const uint8_t retry_count = 5;
190 uint32_t nugget_status;
191 for(uint8_t i = 0; i < retry_count; i++) {
192 nugget_status = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr);
193 if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK) {
194 _hidl_cb({Status::SUCCESS, wipe_vol_ret_msg[wipe_status]});
195 return Void();
196 }
197 }
198
199 // Return exactly what happened
200 if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
201 _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data"});
202 } else if (nugget_status != APP_SUCCESS) {
203 _hidl_cb({Status::FAILURE_UNKNOWN, "Titan M user data wipe failed"});
204 } else {
205 if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
206 _hidl_cb({Status::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status]});
207 else // Should not reach here, but handle it anyway
208 _hidl_cb({Status::FAILURE_UNKNOWN, "Unknown failure"});
209 }
210
211 return Void();
212 }
213
Fastboot()214 Fastboot::Fastboot() {}
215
216 // Methods from ::android::hidl::base::V1_0::IBase follow.
217
HIDL_FETCH_IFastboot(const char *)218 extern "C" IFastboot* HIDL_FETCH_IFastboot(const char* /* name */) {
219 return new Fastboot();
220 }
221
222 } // namespace implementation
223 } // namespace V1_1
224 } // namespace fastboot
225 } // namespace hardware
226 } // namespace android
227