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 "opp_transfer.h"
17
18 #include <fstream>
19 #include "interface_adapter_manager.h"
20 #include "interface_adapter_classic.h"
21 #include "log.h"
22 #include "opp_message.h"
23 #include "opp_service.h"
24
25 namespace OHOS {
26 namespace bluetooth {
27 int OppTransfer::currentTransferId_ = 0;
28
OppTransfer(const std::string & address,const std::vector<std::string> filePaths,const std::vector<std::string> mimeTypes,int direction)29 OppTransfer::OppTransfer(const std::string &address, const std::vector<std::string> filePaths,
30 const std::vector<std::string> mimeTypes, int direction)
31 {
32 HILOGI("[OPP TRANSFER] start");
33 incomingConnectTimer_ = std::make_unique<utility::Timer>(
34 std::bind(&bluetooth::OppTransfer::IncomingConnectTimeout, this));
35 incomingFileTimer_ = std::make_unique<utility::Timer>(
36 std::bind(&bluetooth::OppTransfer::IncomingFileTimeout, this));
37 time(&timeStamp_);
38 auto classicService = IAdapterManager::GetInstance()->GetClassicAdapterInterface();
39 if (classicService != nullptr) {
40 deviceName_ = classicService->GetDeviceName(RawAddress(address));
41 }
42 auto filePathItr = filePaths.begin();
43 auto mimeTypeItr = mimeTypes.begin();
44 for (; (filePathItr != filePaths.end() && (mimeTypeItr != mimeTypes.end()));
45 ++filePathItr, ++mimeTypeItr) {
46 IOppTransferInformation transferInfo;
47 transferInfo.SetId(currentTransferId_++);
48 if (currentTransferId_ >= INT_MAX) {
49 currentTransferId_ = 0;
50 }
51 transferInfo.SetFileName(GetFileNameFromPath(*filePathItr));
52 transferInfo.SetFilePath(*filePathItr);
53 transferInfo.SetFileType(*mimeTypeItr);
54 transferInfo.SetDeviceName(deviceName_);
55 transferInfo.SetDeviceAddress(address);
56 transferInfo.SetDirection(direction);
57 transferInfo.SetStatus(OPP_TRANSFER_STATUS_PENDING);
58 transferInfo.SetTimeStamp(static_cast<uint64_t>(timeStamp_));
59 transferInfo.SetTotalBytes(static_cast<uint64_t>(GetFileLength(*filePathItr)));
60 if (transferInfo.GetTotalBytes() > 0) {
61 fileList_.push(transferInfo);
62 }
63 }
64 direction_ = direction;
65 address_ = address;
66 }
67
~OppTransfer()68 OppTransfer::~OppTransfer()
69 {
70 isConnected_ = false;
71 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_PROTOCOL);
72 }
73
GetFileNumber() const74 int OppTransfer::GetFileNumber() const
75 {
76 return fileList_.size();
77 }
78
GetFileNameFromPath(std::string filePath) const79 std::string OppTransfer::GetFileNameFromPath(std::string filePath) const
80 {
81 std::string fileName;
82 size_t pos = filePath.find_last_of(u'/');
83 if (pos != std::string::npos) {
84 fileName = filePath.substr(pos + 1);
85 }
86 return fileName;
87 }
88
GetFileLength(std::string filePath) const89 size_t OppTransfer::GetFileLength(std::string filePath) const
90 {
91 std::ifstream ifs;
92 ifs.open(filePath, std::ios::in);
93 if (!ifs.is_open()) {
94 HILOGE("[OPP TRANSFER] file open failed");
95 return 0;
96 }
97 ifs.seekg(0, std::ios::end);
98 size_t fileSize = static_cast<size_t>(ifs.tellg());
99 ifs.seekg(0, std::ios::beg);
100 ifs.close();
101 HILOGI("[OPP TRANSFER] File size is %{public}zu", fileSize);
102 return fileSize;
103 }
104
ConnectObex(const ObexClientConfig & config,utility::Dispatcher & dispatcher)105 int OppTransfer::ConnectObex(const ObexClientConfig &config, utility::Dispatcher &dispatcher)
106 {
107 HILOGI("[OPP TRANSFER] Enter");
108
109 if (direction_ == OPP_TRANSFER_DIRECTION_INBOND) {
110 HILOGE("[OPP TRANSFER] is inbond,no need send connect request");
111 return RET_BAD_STATUS;
112 }
113 if (fileList_.empty()) {
114 HILOGE("[OPP TRANSFER] file list is empty");
115 return RET_BAD_STATUS;
116 }
117
118 obexClient_ = std::make_unique<OppObexClient>(config, dispatcher);
119 return obexClient_->Connect(fileList_.size());
120 }
121
DisconnectObex() const122 int OppTransfer::DisconnectObex() const
123 {
124 HILOGI("[OPP TRANSFER] Enter");
125 if (direction_ == OPP_TRANSFER_DIRECTION_INBOND) {
126 HILOGE("[OPP TRANSFER] is inbond,send disconnect request in OppService");
127 return RET_BAD_STATUS;
128 }
129
130 if (obexClient_ == nullptr) {
131 return RET_BAD_STATUS;
132 }
133 return obexClient_->Disconnect(true);
134 }
135
CancelTransfer()136 int OppTransfer::CancelTransfer()
137 {
138 HILOGI("[OPP TRANSFER] Enter");
139 if (direction_ == OPP_TRANSFER_DIRECTION_INBOND) {
140 if (obexSession_ != nullptr) {
141 int ret = obexSession_->SendSimpleResponse(ObexRspCode::BAD_REQUEST);
142 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_CANCELED);
143 return ret;
144 } else {
145 HILOGE("[OPP TRANSFER]obexSession is null");
146 return RET_BAD_STATUS;
147 }
148 } else if (direction_ == OPP_TRANSFER_DIRECTION_OUTBOND) {
149 if (obexClient_ == nullptr) {
150 return RET_BAD_STATUS;
151 }
152 return obexClient_->CancelSendFile();
153 }
154 HILOGE("[OPP TRANSFER] unknow direction");
155 return RET_BAD_STATUS;
156 }
157
OnReceiveIncomingConnect(ObexServerSession & session,uint32_t connectId)158 void OppTransfer::OnReceiveIncomingConnect(ObexServerSession &session, uint32_t connectId)
159 {
160 HILOGI("[OPP TRANSFER] Enter");
161 if (direction_ != OPP_TRANSFER_DIRECTION_INBOND) {
162 HILOGE("[OPP TRANSFER] is outbond");
163 return;
164 }
165 obexSession_ = &session;
166 connectId_ = connectId;
167 incomingConnectTimer_->Start(INCOMING_CONNECT_TIMEOUT_MS);
168 }
169
OnReceiveIncomingFile(IOppTransferInformation info)170 void OppTransfer::OnReceiveIncomingFile(IOppTransferInformation info)
171 {
172 HILOGI("[OPP TRANSFER] Enter");
173 if (direction_ != OPP_TRANSFER_DIRECTION_INBOND) {
174 HILOGE("[OPP TRANSFER] is outbond");
175 return;
176 }
177 if (!fileList_.empty()) {
178 HILOGE("[OPP TRANSFER] file list not empty");
179 SetIncomingFileConfirmation(false);
180 return;
181 }
182
183 info.SetId(currentTransferId_++);
184 if (currentTransferId_ >= INT_MAX) {
185 currentTransferId_ = 0;
186 }
187 info.SetTimeStamp(static_cast<uint64_t>(timeStamp_));
188 info.SetDeviceName(deviceName_);
189 info.SetDeviceAddress(address_);
190 info.SetDirection(direction_);
191 info.SetStatus(OPP_TRANSFER_STATUS_PENDING);
192 fileList_.push(info);
193
194 if (confirm_ == OPP_TRANSFER_CONFIRM_PENDING) {
195 incomingFileTimer_->Start(INCOMING_FILE_TIMEOUT_MS);
196 OppService::GetService()->NotifyReceiveIncomingFile(fileList_.front());
197 } else if (confirm_ == OPP_TRANSFER_CONFIRM_ACCEPT) {
198 SetIncomingFileConfirmation(true);
199 } else if (confirm_ == OPP_TRANSFER_CONFIRM_REJECT) {
200 SetIncomingFileConfirmation(false);
201 } else {
202 HILOGE("[OPP TRANSFER] unknow confirm");
203 SetIncomingFileConfirmation(false);
204 }
205 }
206
AcceptConnect()207 int OppTransfer::AcceptConnect()
208 {
209 if (direction_ != OPP_TRANSFER_DIRECTION_INBOND) {
210 HILOGE("[OPP TRANSFER] is outbond");
211 return RET_BAD_STATUS;
212 }
213 int ret = RET_BAD_STATUS;
214 incomingConnectTimer_->Stop();
215 if (obexSession_ != nullptr) {
216 OppMessage event(OPP_CONNECTED_EVT);
217 event.dev_ = address_;
218 OppService::GetService()->PostEvent(event);
219
220 auto header = ObexHeader::CreateResponse(ObexRspCode::SUCCESS, true);
221 header->AppendItemConnectionId(connectId_);
222 ret = obexSession_->SendResponse(*header);
223 } else {
224 HILOGE("[OPP TRANSFER] obexSession is null");
225 }
226 if (ret != BT_SUCCESS) {
227 OppService::GetService()->OnObexDisconnected(address_);
228 }
229 return ret;
230 }
231
StartTransfer()232 int OppTransfer::StartTransfer()
233 {
234 HILOGI("[OPP TRANSFER] Enter");
235
236 if (direction_ == OPP_TRANSFER_DIRECTION_OUTBOND) {
237 if (fileList_.empty()) {
238 HILOGE("[OPP TRANSFER] file list is null");
239 return RET_BAD_STATUS;
240 }
241 isConnected_ = true;
242 if (obexClient_ == nullptr) {
243 return RET_BAD_STATUS;
244 }
245 OnTransferStateChangeRunning();
246 return obexClient_->SendFile(fileList_.front());
247 } else if (direction_ == OPP_TRANSFER_DIRECTION_INBOND) {
248 isConnected_ = true;
249 return BT_SUCCESS;
250 } else {
251 HILOGE("[OPP TRANSFER] unknow direction");
252 }
253
254 return RET_BAD_STATUS;
255 }
256
SetIncomingFileConfirmation(bool accept)257 int OppTransfer::SetIncomingFileConfirmation(bool accept)
258 {
259 HILOGI("[OPP TRANSFER] Enter");
260 int ret = RET_BAD_STATUS;
261 if (direction_ != OPP_TRANSFER_DIRECTION_INBOND) {
262 return RET_BAD_STATUS;
263 }
264 if (accept) {
265 confirm_ = OPP_TRANSFER_CONFIRM_ACCEPT;
266 } else {
267 confirm_ = OPP_TRANSFER_CONFIRM_REJECT;
268 }
269 incomingFileTimer_->Stop();
270 if (obexSession_ != nullptr) {
271 ObexRspCode rspCode = ObexRspCode::FORBIDDEN;
272 if (accept) {
273 rspCode = ObexRspCode::CONTINUE;
274 OnTransferStateChangeRunning();
275 } else {
276 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_CANCELED);
277 }
278 auto respHeader = ObexHeader::CreateResponse(rspCode);
279 respHeader->AppendItemConnectionId(connectId_);
280 std::unique_ptr<ObexServerReceivedObject> &receivedObject = obexSession_->GetReceivedObject();
281 if ((receivedObject != nullptr) && receivedObject->IsSrmEnable()) {
282 respHeader->AppendItemSrm(true);
283 }
284 ret = obexSession_->SendResponse(*respHeader);
285 }
286 if (ret != BT_SUCCESS) {
287 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_CANCELED);
288 }
289 return ret;
290 }
291
GetCurrentTransferInformation()292 IOppTransferInformation OppTransfer::GetCurrentTransferInformation()
293 {
294 if (curretTransferInfo_ == nullptr) {
295 IOppTransferInformation ret;
296 return ret;
297 }
298 return *curretTransferInfo_;
299 }
300
IncomingConnectTimeout()301 void OppTransfer::IncomingConnectTimeout()
302 {
303 if (obexSession_ == nullptr) {
304 HILOGE("[OPP TRANSFER] obexSession_ is null");
305 return;
306 }
307 auto header = ObexHeader::CreateResponse(ObexRspCode::SERVICE_UNAVAILABLE, true);
308 obexSession_->SendResponse(*header);
309 obexSession_->Disconnect();
310 OppService::GetService()->OnObexDisconnected(address_);
311 }
312
IncomingFileTimeout()313 void OppTransfer::IncomingFileTimeout()
314 {
315 if (obexSession_ == nullptr) {
316 HILOGE("[OPP TRANSFER] obexSession_ is null");
317 return;
318 }
319 auto respHeader = ObexHeader::CreateResponse(ObexRspCode::FORBIDDEN);
320 respHeader->AppendItemConnectionId(connectId_);
321 std::unique_ptr<ObexServerReceivedObject> &receivedObject = obexSession_->GetReceivedObject();
322 if ((receivedObject != nullptr) && receivedObject->IsSrmEnable()) {
323 respHeader->AppendItemSrm(true);
324 }
325 obexSession_->SendResponse(*respHeader);
326 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_CANCELED);
327 }
328
GetDeviceAddress()329 std::string OppTransfer::GetDeviceAddress()
330 {
331 return address_;
332 }
333
GetDirection() const334 int OppTransfer::GetDirection() const
335 {
336 return direction_;
337 }
338
OnObexDisconnected()339 void OppTransfer::OnObexDisconnected()
340 {
341 isConnected_ = false;
342 obexSession_ = nullptr;
343 connectId_ = 0;
344 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_CONNECTION_FAILED);
345 }
346
OnTransferStateChange(int state,int reason)347 void OppTransfer::OnTransferStateChange(int state, int reason)
348 {
349 HILOGI("[OPP TRANSFER] Enter");
350
351 if (state == OPP_TRANSFER_STATUS_RUNNING) {
352 OnTransferStateChangeRunning();
353 } else if (state == OPP_TRANSFER_STATUS_SUCCESS) {
354 OnTransferStateChangeSuccess();
355 } else if (state == OPP_TRANSFER_STATUS_FAILD) {
356 OnTransferStateChangeFaild(reason);
357 } else {
358 HILOGE("[OPP TRANSFER] error state=%{public}d", state);
359 }
360 }
361
OnTransferStateChangeRunning()362 void OppTransfer::OnTransferStateChangeRunning()
363 {
364 if (!isConnected_) {
365 HILOGE("[OPP TRANSFER]obex is not connected");
366 return;
367 }
368 if (fileList_.empty()) {
369 HILOGE("[OPP TRANSFER] file list is empty");
370 return;
371 }
372 curretTransferInfo_ = std::make_unique<IOppTransferInformation>(fileList_.front());
373 curretTransferInfo_->SetStatus(OPP_TRANSFER_STATUS_RUNNING);
374 OppService::GetService()->NotifyTransferStateChanged(*curretTransferInfo_);
375 }
376
OnTransferStateChangeSuccess()377 void OppTransfer::OnTransferStateChangeSuccess()
378 {
379 if (!isConnected_) {
380 HILOGE("[OPP TRANSFER] obex is not connected");
381 return;
382 }
383 if (curretTransferInfo_ == nullptr) {
384 HILOGE("[OPP TRANSFER] curretTransferInfo_ is null");
385 return;
386 }
387 if (fileList_.empty()) {
388 HILOGE("[OPP TRANSFER] file list is empty");
389 return;
390 }
391 curretTransferInfo_->SetStatus(OPP_TRANSFER_STATUS_SUCCESS);
392 OppService::GetService()->NotifyTransferStateChanged(*curretTransferInfo_);
393 curretTransferInfo_ = nullptr;
394 fileList_.pop();
395 if (direction_ == OPP_TRANSFER_DIRECTION_OUTBOND) {
396 if (fileList_.empty()) {
397 OppMessage event(OPP_DISCONNECT_REQ_EVT);
398 event.dev_ = address_;
399 OppService::GetService()->PostEvent(event);
400 } else {
401 if (obexClient_ == nullptr) {
402 HILOGE("[OPP TRANSFER] obexClient_ is null");
403 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_PROTOCOL);
404 return;
405 }
406 OnTransferStateChangeRunning();
407 if (obexClient_->SendFile(fileList_.front()) != BT_SUCCESS) {
408 HILOGE("[OPP TRANSFER] send file error");
409 OnTransferStateChangeFaild(OPP_TRANSFER_FAILED_PROTOCOL);
410 return;
411 }
412 }
413 }
414 }
415
OnTransferStateChangeFaild(int reason)416 void OppTransfer::OnTransferStateChangeFaild(int reason)
417 {
418 if (curretTransferInfo_ != nullptr) {
419 curretTransferInfo_->SetStatus(OPP_TRANSFER_STATUS_FAILD);
420 curretTransferInfo_->SetFailedReason(reason);
421 OppService::GetService()->NotifyTransferStateChanged(*curretTransferInfo_);
422 curretTransferInfo_ = nullptr;
423 if (!fileList_.empty()) {
424 fileList_.pop();
425 }
426 }
427 while (!fileList_.empty()) {
428 IOppTransferInformation transferInfo = fileList_.front();
429 transferInfo.SetStatus(OPP_TRANSFER_STATUS_FAILD);
430 transferInfo.SetFailedReason(reason);
431 OppService::GetService()->NotifyTransferStateChanged(transferInfo);
432 fileList_.pop();
433 }
434 if ((direction_ == OPP_TRANSFER_DIRECTION_OUTBOND) && isConnected_) {
435 OppMessage event(OPP_DISCONNECT_REQ_EVT);
436 event.dev_ = address_;
437 OppService::GetService()->PostEvent(event);
438 }
439 }
440
OnTransferPositionChange(size_t position) const441 void OppTransfer::OnTransferPositionChange(size_t position) const
442 {
443 HILOGI("[OPP TRANSFER] Enter");
444
445 if (curretTransferInfo_ == nullptr) {
446 HILOGE("[OPP TRANSFER] curretTransferInfo_ is null");
447 return;
448 }
449 if (fileList_.empty()) {
450 HILOGE("[OPP TRANSFER] file list is empty");
451 return;
452 }
453 curretTransferInfo_->SetCurrentBytes(static_cast<uint64_t>(position));
454 OppService::GetService()->NotifyTransferStateChanged(*curretTransferInfo_);
455 }
456 } // namespace bluetooth
457 } // namespace OHOS