• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "distributed_bms.h"
17 
18 #include <fstream>
19 #include <vector>
20 
21 #include "account_manager_helper.h"
22 #include "app_log_wrapper.h"
23 #include "appexecfwk_errors.h"
24 #include "bundle_mgr_interface.h"
25 #include "bundle_mgr_proxy.h"
26 #include "distributed_bms_proxy.h"
27 #include "distributed_data_storage.h"
28 #include "event_report.h"
29 #include "iservice_registry.h"
30 #include "if_system_ability_manager.h"
31 #include "locale_config.h"
32 #include "locale_info.h"
33 #include "image_compress.h"
34 #include "image_packer.h"
35 #include "image_source.h"
36 #include "system_ability_definition.h"
37 #include "xcollie/xcollie.h"
38 #include "xcollie/xcollie_define.h"
39 
40 namespace OHOS {
41 namespace AppExecFwk {
42 namespace {
43     const unsigned int LOCAL_TIME_OUT_SECONDS = 5;
44     const unsigned int REMOTE_TIME_OUT_SECONDS = 10;
45     const uint8_t DECODE_VALUE_ONE = 1;
46     const uint8_t DECODE_VALUE_TWO = 2;
47     const uint8_t DECODE_VALUE_THREE = 3;
48     const unsigned char DECODE_VALUE_CHAR_THREE = 3;
49     const uint8_t DECODE_VALUE_FOUR = 4;
50     const uint8_t DECODE_VALUE_SIX = 6;
51     const unsigned char DECODE_VALUE_CHAR_FIFTEEN = 15;
52     const unsigned char DECODE_VALUE_CHAR_SIXTY_THREE = 63;
53     const std::vector<char> DECODE_TABLE = {
54         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
55         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
56         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
57         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
58         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
59     };
60     const std::string POSTFIX = "_Compress.";
61 
GetEventInfo(const std::vector<ElementName> & elements,const std::string & localeInfo,int32_t resultCode)62     DBMSEventInfo GetEventInfo(
63         const std::vector<ElementName> &elements, const std::string &localeInfo, int32_t resultCode)
64     {
65         DBMSEventInfo eventInfo;
66         if (elements.empty()) {
67             return eventInfo;
68         }
69 
70         eventInfo.deviceID = elements[0].GetDeviceID();
71         eventInfo.localeInfo = localeInfo;
72         for (auto element : elements) {
73             if (eventInfo.bundleName.empty()) {
74                 eventInfo.bundleName.append(element.GetBundleName());
75             } else {
76                 eventInfo.bundleName.append(";").append(element.GetBundleName());
77             }
78 
79             if (eventInfo.abilityName.empty()) {
80                 eventInfo.abilityName.append(element.GetAbilityName());
81             } else {
82                 eventInfo.abilityName.append(";").append(element.GetAbilityName());
83             }
84         }
85 
86         eventInfo.resultCode = resultCode;
87         return eventInfo;
88     }
89 
GetEventInfo(const ElementName & element,const std::string & localeInfo,int32_t resultCode)90     DBMSEventInfo GetEventInfo(
91         const ElementName &element, const std::string &localeInfo, int32_t resultCode)
92     {
93         DBMSEventInfo eventInfo;
94         eventInfo.bundleName = element.GetBundleName();
95         eventInfo.abilityName = element.GetAbilityName();
96         eventInfo.deviceID = element.GetDeviceID();
97         eventInfo.localeInfo = localeInfo;
98         eventInfo.resultCode = resultCode;
99         return eventInfo;
100     }
101 }
102 const bool REGISTER_RESULT =
103     SystemAbility::MakeAndRegisterAbility(DelayedSingleton<DistributedBms>::GetInstance().get());
104 
DistributedBms()105 DistributedBms::DistributedBms() : SystemAbility(DISTRIBUTED_BUNDLE_MGR_SERVICE_SYS_ABILITY_ID, true)
106 {
107     APP_LOGI("DistributedBms :%{public}s call", __func__);
108 }
109 
~DistributedBms()110 DistributedBms::~DistributedBms()
111 {
112     APP_LOGI("DistributedBms: DBundleMgrService");
113 }
114 
OnStart()115 void DistributedBms::OnStart()
116 {
117     APP_LOGI("DistributedBms: OnStart");
118     Init();
119     bool res = Publish(this);
120     if (!res) {
121         APP_LOGE("DistributedBms: OnStart failed");
122     }
123     APP_LOGI("DistributedBms: OnStart end");
124 }
125 
OnStop()126 void DistributedBms::OnStop()
127 {
128     APP_LOGI("DistributedBms: OnStop");
129     if (distributedSub_ != nullptr) {
130         EventFwk::CommonEventManager::UnSubscribeCommonEvent(distributedSub_);
131     }
132 }
133 
Init()134 void DistributedBms::Init()
135 {
136     APP_LOGI("DistributedBms: Init");
137     DistributedDataStorage::GetInstance();
138     if (distributedSub_ == nullptr) {
139         EventFwk::MatchingSkills matchingSkills;
140         matchingSkills.AddEvent(EventFwk::CommonEventSupport::COMMON_EVENT_USER_SWITCHED);
141         matchingSkills.AddEvent(EventFwk::CommonEventSupport::COMMON_EVENT_PACKAGE_ADDED);
142         matchingSkills.AddEvent(EventFwk::CommonEventSupport::COMMON_EVENT_PACKAGE_REMOVED);
143         matchingSkills.AddEvent(EventFwk::CommonEventSupport::COMMON_EVENT_PACKAGE_CHANGED);
144         EventFwk::CommonEventSubscribeInfo subscribeInfo(matchingSkills);
145         distributedSub_ = std::make_shared<DistributedMonitor>(subscribeInfo);
146         EventFwk::CommonEventManager::SubscribeCommonEvent(distributedSub_);
147     }
148     int32_t userId = AccountManagerHelper::GetCurrentActiveUserId();
149     if (userId == Constants::INVALID_USERID) {
150         APP_LOGW("get user id failed");
151         return;
152     }
153     DistributedDataStorage::GetInstance()->UpdateDistributedData(userId);
154 }
155 
GetBundleMgr()156 OHOS::sptr<OHOS::AppExecFwk::IBundleMgr> DistributedBms::GetBundleMgr()
157 {
158     if (bundleMgr_ == nullptr) {
159         std::lock_guard<std::mutex> lock(bundleMgrMutex_);
160         if (bundleMgr_ == nullptr) {
161             auto systemAbilityManager = OHOS::SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
162             if (systemAbilityManager == nullptr) {
163                 APP_LOGE("GetBundleMgr GetSystemAbilityManager is null");
164                 return nullptr;
165             }
166             auto bundleMgrSa = systemAbilityManager->GetSystemAbility(OHOS::BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
167             if (bundleMgrSa == nullptr) {
168                 APP_LOGE("GetBundleMgr GetSystemAbility is null");
169                 return nullptr;
170             }
171             bundleMgr_ = OHOS::iface_cast<IBundleMgr>(bundleMgrSa);
172         }
173     }
174     return bundleMgr_;
175 }
176 
GetDistributedBundleMgr(const std::string & deviceId)177 static OHOS::sptr<OHOS::AppExecFwk::IDistributedBms> GetDistributedBundleMgr(const std::string &deviceId)
178 {
179     auto samgr = OHOS::SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
180     OHOS::sptr<OHOS::IRemoteObject> remoteObject;
181     if (deviceId.empty()) {
182         APP_LOGW("GetDistributedBundleMgr deviceId is empty");
183         return nullptr;
184     } else {
185         APP_LOGI("GetDistributedBundleMgr get remote d-bms");
186         remoteObject = samgr->CheckSystemAbility(OHOS::DISTRIBUTED_BUNDLE_MGR_SERVICE_SYS_ABILITY_ID, deviceId);
187     }
188     return OHOS::iface_cast<IDistributedBms>(remoteObject);
189 }
190 
GetRemoteAbilityInfo(const OHOS::AppExecFwk::ElementName & elementName,RemoteAbilityInfo & remoteAbilityInfo)191 int32_t DistributedBms::GetRemoteAbilityInfo(
192     const OHOS::AppExecFwk::ElementName &elementName, RemoteAbilityInfo &remoteAbilityInfo)
193 {
194     return GetRemoteAbilityInfo(elementName, "", remoteAbilityInfo);
195 }
196 
GetRemoteAbilityInfo(const OHOS::AppExecFwk::ElementName & elementName,const std::string & localeInfo,RemoteAbilityInfo & remoteAbilityInfo)197 int32_t DistributedBms::GetRemoteAbilityInfo(const OHOS::AppExecFwk::ElementName &elementName,
198     const std::string &localeInfo, RemoteAbilityInfo &remoteAbilityInfo)
199 {
200     auto iBundleMgr = GetBundleMgr();
201     if (!iBundleMgr->VerifySystemApi()) {
202         APP_LOGE("verify system app failed");
203         return ERR_BUNDLE_MANAGER_SYSTEM_API_DENIED;
204     }
205     auto iDistBundleMgr = GetDistributedBundleMgr(elementName.GetDeviceID());
206     int32_t resultCode;
207     if (!iDistBundleMgr) {
208         APP_LOGE("GetDistributedBundle object failed");
209         resultCode = ERR_BUNDLE_MANAGER_DEVICE_ID_NOT_EXIST;
210     } else {
211         APP_LOGD("GetDistributedBundleMgr get remote d-bms");
212         int timerId = HiviewDFX::XCollie::GetInstance().SetTimer("GetRemoteAbilityInfo", REMOTE_TIME_OUT_SECONDS,
213             nullptr, nullptr, HiviewDFX::XCOLLIE_FLAG_RECOVERY);
214         resultCode = iDistBundleMgr->GetAbilityInfo(elementName, localeInfo, remoteAbilityInfo);
215         HiviewDFX::XCollie::GetInstance().CancelTimer(timerId);
216     }
217 
218     EventReport::SendSystemEvent(
219         DBMSEventType::GET_REMOTE_ABILITY_INFO, GetEventInfo(elementName, localeInfo, resultCode));
220     return resultCode;
221 }
222 
GetRemoteAbilityInfos(const std::vector<ElementName> & elementNames,std::vector<RemoteAbilityInfo> & remoteAbilityInfos)223 int32_t DistributedBms::GetRemoteAbilityInfos(
224     const std::vector<ElementName> &elementNames, std::vector<RemoteAbilityInfo> &remoteAbilityInfos)
225 {
226     return GetRemoteAbilityInfos(elementNames, "", remoteAbilityInfos);
227 }
228 
GetRemoteAbilityInfos(const std::vector<ElementName> & elementNames,const std::string & localeInfo,std::vector<RemoteAbilityInfo> & remoteAbilityInfos)229 int32_t DistributedBms::GetRemoteAbilityInfos(const std::vector<ElementName> &elementNames,
230     const std::string &localeInfo, std::vector<RemoteAbilityInfo> &remoteAbilityInfos)
231 {
232     auto iBundleMgr = GetBundleMgr();
233     if (!iBundleMgr->VerifySystemApi()) {
234         APP_LOGE("verify system app failed");
235         return ERR_BUNDLE_MANAGER_SYSTEM_API_DENIED;
236     }
237     if (elementNames.empty()) {
238         APP_LOGE("GetDistributedBundle failed due to elementNames empty");
239         return ERR_BUNDLE_MANAGER_PARAM_ERROR;
240     }
241     auto iDistBundleMgr = GetDistributedBundleMgr(elementNames[0].GetDeviceID());
242     int32_t resultCode;
243     if (!iDistBundleMgr) {
244         APP_LOGE("GetDistributedBundle object failed");
245         resultCode = ERR_BUNDLE_MANAGER_DEVICE_ID_NOT_EXIST;
246     } else {
247         APP_LOGD("GetDistributedBundleMgr get remote d-bms");
248         int timerId = HiviewDFX::XCollie::GetInstance().SetTimer("GetRemoteAbilityInfos", REMOTE_TIME_OUT_SECONDS,
249             nullptr, nullptr, HiviewDFX::XCOLLIE_FLAG_RECOVERY);
250         resultCode = iDistBundleMgr->GetAbilityInfos(elementNames, localeInfo, remoteAbilityInfos);
251         HiviewDFX::XCollie::GetInstance().CancelTimer(timerId);
252     }
253 
254     EventReport::SendSystemEvent(
255         DBMSEventType::GET_REMOTE_ABILITY_INFOS, GetEventInfo(elementNames, localeInfo, resultCode));
256     return resultCode;
257 }
258 
GetAbilityInfo(const OHOS::AppExecFwk::ElementName & elementName,RemoteAbilityInfo & remoteAbilityInfo)259 int32_t DistributedBms::GetAbilityInfo(
260     const OHOS::AppExecFwk::ElementName &elementName, RemoteAbilityInfo &remoteAbilityInfo)
261 {
262     return GetAbilityInfo(elementName, "", remoteAbilityInfo);
263 }
264 
GetAbilityInfo(const OHOS::AppExecFwk::ElementName & elementName,const std::string & localeInfo,RemoteAbilityInfo & remoteAbilityInfo)265 int32_t DistributedBms::GetAbilityInfo(const OHOS::AppExecFwk::ElementName &elementName,
266     const std::string &localeInfo, RemoteAbilityInfo &remoteAbilityInfo)
267 {
268     APP_LOGI("DistributedBms GetAbilityInfo bundleName:%{public}s , abilityName:%{public}s, localeInfo:%{public}s",
269         elementName.GetBundleName().c_str(), elementName.GetAbilityName().c_str(), localeInfo.c_str());
270     auto iBundleMgr = GetBundleMgr();
271     if (!iBundleMgr) {
272         APP_LOGE("DistributedBms GetBundleMgr failed");
273         return ERR_APPEXECFWK_FAILED_SERVICE_DIED;
274     }
275     int userId = AccountManagerHelper::GetCurrentActiveUserId();
276     if (userId == Constants::INVALID_USERID) {
277         APP_LOGE("GetCurrentUserId failed");
278         return ERR_BUNDLE_MANAGER_INVALID_USER_ID;
279     }
280     std::vector<AbilityInfo> abilityInfos;
281     OHOS::AAFwk::Want want;
282     want.SetElement(elementName);
283     ErrCode ret = iBundleMgr->QueryAbilityInfosV9(want, static_cast<int32_t>(
284         GetAbilityInfoFlag::GET_ABILITY_INFO_WITH_APPLICATION), userId, abilityInfos);
285     if (ret != ERR_OK) {
286         APP_LOGE("DistributedBms QueryAbilityInfo failed");
287         return ret;
288     }
289     if (abilityInfos.empty()) {
290         APP_LOGE("DistributedBms QueryAbilityInfo abilityInfos empty");
291         return ERR_APPEXECFWK_FAILED_GET_ABILITY_INFO;
292     }
293     std::string label = iBundleMgr->GetStringById(
294         abilityInfos[0].bundleName, abilityInfos[0].moduleName, abilityInfos[0].labelId, userId, localeInfo);
295     if (label.empty()) {
296         APP_LOGE("DistributedBms QueryAbilityInfo label empty");
297         return ERR_APPEXECFWK_FAILED_GET_ABILITY_INFO;
298     }
299     remoteAbilityInfo.label = label;
300     remoteAbilityInfo.elementName = elementName;
301     return GetAbilityIconByContent(abilityInfos[0], userId, remoteAbilityInfo);
302 }
303 
GetAbilityIconByContent(const AbilityInfo & abilityInfo,int32_t userId,RemoteAbilityInfo & remoteAbilityInfo)304 int32_t DistributedBms::GetAbilityIconByContent(
305     const AbilityInfo &abilityInfo, int32_t userId, RemoteAbilityInfo &remoteAbilityInfo)
306 {
307     auto iBundleMgr = GetBundleMgr();
308     if (!iBundleMgr) {
309         APP_LOGE("DistributedBms GetBundleMgr failed");
310         return ERR_APPEXECFWK_FAILED_SERVICE_DIED;
311     }
312     std::unique_ptr<uint8_t[]> imageContent;
313     size_t imageContentSize = 0;
314     ErrCode ret = iBundleMgr->GetMediaData(abilityInfo.bundleName, abilityInfo.moduleName, abilityInfo.name,
315         imageContent, imageContentSize, userId);
316     if (ret != ERR_OK) {
317         APP_LOGE("DistributedBms GetMediaData failed");
318         return ret;
319     }
320     APP_LOGD("imageContentSize is %{public}d", static_cast<int32_t>(imageContentSize));
321     std::unique_ptr<ImageCompress> imageCompress = std::make_unique<ImageCompress>();
322     if (imageCompress->IsImageNeedCompressBySize(imageContentSize)) {
323         std::unique_ptr<uint8_t[]> compressData;
324         int64_t compressSize = 0;
325         std::string imageType;
326         if (!imageCompress->CompressImageByContent(imageContent, imageContentSize,
327             compressData, compressSize, imageType)) {
328             return Base64WithoutCompress(imageContent, imageContentSize, remoteAbilityInfo);
329         }
330         if (!GetMediaBase64(compressData, compressSize, imageType, remoteAbilityInfo.icon)) {
331             APP_LOGE("DistributedBms GetMediaBase64 failed");
332             return ERR_APPEXECFWK_ENCODE_BASE64_FILE_FAILED;
333         }
334     } else {
335         return Base64WithoutCompress(imageContent, imageContentSize, remoteAbilityInfo);
336     }
337     return OHOS::NO_ERROR;
338 }
339 
Base64WithoutCompress(std::unique_ptr<uint8_t[]> & imageContent,size_t imageContentSize,RemoteAbilityInfo & remoteAbilityInfo)340 int32_t DistributedBms::Base64WithoutCompress(std::unique_ptr<uint8_t[]> &imageContent, size_t imageContentSize,
341     RemoteAbilityInfo &remoteAbilityInfo)
342 {
343     std::string imageType;
344     std::unique_ptr<ImageCompress> imageCompress = std::make_unique<ImageCompress>();
345     if (!imageCompress->GetImageTypeString(imageContent, imageContentSize, imageType)) {
346         return ERR_APPEXECFWK_INPUT_WRONG_TYPE_FILE;
347     }
348     if (!GetMediaBase64(imageContent, static_cast<int64_t>(imageContentSize), imageType, remoteAbilityInfo.icon)) {
349         APP_LOGE("DistributedBms GetMediaBase64 failed");
350         return ERR_APPEXECFWK_ENCODE_BASE64_FILE_FAILED;
351     }
352     return OHOS::NO_ERROR;
353 }
354 
GetAbilityInfos(const std::vector<ElementName> & elementNames,std::vector<RemoteAbilityInfo> & remoteAbilityInfos)355 int32_t DistributedBms::GetAbilityInfos(
356     const std::vector<ElementName> &elementNames, std::vector<RemoteAbilityInfo> &remoteAbilityInfos)
357 {
358     APP_LOGD("DistributedBms GetAbilityInfos");
359     return GetAbilityInfos(elementNames, "", remoteAbilityInfos);
360 }
361 
GetAbilityInfos(const std::vector<ElementName> & elementNames,const std::string & localeInfo,std::vector<RemoteAbilityInfo> & remoteAbilityInfos)362 int32_t DistributedBms::GetAbilityInfos(const std::vector<ElementName> &elementNames,
363     const std::string &localeInfo, std::vector<RemoteAbilityInfo> &remoteAbilityInfos)
364 {
365     APP_LOGD("DistributedBms GetAbilityInfos");
366     for (auto elementName : elementNames) {
367         RemoteAbilityInfo remoteAbilityInfo;
368         int32_t result = GetAbilityInfo(elementName, localeInfo, remoteAbilityInfo);
369         if (result) {
370             APP_LOGE("get AbilityInfo:%{public}s, %{public}s, %{public}s failed", elementName.GetBundleName().c_str(),
371                 elementName.GetModuleName().c_str(), elementName.GetAbilityName().c_str());
372             return result;
373         }
374         remoteAbilityInfos.push_back(remoteAbilityInfo);
375     }
376     return OHOS::NO_ERROR;
377 }
378 
GetMediaBase64(std::unique_ptr<uint8_t[]> & data,int64_t fileLength,std::string & imageType,std::string & value)379 bool DistributedBms::GetMediaBase64(std::unique_ptr<uint8_t[]> &data, int64_t fileLength,
380     std::string &imageType, std::string &value)
381 {
382     std::unique_ptr<char[]> base64Data = EncodeBase64(data, fileLength);
383     value = "data:" + imageType + ";base64," + base64Data.get();
384     return true;
385 }
386 
GetDistributedBundleInfo(const std::string & networkId,const std::string & bundleName,DistributedBundleInfo & distributedBundleInfo)387 bool DistributedBms::GetDistributedBundleInfo(const std::string &networkId, const std::string &bundleName,
388     DistributedBundleInfo &distributedBundleInfo)
389 {
390     int timerId = HiviewDFX::XCollie::GetInstance().SetTimer("GetDistributedBundleInfo", LOCAL_TIME_OUT_SECONDS,
391         nullptr, nullptr, HiviewDFX::XCOLLIE_FLAG_RECOVERY);
392     bool ret = DistributedDataStorage::GetInstance()->GetStorageDistributeInfo(
393         networkId, bundleName, distributedBundleInfo);
394     HiviewDFX::XCollie::GetInstance().CancelTimer(timerId);
395     return ret;
396 }
397 
EncodeBase64(std::unique_ptr<uint8_t[]> & data,int srcLen)398 std::unique_ptr<char[]> DistributedBms::EncodeBase64(std::unique_ptr<uint8_t[]> &data, int srcLen)
399 {
400     int len = (srcLen / DECODE_VALUE_THREE) * DECODE_VALUE_FOUR; // Split 3 bytes to 4 parts, each containing 6 bits.
401     int outLen = ((srcLen % DECODE_VALUE_THREE) != 0) ? (len + DECODE_VALUE_FOUR) : len;
402     const uint8_t *srcData = data.get();
403     std::unique_ptr<char[]>  result = std::make_unique<char[]>(outLen + DECODE_VALUE_ONE);
404     char *dstData = result.get();
405     int j = 0;
406     int i = 0;
407     for (; i < srcLen - DECODE_VALUE_THREE; i += DECODE_VALUE_THREE) {
408         unsigned char byte1 = srcData[i];
409         unsigned char byte2 = srcData[i + DECODE_VALUE_ONE];
410         unsigned char byte3 = srcData[i + DECODE_VALUE_TWO];
411         dstData[j++] = DECODE_TABLE[byte1 >> DECODE_VALUE_TWO];
412         dstData[j++] =
413             DECODE_TABLE[(static_cast<uint8_t>(byte1 & DECODE_VALUE_CHAR_THREE) << DECODE_VALUE_FOUR)
414              | (byte2 >> DECODE_VALUE_FOUR)];
415         dstData[j++] =
416             DECODE_TABLE[(static_cast<uint8_t>(byte2 & DECODE_VALUE_CHAR_FIFTEEN)
417                 << DECODE_VALUE_TWO) | (byte3 >> DECODE_VALUE_SIX)];
418         dstData[j++] = DECODE_TABLE[byte3 & DECODE_VALUE_CHAR_SIXTY_THREE];
419     }
420     if (srcLen % DECODE_VALUE_THREE == DECODE_VALUE_ONE) {
421         unsigned char byte1 = srcData[i];
422         dstData[j++] = DECODE_TABLE[byte1 >> DECODE_VALUE_TWO];
423         dstData[j++] = DECODE_TABLE[static_cast<uint8_t>(byte1 & DECODE_VALUE_CHAR_THREE) << DECODE_VALUE_FOUR];
424         dstData[j++] = '=';
425         dstData[j++] = '=';
426     } else {
427         unsigned char byte1 = srcData[i];
428         unsigned char byte2 = srcData[i + DECODE_VALUE_ONE];
429         dstData[j++] = DECODE_TABLE[byte1 >> DECODE_VALUE_TWO];
430         dstData[j++] =
431             DECODE_TABLE[(static_cast<uint8_t>(byte1 & DECODE_VALUE_CHAR_THREE) << DECODE_VALUE_FOUR)
432              | (byte2 >> DECODE_VALUE_FOUR)];
433         dstData[j++] = DECODE_TABLE[static_cast<uint8_t>(byte2 & DECODE_VALUE_CHAR_FIFTEEN)
434                                     << DECODE_VALUE_TWO];
435         dstData[j++] = '=';
436     }
437     dstData[outLen] = '\0';
438 
439     return result;
440 }
441 }
442 }