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 
16 #include "file_transfer_manager.h"
17 
18 #include <cinttypes>
19 #include <unistd.h>
20 
21 #include "dfs_error.h"
22 #include "ipc/download_asset_callback_manager.h"
23 #include "sandbox_helper.h"
24 #include "task_state_manager.h"
25 #include "utils_log.h"
26 
27 namespace OHOS::FileManagement::CloudSync {
28 using namespace std;
29 static const string CALLER_NAME = "distributeddata";
30 
FileTransferManager(std::shared_ptr<SessionManager> sessionManager)31 FileTransferManager::FileTransferManager(std::shared_ptr<SessionManager> sessionManager)
32     : sessionManager_(sessionManager)
33 {
34 }
35 
Init()36 void FileTransferManager::Init()
37 {
38     sessionManager_->RegisterDataHandler(shared_from_this());
39 }
40 
DownloadFileFromRemoteDevice(const std::string & networkId,const int32_t userId,const uint64_t taskId,const std::string & uri)41 void FileTransferManager::DownloadFileFromRemoteDevice(const std::string &networkId,
42                                                        const int32_t userId,
43                                                        const uint64_t taskId,
44                                                        const std::string &uri)
45 {
46     IncTransTaskCount();
47     MessageInputInfo info = {.srcNetworkId = "",
48                              .dstNetworkId = networkId,
49                              .uri = uri,
50                              .msgType = MSG_DOWNLOAD_FILE_REQ,
51                              .errorCode = 0,
52                              .userId = userId,
53                              .taskId = taskId};
54     MessageHandler msgHandler(info);
55     uint32_t dataLen = msgHandler.GetDataSize();
56     auto data = make_unique<uint8_t[]>(dataLen);
57     msgHandler.PackData(data.get(), dataLen);
58     LOGI("send data, dataLen:%{public}d, taskId: %{public}" PRIu64 "", dataLen, taskId);
59     auto ret = sessionManager_->SendData(networkId, data.get(), dataLen);
60     if (ret != E_OK) {
61         LOGE("download file failed, uri:%{public}s, ret:%{public}d", GetAnonyString(uri).c_str(), ret);
62         DownloadAssetCallbackManager::GetInstance().OnDownloadFinshed(taskId, uri, ret);
63         DecTransTaskCount();
64     } else {
65         AddTransTask(uri, userId, taskId);
66     }
67 }
68 
HandleDownloadFileRequest(MessageHandler & msgHandler,const std::string & senderNetworkId,int receiverSessionId)69 void FileTransferManager::HandleDownloadFileRequest(MessageHandler &msgHandler,
70                                                     const std::string &senderNetworkId,
71                                                     int receiverSessionId)
72 {
73     IncTransTaskCount();
74     auto uri = msgHandler.GetUri();
75     auto userId = msgHandler.GetUserId();
76     auto [physicalPath, relativePath] = UriToPath(uri, userId);
77     uint32_t errorCode = E_OK;
78     if (!relativePath.empty()) {
79         auto result = sessionManager_->SendFile(senderNetworkId, {physicalPath}, {relativePath});
80         if (result != E_OK) {
81             LOGE("send file failed, relativePath:%{public}s, ret:%{public}d",
82                  GetAnonyString(relativePath).c_str(), result);
83             errorCode = E_SEND_FILE;
84             DecTransTaskCount();
85         }
86     } else {
87         errorCode = E_FILE_NOT_EXIST;
88         DecTransTaskCount();
89     }
90     auto taskId = msgHandler.GetTaskId();
91     MessageInputInfo info = {.srcNetworkId = "",
92                              .dstNetworkId = senderNetworkId,
93                              .uri = uri,
94                              .msgType = MSG_DOWNLOAD_FILE_RSP,
95                              .errorCode = errorCode,
96                              .userId = userId,
97                              .taskId = taskId};
98     MessageHandler resp(info);
99     uint32_t dataLen = resp.GetDataSize();
100     auto data = make_unique<uint8_t[]>(dataLen);
101     resp.PackData(data.get(), dataLen);
102     auto ret = sessionManager_->SendData(receiverSessionId, data.get(), dataLen);
103     if (ret != E_OK) {
104         LOGE("response failed: %{public}d, sessionId:%{public}d", ret, receiverSessionId);
105     }
106     LOGD("send response, sessionId:%{public}d", receiverSessionId);
107 }
108 
HandleDownloadFileResponse(MessageHandler & msgHandler)109 void FileTransferManager::HandleDownloadFileResponse(MessageHandler &msgHandler)
110 {
111     LOGD("recviceve response msg");
112     auto errorCode = msgHandler.GetErrorCode();
113     if (errorCode == E_OK) {
114         LOGD("callback after file recv finished");
115         return;
116     }
117     auto uri = msgHandler.GetUri();
118     auto taskId = msgHandler.GetTaskId();
119     DownloadAssetCallbackManager::GetInstance().OnDownloadFinshed(taskId, uri, errorCode);
120     DecTransTaskCount();
121     RemoveTransTask(taskId);
122 }
123 
HandleRecvFileFinished()124 void FileTransferManager::HandleRecvFileFinished()
125 {
126     LOGI("file transfer finished");
127     DecTransTaskCount(); // task finished, may be can unload sa
128 }
129 
OnMessageHandle(const std::string & senderNetworkId,int receiverSessionId,const void * data,unsigned int dataLen)130 void FileTransferManager::OnMessageHandle(const std::string &senderNetworkId,
131                                           int receiverSessionId,
132                                           const void *data,
133                                           unsigned int dataLen)
134 {
135     MessageHandler msgHandler;
136     bool ret = msgHandler.UnPackData((uint8_t *)data, dataLen);
137     if (!ret) {
138         LOGE("package data invalid");
139         return;
140     }
141     auto msgType = msgHandler.GetMsgType();
142     if (msgType == MSG_DOWNLOAD_FILE_REQ) {
143         HandleDownloadFileRequest(msgHandler, senderNetworkId, receiverSessionId);
144     } else if (msgType == MSG_DOWNLOAD_FILE_RSP) {
145         HandleDownloadFileResponse(msgHandler);
146     } else if (msgType == MSG_FINISH_FILE_RECV) {
147         HandleRecvFileFinished();
148     } else {
149         LOGE("error msg type:%{public}d", msgType);
150     }
151 }
152 
OnFileRecvHandle(const std::string & senderNetworkId,const char * filePath,int result)153 void FileTransferManager::OnFileRecvHandle(const std::string &senderNetworkId, const char *filePath, int result)
154 {
155     LOGE("received file, file path:%{public}s, result:%{public}d", GetAnonyString(filePath).c_str(), result);
156     DecTransTaskCount(); // allways dec task count when finsh one task
157     if (filePath != nullptr) {
158         FinishTransTask(string(filePath), result);
159     }
160 
161     MessageInputInfo info = {.msgType = MSG_FINISH_FILE_RECV};
162     MessageHandler req(info);
163     uint32_t dataLen = req.GetDataSize();
164     auto data = make_unique<uint8_t[]>(dataLen);
165     req.PackData(data.get(), dataLen);
166     auto ret = sessionManager_->SendData(senderNetworkId, data.get(), dataLen);
167     if (ret != E_OK) {
168         LOGE("response failed: %{public}d", ret);
169     }
170     LOGI("send file recv finished msg");
171 }
172 
OnSessionClosed()173 void FileTransferManager::OnSessionClosed() // avoid sa cannot unload when session disconnect
174 {
175     taskCount_.store(0);
176     TaskStateManager::GetInstance().CompleteTask(CALLER_NAME, TaskType::DOWNLOAD_REMOTE_ASSET_TASK);
177 }
178 
IsFileExists(const std::string & filePath)179 bool FileTransferManager::IsFileExists(const std::string &filePath)
180 {
181     if (access(filePath.c_str(), F_OK) != E_OK) {
182         LOGE("file is not exist, path:%{public}s, error:%{public}s", GetAnonyString(filePath).c_str(), strerror(errno));
183         return false;
184     }
185     return true;
186 }
187 
UriToPath(const std::string & uri,const int32_t userId,bool isCheckFileExists)188 std::tuple<std::string, std::string> FileTransferManager::UriToPath(const std::string &uri,
189                                                                     const int32_t userId,
190                                                                     bool isCheckFileExists)
191 {
192     string physicalPath = "";
193     int ret = AppFileService::SandboxHelper::GetPhysicalPath(uri, std::to_string(userId), physicalPath);
194     if (ret != 0) {
195         LOGE("Get physical path failed with %{public}d", ret);
196         return {"", ""};
197     }
198 
199     if (isCheckFileExists) {
200         if (!this->IsFileExists(physicalPath)) {
201             return {"", ""};
202         }
203     }
204 
205     const string HMDFS_DIR = "/mnt/hmdfs/";
206     const string DATA_DIR = HMDFS_DIR + to_string(userId) + "/account/device_view/local/data";
207 
208     std::string relativePath;
209     size_t fileDirPos = physicalPath.find(DATA_DIR);
210     if (fileDirPos == std::string::npos) {
211         return {"", ""};
212     }
213     fileDirPos += DATA_DIR.length();
214     relativePath = physicalPath.substr(fileDirPos);
215 
216     return {physicalPath, relativePath};
217 }
218 
AddTransTask(const std::string & uri,const int32_t userId,uint64_t taskId)219 void FileTransferManager::AddTransTask(const std::string &uri, const int32_t userId, uint64_t taskId)
220 {
221     lock_guard<mutex> lock(taskMutex_);
222     auto [physicalPath, relativePath] = UriToPath(uri, userId, false);
223     TaskInfo info = {uri, relativePath, taskId};
224     taskInfos_.push_back(info);
225 }
226 
RemoveTransTask(uint64_t taskId)227 void FileTransferManager::RemoveTransTask(uint64_t taskId)
228 {
229     LOGI("remove task:%{public}" PRIu64 "", taskId);
230     lock_guard<mutex> lock(taskMutex_);
231     for (auto iter = taskInfos_.begin(); iter != taskInfos_.end();) {
232         if ((*iter).taskId == taskId) {
233             iter = taskInfos_.erase(iter);
234         } else {
235             ++iter;
236         }
237     }
238 }
239 
FinishTransTask(const std::string & relativePath,int result)240 void FileTransferManager::FinishTransTask(const std::string &relativePath, int result)
241 {
242     lock_guard<mutex> lock(taskMutex_);
243     for (auto iter = taskInfos_.begin(); iter != taskInfos_.end();) {
244         if ((*iter).relativePath == relativePath) {
245             DownloadAssetCallbackManager::GetInstance().OnDownloadFinshed((*iter).taskId, (*iter).uri, result);
246             iter = taskInfos_.erase(iter);
247             return; // match the first one
248         } else {
249             ++iter;
250         }
251     }
252     LOGE("not found task, relativePath:%{public}s", GetAnonyString(relativePath).c_str());
253 }
254 
IncTransTaskCount()255 void FileTransferManager::IncTransTaskCount()
256 {
257     TaskStateManager::GetInstance().StartTask(CALLER_NAME, TaskType::DOWNLOAD_REMOTE_ASSET_TASK);
258     taskCount_.fetch_add(1);
259 }
260 
DecTransTaskCount()261 void FileTransferManager::DecTransTaskCount()
262 {
263     auto count = taskCount_.fetch_sub(1);
264     if (count == 1) {
265         TaskStateManager::GetInstance().CompleteTask(CALLER_NAME, TaskType::DOWNLOAD_REMOTE_ASSET_TASK);
266     } else if (count < 1) {
267         taskCount_.store(0);
268     }
269 }
270 } // namespace OHOS::FileManagement::CloudSync
271