1 /*
2  * Copyright (c) 2021-2022 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 "notification_shell_command.h"
17 
18 #include <getopt.h>
19 #include <iostream>
20 
21 #include "ans_const_define.h"
22 #include "ans_inner_errors.h"
23 #include "nativetoken_kit.h"
24 #include "notification_bundle_option.h"
25 #include "token_setproc.h"
26 #include "singleton.h"
27 
28 namespace OHOS {
29 namespace Notification {
30 namespace {
31 constexpr char COMMAND_ACTIVE[] = "active";
32 constexpr char COMMAND_RECENT[] = "recent";
33 #ifdef DISTRIBUTED_NOTIFICATION_SUPPORTED
34 constexpr char COMMAND_DISTRIBUTED[] = "distributed";
35 constexpr char SHORT_OPTIONS[] = "hARDb:u:r:";
36 #else
37 constexpr char SHORT_OPTIONS[] = "hARb:u:";
38 #endif
39 constexpr char COMMAND_SET_RECENT_COUNT[] = "setRecentCount";
40 const struct option LONG_OPTIONS[] = {
41     {"help", no_argument, nullptr, 'h'},
42     {COMMAND_ACTIVE, no_argument, nullptr, 'A'},
43     {COMMAND_RECENT, no_argument, nullptr, 'R'},
44 #ifdef DISTRIBUTED_NOTIFICATION_SUPPORTED
45     {COMMAND_DISTRIBUTED, no_argument, nullptr, 'D'},
46 #endif
47     {"bundle", required_argument, nullptr, 'b'},
48     {"user-id", required_argument, nullptr, 'u'},
49     {"receiver", required_argument, nullptr, 'r'},
50 };
51 constexpr char HELP_MSG[] =
52     "usage: anm <command> [<options>]\n"
53     "These are common commands list:\n"
54     "  help                         list available commands\n"
55     "  dump                         dump the info of notification\n"
56     "  setting                      notification setting\n";
57 constexpr char DUMP_HELP_MSG[] =
58     "usage: anm dump [<options>]\n"
59     "options list:\n"
60     "  --help, -h                   help menu\n"
61 #ifdef DISTRIBUTED_NOTIFICATION_SUPPORTED
62     "  --distributed, -D            list all distributed notifications by remote device\n"
63 #endif
64     "  --active,  -A                 list all active notifications\n"
65     "  --recent,  -R                 list recent notifications\n"
66     "  --bundle,  -b  <name>         dump the info filter by the specified bundle name\n"
67     "  --user-id, -u  <userId>       dump the info filter by the specified userId\n"
68     "  --receiver, -r  <userId>       dump the info filter by the specified receiver userId\n";
69 
70 constexpr char SETTING_SHORT_OPTIONS[] = "c:e:d:";
71 const struct option SETTING_LONG_OPTIONS[] = {
72     {"help", no_argument, nullptr, 'h'},
73     {"recent-count", required_argument, nullptr, 'c'},
74     {"enable-notification", required_argument, nullptr, 'e'},
75     {"set-device-status", required_argument, nullptr, 'd'},
76 };
77 constexpr char SETTING_HELP_MSG[] =
78     "usage: anm setting [<options>]\n"
79     "options list:\n"
80     "  --help, -h                   help menu\n"
81     "  --recent-count -c <number>   set the max count of recent notifications keeping in memory\n"
82     "  --enable-notification -e <bundleName:uid:enable> set notification enabled for the bundle, eg: -e com.example:10100:1\n"
83     "  --set-device-status -d <device:status> set device status, eg: -d device:1\n";
84 }  // namespace
85 
NotificationShellCommand(int argc,char * argv[])86 NotificationShellCommand::NotificationShellCommand(int argc, char *argv[]) : ShellCommand(argc, argv, "anm_dump")
87 {}
88 
CreateCommandMap()89 ErrCode NotificationShellCommand::CreateCommandMap()
90 {
91     commandMap_ = {
92         {"help", std::bind(&NotificationShellCommand::RunAsHelpCommand, this)},
93         {"dump", std::bind(&NotificationShellCommand::RunAsDumpCommand, this)},
94         {"setting", std::bind(&NotificationShellCommand::RunAsSettingCommand, this)},
95     };
96     return ERR_OK;
97 }
98 
Init()99 ErrCode NotificationShellCommand::Init()
100 {
101     SetNativeToken();
102     ErrCode result = OHOS::ERR_OK;
103     if (!ans_) {
104         ans_ = DelayedSingleton<AnsNotification>::GetInstance();
105     }
106     if (!ans_) {
107         result = OHOS::ERR_INVALID_VALUE;
108     }
109     return result;
110 }
111 
SetNativeToken()112 void NotificationShellCommand::SetNativeToken()
113 {
114     uint64_t tokenId;
115     const char **perms = new (std::nothrow) const char *[1];
116     if (perms == nullptr) {
117         ANS_LOGE("Failed to create buffer.");
118         return;
119     }
120     perms[0] = "ohos.permission.NOTIFICATION_CONTROLLER";
121     NativeTokenInfoParams infoInstance = {
122         .dcapsNum = 0,
123         .permsNum = 1,
124         .aclsNum = 0,
125         .dcaps = nullptr,
126         .perms = perms,
127         .acls = nullptr,
128         .aplStr = "system_basic",
129     };
130 
131     infoInstance.processName = "anm";
132     tokenId = GetAccessTokenId(&infoInstance);
133     SetSelfTokenID(tokenId);
134     delete[] perms;
135 }
136 
RunAsHelpCommand()137 ErrCode NotificationShellCommand::RunAsHelpCommand()
138 {
139     resultReceiver_.append(HELP_MSG);
140     return ERR_OK;
141 }
142 
RunHelp()143 ErrCode NotificationShellCommand::RunHelp()
144 {
145     resultReceiver_.append(DUMP_HELP_MSG);
146     return ERR_OK;
147 }
148 
RunAsDumpCommand()149 ErrCode NotificationShellCommand::RunAsDumpCommand()
150 {
151 #ifdef ANM_BUILD_VARIANT_USER
152      resultReceiver_.append("error: user version cannot use dump.\n");
153      return ERR_INVALID_VALUE;
154 #endif
155     ErrCode ret = ERR_OK;
156     std::vector<std::string> infos;
157     std::string cmd;
158     std::string bundle;
159     int32_t userId = SUBSCRIBE_USER_INIT;
160     int32_t recvUserId = SUBSCRIBE_USER_INIT;
161     SetDumpCmdInfo(cmd, bundle, userId, ret, recvUserId);
162     if (ret != ERR_OK) {
163         return ret;
164     }
165     if (cmd.empty()) {
166         resultReceiver_.clear();
167         resultReceiver_ = "request a option 'A' or 'R' or 'D'\n";
168         resultReceiver_.append(DUMP_HELP_MSG);
169         return ERR_INVALID_VALUE;
170     }
171 
172     ret = RunDumpCmd(cmd, bundle, userId, recvUserId, infos);
173     int index = 0;
174     for (const auto &info : infos) {
175         resultReceiver_.append("No." + std::to_string(++index) + "\n");
176         resultReceiver_.append(info);
177     }
178     return ret;
179 }
180 
RunDumpCmd(const std::string & cmd,const std::string & bundle,int32_t userId,int32_t recvUserId,std::vector<std::string> & infos)181 ErrCode NotificationShellCommand::RunDumpCmd(const std::string& cmd, const std::string& bundle,
182     int32_t userId, int32_t recvUserId, std::vector<std::string> &infos)
183 {
184     if (ans_ != nullptr) {
185         ErrCode ret = ans_->ShellDump(cmd, bundle, userId, recvUserId, infos);
186         if (strncmp(cmd.c_str(), COMMAND_SET_RECENT_COUNT, strlen(COMMAND_SET_RECENT_COUNT)) == 0) {
187             if (ret == ERR_OK) {
188                 resultReceiver_.append("set recent count success\n");
189             } else {
190                 resultReceiver_.append("set recent count failed\n");
191             }
192         } else {
193             resultReceiver_.append("Total:" + std::to_string(infos.size()) + "\n");
194         }
195         return ret;
196     }
197     return ERR_ANS_SERVICE_NOT_CONNECTED;
198 }
199 
SetDumpCmdInfo(std::string & cmd,std::string & bundle,int32_t & userId,ErrCode & ret,int32_t & recvUserId)200 void NotificationShellCommand::SetDumpCmdInfo(std::string &cmd, std::string &bundle, int32_t &userId,
201     ErrCode &ret, int32_t &recvUserId)
202 {
203     int option = -1;
204     bool hasOption = false;
205     while ((option = getopt_long(argc_, argv_, SHORT_OPTIONS, LONG_OPTIONS, nullptr)) != -1) {
206         if (option == '?') {
207             CheckDumpOpt();
208             resultReceiver_.append(DUMP_HELP_MSG);
209             ret = ERR_INVALID_VALUE;
210             return;
211         }
212         hasOption = true;
213         switch (option) {
214             case 'h':
215                 ret = RunHelp();
216                 break;
217             case 'A':
218                 cmd = COMMAND_ACTIVE;
219                 break;
220             case 'R':
221                 cmd = COMMAND_RECENT;
222                 break;
223 #ifdef DISTRIBUTED_NOTIFICATION_SUPPORTED
224             case 'D':
225                 cmd = COMMAND_DISTRIBUTED;
226                 break;
227 #endif
228             case 'b':
229                 bundle = optarg;
230                 break;
231             case 'u':
232                 userId = atoi(optarg);
233                 break;
234             case 'r':
235                 recvUserId = atoi(optarg);
236                 break;
237             default:
238                 resultReceiver_.append(DUMP_HELP_MSG);
239                 break;
240         }
241     }
242     if (!hasOption) {
243         resultReceiver_.append(DUMP_HELP_MSG);
244         ret = ERR_INVALID_VALUE;
245     }
246 }
247 
CheckDumpOpt()248 void NotificationShellCommand::CheckDumpOpt()
249 {
250     switch (optopt) {
251         case 'b':
252             resultReceiver_.append("error: option 'b' requires a value.\n");
253             break;
254         case 'u':
255             resultReceiver_.append("error: option 'u' requires a value.\n");
256             break;
257         case 'r':
258             resultReceiver_.append("error: option 'r' requires a value.\n");
259             break;
260         default:
261             resultReceiver_.append("error: unknown option.\n");
262             break;
263     }
264 }
265 
RunAsSettingCommand()266 ErrCode NotificationShellCommand::RunAsSettingCommand()
267 {
268 #ifdef ANM_BUILD_VARIANT_USER
269      resultReceiver_.append("error: user version cannot use setting.\n");
270      return ERR_INVALID_VALUE;
271 #endif
272     int option = getopt_long(argc_, argv_, SETTING_SHORT_OPTIONS, SETTING_LONG_OPTIONS, nullptr);
273     if (option == '?') {
274         if (optopt == 'c') {
275             resultReceiver_.append("error: option 'c' requires a value.\n");
276         } else if (optopt == 'e') {
277             resultReceiver_.append("error: option 'e' requires a value.\n");
278         } else if (optopt == 'd') {
279             resultReceiver_.append("error: option 'd' requires a value.\n");
280         } else {
281             resultReceiver_.append("error: unknown option.\n");
282         }
283         resultReceiver_.append(SETTING_HELP_MSG);
284         return ERR_INVALID_VALUE;
285     }
286     if (option == 'c') {
287         int32_t count = atoi(optarg);
288         if ((count < NOTIFICATION_MIN_COUNT) || (count > NOTIFICATION_MAX_COUNT)) {
289             resultReceiver_.append("error: recent count should between 1 and 1024\n");
290             resultReceiver_.append(SETTING_HELP_MSG);
291             return ERR_INVALID_VALUE;
292         }
293         std::vector<std::string> infos;
294         std::string cmd = COMMAND_SET_RECENT_COUNT;
295         cmd.append(" ").append(std::string(optarg));
296         return RunDumpCmd(cmd, "", SUBSCRIBE_USER_INIT, SUBSCRIBE_USER_INIT, infos);
297     }
298     if (option == 'e') {
299         return RunSetEnableCmd();
300     }
301     if (option == 'd') {
302         return RunSetDeviceStatusCmd();
303     }
304 
305     resultReceiver_.append(SETTING_HELP_MSG);
306     return ERR_INVALID_VALUE;
307 }
308 
RunSetEnableCmd()309 ErrCode NotificationShellCommand::RunSetEnableCmd()
310 {
311     if (ans_ == nullptr) {
312         resultReceiver_.append("error: object is null\n");
313         return ERR_ANS_SERVICE_NOT_CONNECTED;
314     }
315 
316     NotificationBundleOption bundleOption;
317     std::string info = std::string(optarg);
318     if (std::count(info.begin(), info.end(), ':') != 2) {  // 2 (bundleName:uid:enable)
319         resultReceiver_.append("error: setting information error\n");
320         resultReceiver_.append(SETTING_HELP_MSG);
321         return ERR_INVALID_VALUE;
322     }
323 
324     size_t pos = info.find(':');
325     bundleOption.SetBundleName(info.substr(0, pos));
326     info = info.substr(pos + 1);
327     pos = info.find(':');
328     bundleOption.SetUid(atoi(info.substr(0, pos).c_str()));
329     bool enable = atoi(info.substr(pos + 1).c_str());
330 
331     ErrCode ret = ans_->SetNotificationsEnabledForSpecifiedBundle(bundleOption, "", enable);
332     if (ret == ERR_OK) {
333         resultReceiver_.append("set notification enabled success\n");
334     } else {
335         resultReceiver_.append("set notification enabled failed\n");
336     }
337     return ret;
338 }
339 
RunSetDeviceStatusCmd()340 ErrCode NotificationShellCommand::RunSetDeviceStatusCmd()
341 {
342     if (ans_ == nullptr) {
343         resultReceiver_.append("error: object is null\n");
344         return ERR_ANS_SERVICE_NOT_CONNECTED;
345     }
346 
347     std::string deviceType;
348     uint32_t status = 0;
349     std::string info = std::string(optarg);
350     if (std::count(info.begin(), info.end(), ':') != 1) {  // 1 (deviceType:status)
351         resultReceiver_.append("error: setting information error\n");
352         resultReceiver_.append(SETTING_HELP_MSG);
353         return ERR_INVALID_VALUE;
354     }
355 
356     size_t pos = info.find(':');
357     deviceType = info.substr(0, pos);
358     status = atoi(info.substr(pos + 1).c_str());
359 
360     ErrCode ret = ans_->SetTargetDeviceStatus(deviceType, status);
361     if (ret == ERR_OK) {
362         resultReceiver_.append("set device status success\n");
363     } else {
364         resultReceiver_.append("set device status failed\n");
365     }
366     return ret;
367 }
368 }  // namespace Notification
369 }  // namespace OHOS
370