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