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