• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }