1 /*
2 * Copyright (c) 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 #include "first_use_dialog.h"
16
17 #include <fcntl.h>
18 #include <fstream>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include "ability_manager_client.h"
23 #include "accesstoken_kit.h"
24 #include "sec_comp_err.h"
25 #include "sec_comp_log.h"
26 #include "want_params_wrapper.h"
27 #include "want.h"
28
29 namespace OHOS {
30 namespace Security {
31 namespace SecurityComponent {
32 namespace {
33 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, SECURITY_DOMAIN_SECURITY_COMPONENT, "FirstUseDialog"};
34 static const std::string SEC_COMP_SRV_CFG_PATH = "/data/service/el1/public/security_component_service";
35 static const std::string FIRST_USE_RECORD_JSON = SEC_COMP_SRV_CFG_PATH + "/first_use_record.json";
36 static const std::string FIRST_USE_RECORD_TAG = "FirstUseRecord";
37 static const std::string TOKEN_ID_TAG = "TokenId";
38 static const std::string COMP_TYPE_TAG = "CompType";
39
40 const std::string GRANT_ABILITY_BUNDLE_NAME = "com.ohos.permissionmanager";
41 const std::string GRANT_ABILITY_ABILITY_NAME = "com.ohos.permissionmanager.SecurityExtAbility";
42 const std::string TYPE_KEY = "ohos.user.security.type";
43 const std::string TOKEN_KEY = "ohos.ability.params.token";
44
45 constexpr uint32_t MAX_CFG_FILE_SIZE = 100 * 1024; // 100k
46 constexpr uint64_t LOCATION_BUTTON_FIRST_USE = 1 << 0;
47 constexpr uint64_t SAVE_BUTTON_FIRST_USE = 1 << 1;
48 }
49
IsCfgDirExist(void)50 bool FirstUseDialog::IsCfgDirExist(void)
51 {
52 struct stat fstat = {};
53 if (stat(SEC_COMP_SRV_CFG_PATH.c_str(), &fstat) != 0) {
54 SC_LOG_INFO(LABEL, "path %{public}s errno %{public}d.", SEC_COMP_SRV_CFG_PATH.c_str(), errno);
55 return false;
56 }
57
58 if (!S_ISDIR(fstat.st_mode)) {
59 SC_LOG_ERROR(LABEL, "path %{public}s is not directory.", SEC_COMP_SRV_CFG_PATH.c_str());
60 return false;
61 }
62 return true;
63 }
64
IsCfgFileExist(void)65 bool FirstUseDialog::IsCfgFileExist(void)
66 {
67 struct stat fstat = {};
68 if (stat(FIRST_USE_RECORD_JSON.c_str(), &fstat) != 0) {
69 SC_LOG_INFO(LABEL, "path %{public}s errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
70 return false;
71 }
72 return true;
73 }
74
IsCfgFileValid(void)75 bool FirstUseDialog::IsCfgFileValid(void)
76 {
77 struct stat fstat = {};
78 if (stat(FIRST_USE_RECORD_JSON.c_str(), &fstat) != 0) {
79 SC_LOG_INFO(LABEL, "path %{public}s errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
80 return false;
81 }
82 if (fstat.st_size > MAX_CFG_FILE_SIZE) {
83 SC_LOG_INFO(LABEL, "path %{public}s size too large.", FIRST_USE_RECORD_JSON.c_str());
84 return false;
85 }
86 return true;
87 }
88
ReadCfgContent(std::string & content)89 bool FirstUseDialog::ReadCfgContent(std::string& content)
90 {
91 std::stringstream buffer;
92 std::ifstream i(FIRST_USE_RECORD_JSON);
93 if (!i.is_open()) {
94 SC_LOG_ERROR(LABEL, "cannot open file %{public}s, errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
95 return false;
96 }
97 buffer << i.rdbuf();
98 content = buffer.str();
99 i.close();
100 return true;
101 }
102
WriteCfgContent(const std::string & content)103 void FirstUseDialog::WriteCfgContent(const std::string& content)
104 {
105 std::ofstream out(FIRST_USE_RECORD_JSON);
106 if (!out.is_open()) {
107 SC_LOG_ERROR(LABEL, "cannot open file %{public}s, errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
108 return;
109 }
110 out << content;
111 out.close();
112 }
113
ParseRecord(nlohmann::json & jsonRes,AccessToken::AccessTokenID & id,uint64_t & type)114 bool FirstUseDialog::ParseRecord(nlohmann::json& jsonRes,
115 AccessToken::AccessTokenID& id, uint64_t& type)
116 {
117 if (jsonRes.find(TOKEN_ID_TAG) == jsonRes.end() ||
118 !jsonRes.at(TOKEN_ID_TAG).is_number()) {
119 SC_LOG_ERROR(LABEL, "parse TokenId failed.");
120 return false;
121 }
122 id = jsonRes.at(TOKEN_ID_TAG).get<uint32_t>();
123 if (id == AccessToken::INVALID_TOKENID) {
124 SC_LOG_ERROR(LABEL, "TokenId is not invalid.");
125 return false;
126 }
127
128 if (jsonRes.find(COMP_TYPE_TAG) == jsonRes.end() ||
129 !jsonRes.at(COMP_TYPE_TAG).is_number()) {
130 SC_LOG_ERROR(LABEL, "parse CompType failed.");
131 return false;
132 }
133 type = jsonRes.at(COMP_TYPE_TAG).get<uint64_t>();
134 return true;
135 }
136
ParseRecords(nlohmann::json & jsonRes)137 void FirstUseDialog::ParseRecords(nlohmann::json& jsonRes)
138 {
139 std::unique_lock<std::mutex> lock(useMapMutex_);
140 if (jsonRes.find(FIRST_USE_RECORD_TAG) == jsonRes.end() ||
141 !jsonRes.at(FIRST_USE_RECORD_TAG).is_array()) {
142 SC_LOG_ERROR(LABEL, "parse tag failed.");
143 return;
144 }
145
146 nlohmann::json recordListJson = jsonRes.at(FIRST_USE_RECORD_TAG);
147 for (auto& recordJson : recordListJson) {
148 AccessToken::AccessTokenID id;
149 uint64_t type;
150 if (!ParseRecord(recordJson, id, type)) {
151 SC_LOG_ERROR(LABEL, "parse record failed.");
152 return;
153 }
154 firstUseMap_[id] = type;
155 }
156 }
157
LoadFirstUseRecord(void)158 void FirstUseDialog::LoadFirstUseRecord(void)
159 {
160 if (!IsCfgFileValid()) {
161 SC_LOG_INFO(LABEL, "first use record is invalid.");
162 return;
163 }
164
165 std::string content;
166 if (!ReadCfgContent(content)) {
167 return;
168 }
169
170 nlohmann::json jsonRes = nlohmann::json::parse(content, nullptr, false);
171 if (jsonRes.is_discarded()) {
172 SC_LOG_ERROR(LABEL, "cfg info format is invalid");
173 return;
174 }
175
176 ParseRecords(jsonRes);
177 }
178
SaveFirstUseRecord(void)179 void FirstUseDialog::SaveFirstUseRecord(void)
180 {
181 SC_LOG_INFO(LABEL, "start save first_use_record json");
182 if (!IsCfgDirExist()) {
183 SC_LOG_ERROR(LABEL, "dir %{public}s is not exist, errno %{public}d",
184 SEC_COMP_SRV_CFG_PATH.c_str(), errno);
185 return;
186 }
187
188 if (!IsCfgFileExist()) {
189 if (creat(FIRST_USE_RECORD_JSON.c_str(), S_IRUSR | S_IWUSR) == -1) {
190 SC_LOG_ERROR(LABEL, "create %{public}s failed, errno %{public}d",
191 FIRST_USE_RECORD_JSON.c_str(), errno);
192 return;
193 }
194 }
195
196 nlohmann::json jsonRes;
197 {
198 std::unique_lock<std::mutex> lock(useMapMutex_);
199 nlohmann::json recordsJson;
200 for (auto iter = firstUseMap_.begin(); iter != firstUseMap_.end(); ++iter) {
201 AccessToken::AccessTokenID id = iter->first;
202 AccessToken::HapTokenInfo info;
203 if (AccessToken::AccessTokenKit::GetHapTokenInfo(id, info) != AccessToken::RET_SUCCESS) {
204 SC_LOG_INFO(LABEL, "token id %{public}d is not exist, do not update it.", id);
205 continue;
206 }
207 nlohmann::json recordJson;
208 recordJson[TOKEN_ID_TAG] = id;
209 recordJson[COMP_TYPE_TAG] = iter->second;
210 recordsJson.emplace_back(recordJson);
211 }
212
213 jsonRes[FIRST_USE_RECORD_TAG] = recordsJson;
214 }
215 WriteCfgContent(jsonRes.dump());
216 }
217
StartDialogAbility(SecCompType type,sptr<IRemoteObject> callerToken)218 void FirstUseDialog::StartDialogAbility(SecCompType type, sptr<IRemoteObject> callerToken)
219 {
220 int32_t typeNum;
221 if (type == LOCATION_COMPONENT) {
222 typeNum = 0;
223 } else if (type == SAVE_COMPONENT) {
224 typeNum = 1;
225 } else {
226 SC_LOG_ERROR(LABEL, "unknown type.");
227 return;
228 }
229
230 AAFwk::Want want;
231 want.SetElementName(GRANT_ABILITY_BUNDLE_NAME, GRANT_ABILITY_ABILITY_NAME);
232 want.SetParam(TYPE_KEY, typeNum);
233 want.SetParam(TOKEN_KEY, callerToken);
234 int startRes = AAFwk::AbilityManagerClient::GetInstance()->StartExtensionAbility(want, callerToken);
235 SC_LOG_INFO(LABEL, "start ability res %{public}d", startRes);
236 }
237
SendSaveEventHandler(void)238 void FirstUseDialog::SendSaveEventHandler(void)
239 {
240 std::function<void()> delayed = ([this]() {
241 this->SaveFirstUseRecord();
242 });
243
244 SC_LOG_INFO(LABEL, "Delay first_use_record json");
245 secHandler_->ProxyPostTask(delayed);
246 }
247
NotifyFirstUseDialog(AccessToken::AccessTokenID tokenId,SecCompType type,sptr<IRemoteObject> callerToken)248 bool FirstUseDialog::NotifyFirstUseDialog(AccessToken::AccessTokenID tokenId, SecCompType type,
249 sptr<IRemoteObject> callerToken)
250 {
251 if (secHandler_ == nullptr) {
252 SC_LOG_ERROR(LABEL, "event handler invalid.");
253 return false;
254 }
255 if (callerToken == nullptr) {
256 SC_LOG_INFO(LABEL, "callerToken is null, no need to notify dialog");
257 return false;
258 }
259
260 uint64_t typeMask;
261 if (type == LOCATION_COMPONENT) {
262 typeMask = LOCATION_BUTTON_FIRST_USE;
263 } else if (type == SAVE_COMPONENT) {
264 typeMask = SAVE_BUTTON_FIRST_USE;
265 } else {
266 SC_LOG_INFO(LABEL, "this type need not notify dialog to user");
267 return false;
268 }
269
270 std::unique_lock<std::mutex> lock(useMapMutex_);
271 auto iter = firstUseMap_.find(tokenId);
272 if (iter == firstUseMap_.end()) {
273 SC_LOG_INFO(LABEL, "has not use record, start dialog");
274 StartDialogAbility(type, callerToken);
275 firstUseMap_[tokenId] = typeMask;
276 SendSaveEventHandler();
277 return true;
278 }
279
280 uint64_t compTypes = firstUseMap_[tokenId];
281 if ((compTypes & typeMask) == typeMask) {
282 SC_LOG_INFO(LABEL, "no need notify again.");
283 return true;
284 }
285 StartDialogAbility(type, callerToken);
286 firstUseMap_[tokenId] |= typeMask;
287 SendSaveEventHandler();
288 return true;
289 }
290
Init(std::shared_ptr<SecEventHandler> secHandler)291 void FirstUseDialog::Init(std::shared_ptr<SecEventHandler> secHandler)
292 {
293 SC_LOG_DEBUG(LABEL, "Init!!");
294 secHandler_ = secHandler;
295 LoadFirstUseRecord();
296 }
297 } // namespace SecurityComponent
298 } // namespace Security
299 } // namespace OHOS
300