1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "devicestatus_dumper.h"
17
18 #include <cinttypes>
19 #include <cstring>
20 #include <getopt.h>
21 #include <iomanip>
22 #include <map>
23 #include <sstream>
24
25 #include <ipc_skeleton.h>
26 #include "securec.h"
27 #include "string_ex.h"
28 #include "unique_fd.h"
29
30 #include "devicestatus_common.h"
31 #include "devicestatus_define.h"
32 #include "include/util.h"
33
34 #undef LOG_TAG
35 #define LOG_TAG "DeviceStatusDumper"
36
37 namespace OHOS {
38 namespace Msdp {
39 namespace DeviceStatus {
40 namespace {
41 constexpr size_t MAX_DEVICE_STATUS_SIZE { 10 };
42 } // namespace
43
DeviceStatusDumper()44 DeviceStatusDumper::DeviceStatusDumper() {}
~DeviceStatusDumper()45 DeviceStatusDumper::~DeviceStatusDumper() {}
46
Init(IContext * context)47 int32_t DeviceStatusDumper::Init(IContext *context)
48 {
49 CALL_DEBUG_ENTER;
50 CHKPR(context, RET_ERR);
51 context_ = context;
52 return RET_OK;
53 }
54
ParseCommand(int32_t fd,const std::vector<std::string> & args,const std::vector<Data> & datas)55 void DeviceStatusDumper::ParseCommand(int32_t fd, const std::vector<std::string> &args, const std::vector<Data> &datas)
56 {
57 constexpr size_t BUFSIZE { 1024 };
58 char buf[BUFSIZE] { "hidumper" };
59
60 std::vector<char *> argv(args.size() + 1);
61 argv[0] = buf;
62
63 size_t len = std::strlen(buf) + 1;
64 char *pbuf = buf + len;
65 size_t bufLen = sizeof(buf) - len;
66
67 for (size_t index = 0, cnt = args.size(); index < cnt; ++index) {
68 len = args[index].size() + 1;
69 if (len > bufLen) {
70 FI_HILOGE("Buffer overflow");
71 return;
72 }
73 args[index].copy(pbuf, args[index].size());
74 pbuf[args[index].size()] = '\0';
75
76 argv[index + 1] = pbuf;
77 pbuf += len;
78 bufLen -= len;
79 }
80
81 struct option dumpOptions[] {
82 { "help", no_argument, nullptr, 'h' },
83 { "subscribe", no_argument, nullptr, 's' },
84 { "list", no_argument, nullptr, 'l' },
85 { "current", no_argument, nullptr, 'c' },
86 { "coordination", no_argument, nullptr, 'o' },
87 { "drag", no_argument, nullptr, 'd' },
88 { "macroState", no_argument, nullptr, 'm' },
89 { nullptr, 0, nullptr, 0 }
90 };
91 optind = 0;
92
93 for (;;) {
94 int32_t opt = getopt_long(argv.size(), argv.data(), "+hslcodm", dumpOptions, nullptr);
95 if (opt < 0) {
96 break;
97 }
98 ExecutDump(fd, datas, opt);
99 }
100 }
101
ExecutDump(int32_t fd,const std::vector<Data> & datas,int32_t opt)102 void DeviceStatusDumper::ExecutDump(int32_t fd, const std::vector<Data> &datas, int32_t opt)
103 {
104 switch (opt) {
105 case 'h': {
106 DumpHelpInfo(fd);
107 break;
108 }
109 case 's': {
110 DumpDeviceStatusSubscriber(fd);
111 break;
112 }
113 case 'l': {
114 DumpDeviceStatusChanges(fd);
115 break;
116 }
117 case 'c': {
118 DumpDeviceStatusCurrentStatus(fd, datas);
119 break;
120 }
121 case 'o': {
122 #ifdef OHOS_BUILD_ENABLE_COORDINATION
123 dprintf(fd, "device coordination is not supported\n");
124 #endif // OHOS_BUILD_ENABLE_COORDINATION
125 break;
126 }
127 case 'd': {
128 CHKPV(context_);
129 context_->GetDragManager().Dump(fd);
130 break;
131 }
132 case 'm': {
133 DumpCheckDefine(fd);
134 break;
135 }
136 default: {
137 dprintf(fd, "cmd param is error\n");
138 DumpHelpInfo(fd);
139 break;
140 }
141 }
142 }
143
DumpDeviceStatusSubscriber(int32_t fd)144 void DeviceStatusDumper::DumpDeviceStatusSubscriber(int32_t fd)
145 {
146 CALL_DEBUG_ENTER;
147 std::unique_lock lock(mutex_);
148 if (appInfos_.empty()) {
149 FI_HILOGE("appInfos_ is empty");
150 return;
151 }
152 std::string startTime;
153 GetTimeStamp(startTime);
154 dprintf(fd, "Current time:%s \n", startTime.c_str());
155 for (const auto &item : appInfos_) {
156 for (const auto &appInfo : item.second) {
157 dprintf(fd, "startTime:%s | uid:%d | pid:%d | type:%s | packageName:%s\n",
158 appInfo->startTime.c_str(), appInfo->uid, appInfo->pid, GetStatusType(appInfo->type).c_str(),
159 appInfo->packageName.c_str());
160 }
161 }
162 }
163
DumpDeviceStatusChanges(int32_t fd)164 void DeviceStatusDumper::DumpDeviceStatusChanges(int32_t fd)
165 {
166 CALL_DEBUG_ENTER;
167 std::unique_lock lock(mutex_);
168 if (deviceStatusQueue_.empty()) {
169 FI_HILOGI("deviceStatusQueue_ is empty");
170 return;
171 }
172 std::string startTime;
173 GetTimeStamp(startTime);
174 dprintf(fd, "Current time:%s\n", startTime.c_str());
175 size_t length = deviceStatusQueue_.size() > MAX_DEVICE_STATUS_SIZE ?
176 MAX_DEVICE_STATUS_SIZE : deviceStatusQueue_.size();
177 for (size_t i = 0; i < length; ++i) {
178 auto record = deviceStatusQueue_.front();
179 CHKPC(record);
180 deviceStatusQueue_.push(record);
181 deviceStatusQueue_.pop();
182 dprintf(fd, "startTime:%s | type:%s | value:%s\n",
183 record->startTime.c_str(), GetStatusType(record->data.type).c_str(),
184 GetDeviceState(record->data.value).c_str());
185 }
186 }
187
DumpDeviceStatusCurrentStatus(int32_t fd,const std::vector<Data> & datas) const188 void DeviceStatusDumper::DumpDeviceStatusCurrentStatus(int32_t fd, const std::vector<Data> &datas) const
189 {
190 CALL_DEBUG_ENTER;
191 std::string startTime;
192 GetTimeStamp(startTime);
193 dprintf(fd, "Current time:%s\n", startTime.c_str());
194 dprintf(fd, "Current device status:\n");
195 if (datas.empty()) {
196 dprintf(fd, "No device status available\n");
197 return;
198 }
199 for (auto it = datas.begin(); it != datas.end(); ++it) {
200 if (it->value == VALUE_INVALID) {
201 continue;
202 }
203 dprintf(fd, "type:%s | state:%s\n",
204 GetStatusType(it->type).c_str(), GetDeviceState(it->value).c_str());
205 }
206 }
207
GetDeviceState(OnChangedValue value) const208 std::string DeviceStatusDumper::GetDeviceState(OnChangedValue value) const
209 {
210 std::string state;
211 switch (value) {
212 case VALUE_ENTER: {
213 state = "enter";
214 break;
215 }
216 case VALUE_EXIT: {
217 state = "exit";
218 break;
219 }
220 case VALUE_INVALID: {
221 state = "invalid";
222 break;
223 }
224 default: {
225 state = "unknown";
226 break;
227 }
228 }
229 return state;
230 }
231
GetStatusType(Type type) const232 std::string DeviceStatusDumper::GetStatusType(Type type) const
233 {
234 std::string stateType;
235 switch (type) {
236 case TYPE_ABSOLUTE_STILL: {
237 stateType = "absolute still";
238 break;
239 }
240 case TYPE_HORIZONTAL_POSITION: {
241 stateType = "horizontal position";
242 break;
243 }
244 case TYPE_VERTICAL_POSITION: {
245 stateType = "vertical position";
246 break;
247 }
248 case TYPE_LID_OPEN: {
249 stateType = "lid open";
250 break;
251 }
252 default: {
253 stateType = "unknown";
254 break;
255 }
256 }
257 return stateType;
258 }
259
DumpHelpInfo(int32_t fd) const260 void DeviceStatusDumper::DumpHelpInfo(int32_t fd) const
261 {
262 dprintf(fd, "Usage:\n");
263 dprintf(fd, " -h: dump help\n");
264 dprintf(fd, " -s: dump the subscribers\n");
265 dprintf(fd, " -l: dump the last 10 device status change\n");
266 dprintf(fd, " -c: dump the current device status\n");
267 dprintf(fd, " -o: dump the coordination status\n");
268 dprintf(fd, " -d: dump the drag status\n");
269 dprintf(fd, " -m, dump the macro state\n");
270 }
271
SaveAppInfo(std::shared_ptr<AppInfo> appInfo)272 void DeviceStatusDumper::SaveAppInfo(std::shared_ptr<AppInfo> appInfo)
273 {
274 CALL_DEBUG_ENTER;
275 CHKPV(appInfo);
276 GetTimeStamp(appInfo->startTime);
277 std::set<std::shared_ptr<AppInfo>> appInfos;
278 std::unique_lock lock(mutex_);
279 auto iter = appInfos_.find(appInfo->type);
280 if (iter == appInfos_.end()) {
281 if (appInfos.insert(appInfo).second) {
282 auto [_, ret] = appInfos_.insert(std::make_pair(appInfo->type, appInfos));
283 if (!ret) {
284 FI_HILOGW("type is duplicated");
285 }
286 }
287 } else {
288 if (!appInfos_[iter->first].insert(appInfo).second) {
289 FI_HILOGW("appInfo is duplicated");
290 }
291 }
292 }
293
RemoveAppInfo(std::shared_ptr<AppInfo> appInfo)294 void DeviceStatusDumper::RemoveAppInfo(std::shared_ptr<AppInfo> appInfo)
295 {
296 CALL_DEBUG_ENTER;
297 CHKPV(appInfo);
298 CHKPV(appInfo->callback);
299 std::unique_lock lock(mutex_);
300 auto appInfoSetIter = appInfos_.find(appInfo->type);
301 if (appInfoSetIter == appInfos_.end()) {
302 FI_HILOGE("Not exist %{public}d type appInfo", appInfo->type);
303 return;
304 }
305 FI_HILOGI("callbacklist type:%{public}d, size:%{public}zu, appInfoMap size:%{public}zu",
306 appInfo->type, appInfos_[appInfoSetIter->first].size(), appInfos_.size());
307 auto iter = appInfos_.find(appInfo->type);
308 if (iter == appInfos_.end()) {
309 FI_HILOGW("Remove app info is not exists");
310 return;
311 }
312 for (const auto &item : iter->second) {
313 if (item->pid == appInfo->pid) {
314 iter->second.erase(item);
315 break;
316 }
317 }
318 }
319
PushDeviceStatus(const Data & data)320 void DeviceStatusDumper::PushDeviceStatus(const Data &data)
321 {
322 CALL_DEBUG_ENTER;
323 std::unique_lock lock(mutex_);
324 auto record = std::make_shared<DeviceStatusRecord>();
325 GetTimeStamp(record->startTime);
326 record->data = data;
327 deviceStatusQueue_.push(record);
328 if (deviceStatusQueue_.size() > MAX_DEVICE_STATUS_SIZE) {
329 deviceStatusQueue_.pop();
330 }
331 }
332
GetPackageName(Security::AccessToken::AccessTokenID tokenId)333 std::string DeviceStatusDumper::GetPackageName(Security::AccessToken::AccessTokenID tokenId)
334 {
335 CALL_DEBUG_ENTER;
336 std::string packageName = "unknown";
337 int32_t tokenType = Security::AccessToken::AccessTokenKit::GetTokenTypeFlag(tokenId);
338 switch (tokenType) {
339 case Security::AccessToken::ATokenTypeEnum::TOKEN_NATIVE: {
340 Security::AccessToken::NativeTokenInfo tokenInfo;
341 if (Security::AccessToken::AccessTokenKit::GetNativeTokenInfo(tokenId, tokenInfo) != 0) {
342 FI_HILOGE("Get native token info fail");
343 return packageName;
344 }
345 packageName = tokenInfo.processName;
346 break;
347 }
348 case Security::AccessToken::ATokenTypeEnum::TOKEN_HAP: {
349 Security::AccessToken::HapTokenInfo hapInfo;
350 if (Security::AccessToken::AccessTokenKit::GetHapTokenInfo(tokenId, hapInfo) != RET_OK) {
351 FI_HILOGE("Get hap token info fail");
352 return packageName;
353 }
354 packageName = hapInfo.bundleName;
355 break;
356 }
357 default: {
358 FI_HILOGW("token type not match");
359 break;
360 }
361 }
362 return packageName;
363 }
364
DumpCheckDefine(int32_t fd)365 void DeviceStatusDumper::DumpCheckDefine(int32_t fd)
366 {
367 ChkDefineOutput(fd);
368 }
369
ChkDefineOutput(int32_t fd)370 void DeviceStatusDumper::ChkDefineOutput(int32_t fd)
371 {
372 CheckDefineOutput(fd, "Macro switch state:\n");
373 #ifdef OHOS_BUILD_ENABLE_COORDINATION
374 CheckDefineOutput(fd, "%-40s", "OHOS_BUILD_ENABLE_COORDINATION");
375 #endif // OHOS_BUILD_ENABLE_COORDINATION
376 }
377
378 template<class ...Ts>
CheckDefineOutput(int32_t fd,const char * fmt,Ts...args)379 void DeviceStatusDumper::CheckDefineOutput(int32_t fd, const char* fmt, Ts... args)
380 {
381 CALL_DEBUG_ENTER;
382 CHKPV(fmt);
383 char buf[MAX_PACKET_BUF_SIZE] = { 0 };
384 int32_t ret = snprintf_s(buf, MAX_PACKET_BUF_SIZE, MAX_PACKET_BUF_SIZE - 1, fmt, args...);
385 if (ret == -1) {
386 FI_HILOGE("Call snprintf_s failed, ret:%{public}d", ret);
387 return;
388 }
389 dprintf(fd, "%s", buf);
390 }
391 } // namespace DeviceStatus
392 } // namespace Msdp
393 } // namespace OHOS
394