1 /*
2 * Copyright (c) 2025 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 "svc_distributed_connection.h"
17
18 #include <chrono>
19 #include <iomanip>
20 #include <thread>
21 #include <map>
22
23 #include "ability_manager_client.h"
24 #include "common_event_manager.h"
25 #include "common_event_subscribe_info.h"
26 #include "cJSON.h"
27 #include "device_manager.h"
28 #include "distributed_extension_proxy.h"
29 #include "distributed_sched_utils.h"
30 #include "dtbschedmgr_log.h"
31 #include "file_ex.h"
32 #include "hisysevent.h"
33 #include "locale_config.h"
34 #include "locale_info.h"
35 #include "notification_bundle_option.h"
36 #include "notification_constant.h"
37 #include "notification_helper.h"
38 #include "notification_request.h"
39 #include "want_agent_helper.h"
40 #include "want_agent_info.h"
41 #include "string_wrapper.h"
42
43 namespace OHOS {
44 namespace DistributedSchedule {
45 const std::string TAG = "SvcDistributedConnection";
46 constexpr int WAIT_TIME = 3;
47 constexpr int32_t TIME_OUT_CLOSE = 10 * 1000 * 1000; // 10s;
48 constexpr int32_t TIME_OUT_NOTIFICATION = 10 * 1000;
49
50 constexpr const char* DMS_LANGUAGE_MAP_PATH = "system/etc/dmsfwk/resources/base/profile/dms_language_map.json";
51 constexpr const char* DMS_DEFAULT_LANGUAGE_FILE_PATH = "zh_CN";
52 constexpr const char* DMS_LANGUAGE_FILEPATH_PREFIX = "system/etc/dmsfwk/resources/";
53 constexpr const char* DMS_LANGUAGE_FILEPATH_SUFFIX = "/element/string.json";
54 constexpr const char* DMS_ZHTW_LANGUAGE_FILE_PATH = "zh_TW";
55 constexpr const char* DMS_ZHHANT_LANGUAGE_FILE_PATH = "zh-Hant"; // The language type is Traditional Chinese
56 constexpr const char* DMS_ZHTW_REGION = "TW";
57
58 constexpr const char* KEY_LANGUAGE_MAP = "dms_language_map";
59 constexpr const char* KEY_SYSTEM_LANGUAGE = "system_language";
60 constexpr const char* KEY_FILE_PATH = "file_path";
61 constexpr const char* KEY_STRING = "string";
62 constexpr const char* KEY_NAME = "name";
63 constexpr const char* KEY_VALUE = "value";
64 constexpr const char* KEY_TAG_USING = "Using";
65 constexpr const char* KEY_TAG_BGS = "BackgroundService";
66 constexpr const char* KEY_TAG_EOS = "EOS";
67
68 const int MAX_RES_VEC_LEN = 100;
69 const std::string PKG_NAME = "DBinderBus_Dms_" + std::to_string(getprocpid());
70 const uint32_t DMS_UID = 5522;
71 const int32_t NOTIFICATION_BANNER_FLAG = 1 << 9;
72 static std::string g_sysLanguage = "";
73 static std::string g_sysRegion = "";
74 static std::mutex g_resourceMutex;
75 static std::map<std::string, std::string> g_resourceMap;
76
77 const std::string CONNECT_PROXY = "VALUE_ABILITY_COLLAB_TYPE_CONNECT_PROXY";
78 const std::string COLLABRATION_TYPE = "CollabrationType";
79 const std::string SOURCE_DELEGATEE = "SourceDelegatee";
80 using namespace std;
81 using namespace AAFwk;
82
OnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)83 void SvcDistributedConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element,
84 const sptr<IRemoteObject> &remoteObject, int resultCode)
85 {
86 HILOGI("called begin");
87 if (remoteObject == nullptr) {
88 HILOGE("Failed to ability connect done, remote is nullptr");
89 return;
90 }
91 auto func = [this]() {
92 HILOGI("close begin");
93 usleep(TIME_OUT_CLOSE);
94 while (isDelay_.load()) {
95 isDelay_.store(false);
96 usleep(TIME_OUT_CLOSE);
97 }
98 this->DisconnectDistributedExtAbility();
99 HILOGI("close end");
100 };
101 std::thread task(func);
102 task.detach();
103 distributedProxy_ = iface_cast<DExtensionProxy>(remoteObject);
104 if (distributedProxy_ == nullptr) {
105 HILOGE("Failed to ability connect done, distributedProxy_ is nullptr");
106 return;
107 }
108 isConnected_.store(true);
109 string bundleName = element.GetBundleName();
110 HILOGI("bundleName:%{public}s, OnAbilityConnectDone, bundleNameIndexInfo:%{public}s", bundleName.c_str(),
111 bundleNameIndexInfo_.c_str());
112 if (bundleNameIndexInfo_.find(bundleName) == string::npos) {
113 HILOGE("Current bundle name is wrong, bundleNameIndexInfo:%{public}s, bundleName:%{public}s",
114 bundleNameIndexInfo_.c_str(), bundleName.c_str());
115 return;
116 }
117 bundleName = bundleNameIndexInfo_;
118 callConnected_(move(bundleName));
119 HILOGI("called end");
120 }
121
OnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)122 void SvcDistributedConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)
123 {
124 HILOGI("called begin");
125 isConnected_.store(false);
126 string bundleName = element.GetBundleName();
127 HILOGI("bundleName:%{public}s, OnAbilityDisconnectDone, bundleNameIndexInfo:%{public}s", bundleName.c_str(),
128 bundleNameIndexInfo_.c_str());
129 if (bundleNameIndexInfo_.find(bundleName) == string::npos) {
130 HILOGE("Current bundle name is wrong, bundleNameIndexInfo:%{public}s, bundleName:%{public}s",
131 bundleNameIndexInfo_.c_str(), bundleName.c_str());
132 return;
133 }
134 bundleName = bundleNameIndexInfo_;
135 HILOGI("called end, name: %{public}s", bundleNameIndexInfo_.c_str());
136 }
137
ConnectDExtAbility(AAFwk::Want & want,int32_t userId,bool isCleanCalled,const std::string & delegatee,bool & isDelay)138 ErrCode SvcDistributedConnection::ConnectDExtAbility(AAFwk::Want &want, int32_t userId, bool isCleanCalled,
139 const std::string& delegatee, bool &isDelay)
140 {
141 HILOGI("SvcDistributedConnection::ConnectDExtAbility Called begin");
142 if (isConnectCalled_.load()) {
143 HILOGI("Connect distributed extension called before");
144 isDelay_.store(true);
145 HILOGI("Connect ability again, isDelay:%{public}d", isDelay_.load());
146 auto proxy = GetDistributedExtProxy();
147 if (proxy == nullptr) {
148 HILOGE("Extension distribute proxy is empty");
149 return INVALID_PARAMETERS_ERR;
150 }
151 AAFwk::WantParams wantParam;
152 wantParam.SetParam(COLLABRATION_TYPE, String::Box(CONNECT_PROXY));
153 wantParam.SetParam(SOURCE_DELEGATEE, String::Box(delegatee));
154 proxy->TriggerOnCollaborate(wantParam);
155 isDelay = true;
156 return ERR_OK;
157 }
158 isCleanCalled_.store(isCleanCalled);
159 std::unique_lock<std::mutex> lock(mutex_);
160 ErrCode ret = AAFwk::AbilityManagerClient::GetInstance()->ConnectAbility(want, this, userId);
161 if (ret == ERR_OK) {
162 isConnectCalled_.store(true);
163 }
164 HILOGI("Called end, ret=%{public}d, userId=%{public}d.", ret, userId);
165 return ret;
166 }
167
DisconnectDistributedExtAbility()168 ErrCode SvcDistributedConnection::DisconnectDistributedExtAbility()
169 {
170 HILOGI("called begin");
171 std::unique_lock<std::mutex> lock(mutex_);
172 isConnectCalled_.store(false);
173 if (distributedProxy_ == nullptr) {
174 HILOGE("distributedProxy is nullptr");
175 return INVALID_PARAMETERS_ERR;
176 }
177 int32_t res = distributedProxy_->TriggerOnDestroy();
178 if (res != ERR_OK) {
179 HILOGE("destroy connect failed");
180 }
181 ErrCode ret = AppExecFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(this);
182 auto callback = [extConn {wptr(this)}] {
183 auto extPtr = extConn.promote();
184 if (!extPtr) {
185 HILOGE("Dis connect failed");
186 return false;
187 }
188 return extPtr->isConnected_.load() == false;
189 };
190 if (condition_.wait_for(lock, std::chrono::seconds(WAIT_TIME), callback)) {
191 HILOGI("Wait until the connection ends");
192 }
193 HILOGI("called end, ret=%{public}d", ret);
194 return ret;
195 }
196
IsExtAbilityConnected()197 bool SvcDistributedConnection::IsExtAbilityConnected()
198 {
199 return isConnected_.load();
200 }
201
GetDistributedExtProxy()202 sptr<IDExtension> SvcDistributedConnection::GetDistributedExtProxy()
203 {
204 return distributedProxy_;
205 }
206
SetCallback(function<void (const std::string &&)> callConnected)207 void SvcDistributedConnection::SetCallback(function<void(const std::string &&)> callConnected)
208 {
209 callConnected_ = callConnected;
210 }
211
UpdateResourceMap(const std::string & resourcePath)212 static void UpdateResourceMap(const std::string &resourcePath)
213 {
214 HILOGI("Reading resource string from json config.");
215
216 std::string content;
217 LoadStringFromFile(resourcePath, content);
218 cJSON *json = cJSON_Parse(content.c_str());
219 if (json == nullptr) {
220 HILOGE("json nullptr.");
221 return;
222 }
223
224 cJSON *resJson = cJSON_GetObjectItemCaseSensitive(json, KEY_STRING);
225 if (resJson == nullptr || cJSON_GetArraySize(resJson) > MAX_RES_VEC_LEN) {
226 HILOGE("fail to parse res json");
227 cJSON_Delete(json);
228 return;
229 }
230
231 {
232 std::lock_guard<std::mutex> lock(g_resourceMutex);
233 g_resourceMap.clear();
234 }
235 cJSON *resJsonEach = nullptr;
236 cJSON_ArrayForEach(resJsonEach, resJson) {
237 cJSON *key = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_NAME);
238 if (key == nullptr || !cJSON_IsString(key)) {
239 HILOGE("json param not string");
240 cJSON_Delete(json);
241 return;
242 }
243
244 cJSON *value = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_VALUE);
245 if (value == nullptr || !cJSON_IsString(value)) {
246 HILOGE("json param not string");
247 cJSON_Delete(json);
248 return;
249 }
250
251 std::lock_guard<std::mutex> lock(g_resourceMutex);
252 g_resourceMap.insert(std::pair<std::string, std::string>(key->valuestring, value->valuestring));
253 }
254 cJSON_Delete(json);
255 }
256
GetLanguageFilePath(const std::string & sysLanguage,const std::string & sysRegion)257 static std::string GetLanguageFilePath(const std::string &sysLanguage, const std::string &sysRegion)
258 {
259 HILOGI("Reading language file path from json config.");
260 std::string content;
261 std::string filePath = DMS_DEFAULT_LANGUAGE_FILE_PATH;
262 LoadStringFromFile(DMS_LANGUAGE_MAP_PATH, content);
263 cJSON *json = cJSON_Parse(content.c_str());
264 if (json == nullptr) {
265 HILOGE("json nullptr.");
266 return filePath;
267 }
268
269 cJSON *resJson = cJSON_GetObjectItemCaseSensitive(json, KEY_LANGUAGE_MAP);
270 if (resJson == nullptr || !cJSON_IsArray(resJson)) {
271 HILOGE("fail to parse KEY_LANGUAGE_MAP");
272 cJSON_Delete(json);
273 return filePath;
274 }
275
276 if (sysLanguage == DMS_ZHHANT_LANGUAGE_FILE_PATH && sysRegion == DMS_ZHTW_REGION) {
277 cJSON_Delete(json);
278 HILOGI("file path is zh-TW");
279 return DMS_ZHTW_LANGUAGE_FILE_PATH;
280 }
281
282 cJSON *resJsonEach = nullptr;
283 cJSON_ArrayForEach(resJsonEach, resJson) {
284 cJSON *key = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_SYSTEM_LANGUAGE);
285 if (key == nullptr || !cJSON_IsString(key)) {
286 HILOGE("json param KEY_SYSTEM_LANGUAGE not string");
287 continue;
288 }
289 if (key->valuestring != sysLanguage) {
290 continue;
291 }
292
293 cJSON *value = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_FILE_PATH);
294 if (value == nullptr || !cJSON_IsString(value)) {
295 HILOGE("json param KEY_FILE_PATH not string");
296 cJSON_Delete(json);
297 return filePath;
298 }
299
300 filePath = value->valuestring;
301 break;
302 }
303 cJSON_Delete(json);
304 HILOGI("file path %{public}s", GetAnonymStr(filePath).c_str());
305 return filePath;
306 }
307
UpdateResourceMapByLanguage()308 static void UpdateResourceMapByLanguage()
309 {
310 std::string curSysLanguage = Global::I18n::LocaleConfig::GetSystemLanguage();
311 std::string curSysRegion = Global::I18n::LocaleConfig::GetSystemRegion();
312 if (g_sysLanguage == curSysLanguage && curSysRegion == g_sysRegion) {
313 HILOGD("same language environment[%{public}s], region[%{public}s] ,no need to update resource map.",
314 curSysLanguage.c_str(), curSysRegion.c_str());
315 return;
316 }
317
318 HILOGI("current system language[%{public}s], region[%{public}s] changes, should update resource map",
319 curSysLanguage.c_str(), curSysRegion.c_str());
320 g_sysLanguage = curSysLanguage;
321 g_sysRegion = curSysRegion;
322
323 std::string filePath = DMS_LANGUAGE_FILEPATH_PREFIX +
324 GetLanguageFilePath(g_sysLanguage, g_sysRegion) +
325 DMS_LANGUAGE_FILEPATH_SUFFIX;
326 UpdateResourceMap(filePath);
327 }
328
EndTaskFunction()329 void SvcDistributedConnection::EndTaskFunction()
330 {
331 HILOGI("End task function called");
332 if (isConnected_.load()) {
333 DisconnectDistributedExtAbility();
334 }
335 }
336
GetDeliveryTime()337 static int64_t GetDeliveryTime()
338 {
339 auto now = std::chrono::system_clock::now();
340 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
341 return duration.count();
342 }
343
SetBasicOptions(Notification::NotificationRequest & request,AppExecFwk::ApplicationInfo & appInfo)344 static void SetBasicOptions(Notification::NotificationRequest &request, AppExecFwk::ApplicationInfo &appInfo)
345 {
346 request.SetCreatorUid(DMS_UID);
347 request.SetOwnerUid(appInfo.uid);
348 request.SetDeliveryTime(GetDeliveryTime());
349 request.SetAutoDeletedTime(GetDeliveryTime() + TIME_OUT_NOTIFICATION);
350 request.SetTapDismissed(true);
351 request.SetSlotType(OHOS::Notification::NotificationConstant::SlotType::SOCIAL_COMMUNICATION);
352 request.SetNotificationControlFlags(NOTIFICATION_BANNER_FLAG);
353 }
354
RegisterEventListener()355 void SvcDistributedConnection::RegisterEventListener()
356 {
357 HILOGI("Registering event listener for DMS_ACTION_END_TASK");
358 EventFwk::MatchingSkills matchingSkills;
359 matchingSkills.AddEvent("DMS_ACTION_END_TASK");
360 EventFwk::CommonEventSubscribeInfo subscribeInfo(matchingSkills);
361 auto subscriber = std::make_shared<EndTaskEventSubscriber>(subscribeInfo, this);
362 if (!EventFwk::CommonEventManager::SubscribeCommonEvent(subscriber)) {
363 HILOGE("Failed to subscribe to common event DMS_ACTION_END_TASK");
364 }
365 }
366
SetEndTaskButton(Notification::NotificationRequest & request)367 static void SetEndTaskButton(Notification::NotificationRequest& request)
368 {
369 auto want = std::make_shared<AAFwk::Want>();
370 want->SetAction("DMS_ACTION_END_TASK");
371
372 std::vector<std::shared_ptr<AAFwk::Want>> wants;
373 wants.push_back(want);
374 std::vector<AbilityRuntime::WantAgent::WantAgentConstant::Flags> flags;
375 flags.push_back(AbilityRuntime::WantAgent::WantAgentConstant::Flags::CONSTANT_FLAG);
376 AbilityRuntime::WantAgent::WantAgentInfo wantAgentInfo(
377 0, AbilityRuntime::WantAgent::WantAgentConstant::OperationType::SEND_COMMON_EVENT,
378 flags, wants, nullptr
379 );
380
381 auto wantAgent = AbilityRuntime::WantAgent::WantAgentHelper::GetWantAgent(wantAgentInfo);
382 if (wantAgent == nullptr) {
383 HILOGE("Failed to create WantAgent.");
384 return;
385 }
386
387 std::lock_guard<std::mutex> lock(g_resourceMutex);
388 std::string buttonName = g_resourceMap[KEY_TAG_EOS];
389 std::shared_ptr<Notification::NotificationActionButton> actionButton =
390 Notification::NotificationActionButton::Create(nullptr, buttonName, wantAgent);
391
392 if (actionButton == nullptr) {
393 HILOGE("Failed to create action button.");
394 return;
395 }
396 request.AddActionButton(actionButton);
397 }
398
PublishDExtensionNotification(const std::string & deviceId,const std::string & bundleName,const int32_t userId,const std::string & networkId,AppExecFwk::BundleResourceInfo & bundleResourceInfo)399 void SvcDistributedConnection::PublishDExtensionNotification(const std::string &deviceId,
400 const std::string &bundleName, const int32_t userId,
401 const std::string &networkId, AppExecFwk::BundleResourceInfo &bundleResourceInfo)
402 {
403 HILOGI("SvcDistributedConnection::PublishDExtensionNotification called");
404 UpdateResourceMapByLanguage();
405 std::shared_ptr<Notification::NotificationNormalContent> normalContent =
406 std::make_shared<Notification::NotificationNormalContent>();
407 if (normalContent == nullptr) {
408 HILOGE("Set notification normal content nullptr");
409 return;
410 }
411
412 std::string deviceName;
413 int32_t ret = DistributedHardware::DeviceManager::GetInstance().GetDeviceName(PKG_NAME, networkId, deviceName);
414 if (ret != ERR_OK) {
415 HILOGE("Failed to get device name, ret = %{public}d", ret);
416 return;
417 }
418 normalContent->SetTitle(bundleResourceInfo.label);
419 {
420 std::lock_guard<std::mutex> lock(g_resourceMutex);
421 normalContent->SetText(deviceName + g_resourceMap[KEY_TAG_USING] + bundleResourceInfo.label +
422 g_resourceMap[KEY_TAG_BGS]);
423 }
424
425 std::shared_ptr<Notification::NotificationContent> content =
426 std::make_shared<Notification::NotificationContent>(normalContent);
427 if (content == nullptr) {
428 HILOGE("Set notification content nullptr");
429 return;
430 }
431
432 AppExecFwk::ApplicationInfo appInfo;
433 auto bundleMgr_ = BundleManagerInternal::GetBundleManager();
434 if (bundleMgr_ == nullptr) {
435 HILOGE("Get bundle manager failed");
436 return;
437 }
438 std::string identity = IPCSkeleton::ResetCallingIdentity();
439 if (!bundleMgr_->GetApplicationInfo(bundleName, AppExecFwk::ApplicationFlag::GET_BASIC_APPLICATION_INFO, userId,
440 appInfo)) {
441 HILOGE("Get application info failed");
442 return;
443 }
444 IPCSkeleton::SetCallingIdentity(identity);
445
446 Notification::NotificationRequest request;
447 SetBasicOptions(request, appInfo);
448 request.SetContent(content);
449 SetEndTaskButton(request);
450
451 ret = Notification::NotificationHelper::PublishNotification(request);
452 if (ret != 0) {
453 HILOGE("Publish notification failed, ret = %{public}d", ret);
454 return;
455 }
456 }
457 }
458 }