1/** 2 * Copyright (c) 2024 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 16import BaseModel from '../../../../../../../common/utils/src/main/ets/default/model/BaseModel'; 17import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil'; 18import ConfigData from '../../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData'; 19import Log from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogDecorator'; 20import bluetooth from '@ohos.bluetooth'; 21import bluetoothManager from '@ohos.bluetoothManager'; 22import AboutDeviceModel from '../../model/aboutDeviceImpl/AboutDeviceModel'; 23import constant from '@ohos.bluetooth.constant'; 24import { emitter } from '@kit.BasicServicesKit'; 25 26export enum ProfileCode { 27 CODE_BT_PROFILE_A2DP_SINK = 0, 28 CODE_BT_PROFILE_A2DP_SOURCE, 29 CODE_BT_PROFILE_AVRCP_CT, 30 CODE_BT_PROFILE_AVRCP_TG, 31 CODE_BT_PROFILE_HANDS_FREE_AUDIO_GATEWAY, 32 CODE_BT_PROFILE_HANDS_FREE_UNIT, 33 CODE_BT_PROFILE_HID_HOST, 34 CODE_BT_PROFILE_PAN_NETWORK, 35 CODE_BT_PROFILE_PBAP_CLIENT, 36 CODE_BT_PROFILE_PBAP_SERVER, 37}; 38 39export enum ProfileConnectionState { 40 /** the current profile is disconnected */ 41 STATE_DISCONNECTED = 0, 42 /** the current profile is being connected */ 43 STATE_CONNECTING = 1, 44 /** the current profile is connected */ 45 STATE_CONNECTED = 2, 46 /** the current profile is being disconnected */ 47 STATE_DISCONNECTING = 3 48} 49 50export enum BondState { 51 /** Indicate the bond state is invalid */ 52 BOND_STATE_INVALID = 0, 53 /** Indicate the bond state is bonding */ 54 BOND_STATE_BONDING = 1, 55 /** Indicate the bond state is bonded*/ 56 BOND_STATE_BONDED = 2 57} 58 59export enum DeviceType { 60 BLUETOOTH = '1', 61 HEADPHONE = '2', 62 PHONE = '3', 63 COMPUTER = '4', 64 WATCH = '5' 65} 66 67export enum BluetoothErrorCode { 68 SUCCESS = -1, 69 HOLD_PAIRING_MODE = 1, 70 APP_PAIR = 2, 71 PAIR_FAILED = 3, 72 DEVICE_ILLEGAL = 4, 73 CONNECT_FAILED = 5 74} 75 76enum BluetoothState { 77 /** Indicates the local Bluetooth is off */ 78 STATE_OFF = 0, 79 /** Indicates the local Bluetooth is turning on */ 80 STATE_TURNING_ON = 1, 81 /** Indicates the local Bluetooth is on, and ready for use */ 82 STATE_ON = 2, 83 /** Indicates the local Bluetooth is turning off */ 84 STATE_TURNING_OFF = 3, 85 /** Indicates the local Bluetooth is turning LE mode on */ 86 STATE_BLE_TURNING_ON = 4, 87 /** Indicates the local Bluetooth is in LE only mode */ 88 STATE_BLE_ON = 5, 89 /** Indicates the local Bluetooth is turning off LE only mode */ 90 STATE_BLE_TURNING_OFF = 6 91} 92 93/** 94 * bluetooth service class 95 */ 96export class BluetoothModel extends BaseModel { 97 private TAG = ConfigData.TAG + 'BluetoothModel '; 98 private profiles: any[] = new Array(10); 99 public canUse: boolean = false; 100 101 /** 102 * constructor 103 */ 104 constructor() { 105 super(); 106 try{ 107 LogUtil.info('bluetooth.getProfile start') 108 let profileId = bluetoothManager.ProfileId; 109 this.profiles[profileId.PROFILE_A2DP_SOURCE] = 110 bluetoothManager.getProfileInstance(profileId.PROFILE_A2DP_SOURCE); 111 this.profiles[profileId.PROFILE_HANDS_FREE_AUDIO_GATEWAY] = 112 bluetoothManager.getProfileInstance(profileId.PROFILE_HANDS_FREE_AUDIO_GATEWAY); 113 this.profiles[profileId.PROFILE_HID_HOST] = 114 bluetoothManager.getProfileInstance(profileId.PROFILE_HID_HOST); 115 LogUtil.info('bluetooth.getProfile end') 116 this.canUse = true; 117 } 118 catch(error){ 119 LogUtil.info('bluetooth.getProfile error') 120 this.canUse = false; 121 LogUtil.info(`BluetoothModel error: ${JSON.stringify(error)}.`); 122 } 123 } 124 125 126 /** 127 * Get Bluetooth status 128 * @return value of bluetooth.BluetoothState type 129 */ 130 getState(): number { 131 let bluetoothState = bluetooth.getState(); 132 LogUtil.info(`${this.TAG} getState: bluetoothState = ${bluetoothState}`); 133 return bluetoothState; 134 } 135 136 /** 137 * Get Bluetooth switch status 138 */ 139 isStateOn(): boolean { 140 let result = false; 141 let state = bluetooth.getState(); 142 LogUtil.info(`${this.TAG} isStateOn: state = ${state}`); 143 switch (state) { 144 case BluetoothState.STATE_ON: 145 result = true 146 break; 147 default: 148 break; 149 } 150 LogUtil.info(`${this.TAG} isStateOn: bluetoothState = ${result}`); 151 return result; 152 } 153 154 /** 155 * Subscribe Bluetooth switch status Change 156 */ 157 subscribeStateChange(callback: (data: boolean) => void): void { 158 LogUtil.info('bluetooth.subscribeStateChange start'); 159 bluetooth.on('stateChange', (data) => { 160 LogUtil.info(`${this.TAG} subscribeStateChange->stateChange data:${data}`); 161 if (callback) { 162 switch (data) { 163 case BluetoothState.STATE_ON: 164 let deviceName: string = AboutDeviceModel.getSystemName(); 165 let bluetoothName: string = bluetooth.getLocalName(); 166 LogUtil.info(`${this.TAG} subscribeStateChange get deviceName: ${deviceName}, bluetoothName: ${bluetoothName}`); 167 if(deviceName !== bluetoothName){ 168 LogUtil.info(`${this.TAG} subscribeStateChange deviceName != bluetoothName`) 169 bluetooth.setLocalName(deviceName); 170 } 171 bluetooth.setBluetoothScanMode(4, 0); 172 LogUtil.info(`${this.TAG} subscribeStateChange->stateChange return: true`); 173 callback(true) 174 break; 175 176 case BluetoothState.STATE_OFF: 177 LogUtil.info(`${this.TAG} subscribeStateChange->stateChange return: false`); 178 callback(false) 179 break; 180 181 default: 182 break; 183 } 184 } 185 }) 186 } 187 188 /** 189 * unsubscribe Bluetooth switch status Change 190 */ 191 unsubscribeStateChange(callback?: (data: boolean) => void): void { 192 LogUtil.info('bluetooth.unsubscribeStateChange start'); 193 bluetooth.off('stateChange', (data) => { 194 LogUtil.info(`${this.TAG} unsubscribeStateChange->stateChange data:${data}`); 195 if (callback) { 196 let result = false; 197 switch (data) { 198 case BluetoothState.STATE_ON: 199 LogUtil.info(`${this.TAG} unsubscribeStateChange->stateChange return : true`); 200 callback(true) 201 break; 202 case BluetoothState.STATE_OFF: 203 LogUtil.info(`${this.TAG} unsubscribeStateChange->stateChange return : false`); 204 callback(false) 205 break; 206 default: 207 break; 208 } 209 } 210 }) 211 } 212 213 /** 214 * Turn on Bluetooth 215 */ 216 @Log 217 enableBluetooth(): boolean { 218 return bluetooth.enableBluetooth(); 219 } 220 221 /** 222 * Turn off Bluetooth 223 */ 224 @Log 225 disableBluetooth(): boolean { 226 return bluetooth.disableBluetooth(); 227 } 228 229 /** 230 * Get local name 231 */ 232 getLocalName(): string { 233 return bluetooth.getLocalName(); 234 } 235 236 /** 237 * Set local name 238 */ 239 setLocalName(name: string): boolean { 240 return bluetooth.setLocalName(name); 241 } 242 243 /** 244 * Get paired device ids 245 */ 246 getPairedDeviceIds(): Array<string> { 247 return bluetooth.getPairedDevices(); 248 } 249 250 /** 251 * Start Bluetooth discovery 252 */ 253 @Log 254 startBluetoothDiscovery(): boolean { 255 return bluetooth.startBluetoothDiscovery(); 256 } 257 258 /** 259 * Stop Bluetooth discovery 260 */ 261 @Log 262 stopBluetoothDiscovery(): boolean { 263 return bluetooth.stopBluetoothDiscovery(); 264 } 265 266 /** 267 * Subscribe Bluetooth status Change 268 */ 269 subscribeBluetoothDeviceFind(callback: (data: Array<string>) => void): void { 270 LogUtil.info('bluetooth.subscribeBluetoothDeviceFind start'); 271 bluetooth.on('bluetoothDeviceFind', (data: Array<string>) => { 272 LogUtil.info(`${this.TAG} subscribeBluetoothDeviceFind->deviceFind callback`); 273 if (callback) { 274 callback(data) 275 } 276 }) 277 } 278 279 /** 280 * unsubscribe Bluetooth status Change 281 */ 282 unsubscribeBluetoothDeviceFind(callback?: (data: Array<string>) => void): void { 283 LogUtil.info('bluetooth.unsubscribeBluetoothDeviceFind start'); 284 bluetooth.off('bluetoothDeviceFind', (data) => { 285 LogUtil.info(`${this.TAG} unsubscribeBluetoothDeviceFind->deviceFind callback`); 286 if (callback) { 287 callback(data) 288 } 289 }) 290 } 291 292 /** 293 * Pair device 294 */ 295 pairDevice(deviceId: string): boolean { 296 return bluetooth.pairDevice(deviceId); 297 } 298 299 /** 300 * Subscribe PinRequired 301 */ 302 subscribePinRequired(callback: (data: { 303 deviceId: string; 304 pinCode: string; 305 }) => void): void { 306 LogUtil.info('bluetooth.subscribePinRequired start'); 307 bluetooth.on('pinRequired', (data: { 308 deviceId: string; 309 pinCode: string; 310 }) => { 311 LogUtil.info(`${this.TAG} subscribePinRequired->pinRequired`); 312 if (callback) { 313 callback(data) 314 } 315 }) 316 } 317 318 /** 319 * Unsubscribe PinRequired 320 */ 321 unsubscribePinRequired(callback?: (data: { 322 deviceId: string; 323 pinCode: string; 324 }) => void): void { 325 LogUtil.info('bluetooth.unsubscribePinRequired start'); 326 bluetooth.off('pinRequired', (data: { 327 deviceId: string; 328 pinCode: string; 329 }) => { 330 if(data == undefined || !data){ 331 LogUtil.error(`${this.TAG} unsubscribePinRequired->pinRequired error`); 332 return; 333 } 334 LogUtil.info(`${this.TAG} unsubscribePinRequired->pinRequired`); 335 if (callback) { 336 callback(data) 337 } 338 }) 339 } 340 341 /** 342 * Set device PairingConfirmation 343 */ 344 setDevicePairingConfirmation(deviceId: string, accept: boolean): boolean { 345 LogUtil.info('bluetooth.setDevicePairingConfirmation start, accept:' + accept); 346 let ret = bluetooth.setDevicePairingConfirmation(deviceId, accept); 347 LogUtil.info('bluetooth.unsubscribePinRequired end, ret: ' + ret); 348 return ret; 349 } 350 351 /** 352 * Subscribe bondStateChange 353 */ 354 subscribeBondStateChange(callback): void { 355 LogUtil.info('bluetooth.subscribeBondStateChange start'); 356 bluetooth.on('bondStateChange', (data) => { 357 LogUtil.info(`${this.TAG} subscribeBondStateChange->bondStateChange data.state:${JSON.stringify(data.state)}`); 358 if (callback) { 359 let result = { 360 deviceId: data.deviceId, 361 bondState: data.state 362 } 363 LogUtil.info(`${this.TAG} subscribeBondStateChange->bondStateChange return:${JSON.stringify(result.bondState)}`); 364 callback(result); 365 } 366 }) 367 } 368 369 /** 370 * Unsubscribe bondStateChange 371 */ 372 unsubscribeBondStateChange(callback?: (data: { 373 deviceId: string; 374 bondState: number; 375 }) => void): void { 376 bluetooth.off('bondStateChange', (data) => { 377 LogUtil.info(`${this.TAG} unsubscribeBondStateChange->bondStateChange start`); 378 if (callback) { 379 let result = { 380 deviceId: data.deviceId, 381 bondState: data.state 382 } 383 LogUtil.info(`${this.TAG} unsubscribeBondStateChange->bondStateChange return:${JSON.stringify(result.bondState)}`); 384 callback(result); 385 } 386 }) 387 } 388 389 /** 390 * Get device name 391 */ 392 getDeviceName(deviceId: string): string { 393 return bluetooth.getRemoteDeviceName(deviceId); 394 } 395 396 /** 397 * Get device type 398 */ 399 getDeviceType(deviceId: string): string { 400 let deviceType = DeviceType.BLUETOOTH; 401 let deviceClass = bluetooth.getRemoteDeviceClass(deviceId); 402 switch (deviceClass.majorClass) { 403 case 0x0100: 404 deviceType = DeviceType.COMPUTER; 405 break; 406 case 0x0400: 407 if (deviceClass.majorMinorClass === 0x0418 || deviceClass.majorMinorClass === 0x0404) { 408 deviceType = DeviceType.HEADPHONE; 409 } 410 break; 411 case 0x0700: 412 if (deviceClass.majorMinorClass === 0x0704) { 413 deviceType = DeviceType.WATCH; 414 } 415 break; 416 case 0x0200: 417 deviceType = DeviceType.PHONE; 418 break; 419 default: 420 deviceType = DeviceType.BLUETOOTH; 421 break; 422 } 423 LogUtil.info('bluetooth.getDeviceType end, return:' + deviceType); 424 return deviceType; 425 } 426 427 /** 428 * Get device state 429 */ 430 getDeviceState(deviceId: string): Array<{ 431 profileId: number; 432 profileConnectionState: number; 433 }> { 434 let result = []; 435 for (let i = 0; i < this.profiles.length; i++) { 436 if (this.profiles[i]) { 437 try { 438 let state = this.profiles[i].getDeviceState(deviceId); 439 result.push({ 440 profileId: i, 441 profileConnectionState: state 442 }); 443 } catch (BusinessError) { 444 LogUtil.error('Bluetooth getDeviceState failed , BusinessError is ' + JSON.stringify(BusinessError)); 445 } 446 } 447 } 448 return result; 449 } 450 451 /** 452 * Unpair device 453 */ 454 unpairDevice(deviceId: string): boolean { 455 return bluetooth.cancelPairedDevice(deviceId); 456 } 457 458 /** 459 * Connect device 460 */ 461 connectDevice(deviceId: string): Array<{ 462 profileId: number; 463 connectRet: boolean; 464 }> { 465 LogUtil.info('bluetooth.connectDevice start'); 466 let result = []; 467 for (let i = 0; i < this.profiles.length; i++) { 468 if (this.profiles[i]) { 469 let profile = this.profiles[i]; 470 let connectRet = true; 471 try { 472 profile.connect(deviceId); 473 } catch (BusinessError) { 474 LogUtil.info(`${this.TAG} connect failed. BusinessError is ` + JSON.stringify(BusinessError)); 475 connectRet = false; 476 } 477 result.push({ 478 profileId: i, 479 connectRet: connectRet 480 }); 481 } 482 } 483 LogUtil.info('bluetooth.connectDevice end, return:' + result); 484 return result; 485 } 486 487 /** 488 * Disconnect device 489 */ 490 disconnectDevice(deviceId: string): Array<{ 491 profileId: number; 492 disconnectRet: boolean; 493 }> { 494 LogUtil.info('bluetooth.disconnectDevice start'); 495 let result = []; 496 for (let i = 0; i < this.profiles.length; i++) { 497 let profile = this.profiles[i]; 498 if (this.profiles[i]) { 499 let profileConnectionState = profile.getDeviceState(deviceId); 500 let disconnectRet = true; 501 LogUtil.info(`${this.TAG} disconnectDevice , connectionState = ${profileConnectionState}`); 502 if (profileConnectionState === 2) { 503 try { 504 profile.disconnect(deviceId); 505 } catch (BusinessError) { 506 LogUtil.info(`${this.TAG} disconnect failed. BusinessError is ` + JSON.stringify(BusinessError)); 507 disconnectRet = false; 508 } 509 } 510 result.push({ 511 profileId: i, 512 disconnectRet: disconnectRet 513 }); 514 } 515 } 516 LogUtil.info('bluetooth.connectDevice end, return:' + result); 517 return result; 518 } 519 520 /** 521 * Get profile state 522 */ 523 getProfileState(deviceId: string, profileId: number): { 524 isOn: boolean, 525 isEnable: boolean, 526 description: string | ResourceStr 527 } { 528 let profileState: { 529 isOn: boolean, 530 isEnable: boolean, 531 description: string | ResourceStr 532 } = { isOn: false, isEnable: true, description: '' }; 533 534 if (this.profiles[profileId]) { 535 try { 536 let state = this.profiles[profileId].getDeviceState(deviceId) 537 if (state === constant.ProfileConnectionState.STATE_DISCONNECTED) { 538 profileState.isOn = false; 539 profileState.isEnable = true; 540 profileState.description = ''; 541 } else if (state === constant.ProfileConnectionState.STATE_CONNECTING) { 542 profileState.isOn = true; 543 profileState.isEnable = false; 544 profileState.description = $r('app.string.bt_state_connecting'); 545 } else if (state === constant.ProfileConnectionState.STATE_CONNECTED) { 546 profileState.isOn = true; 547 profileState.isEnable = true; 548 profileState.description = $r('app.string.bt_state_connected'); 549 } else if (state === constant.ProfileConnectionState.STATE_DISCONNECTING) { 550 profileState.isOn = true; 551 profileState.isEnable = false; 552 profileState.description = $r('app.string.bt_state_connected'); 553 } 554 } catch (BusinessError) { 555 LogUtil.error('Bluetooth getDeviceState failed , BusinessError is ' + JSON.stringify(BusinessError)); 556 } 557 } 558 return profileState 559 } 560 561 /** 562 * Connect profile 563 */ 564 connectProfile(deviceId: string, profileId: number): void { 565 LogUtil.info('bluetooth.connectProfile start'); 566 LogUtil.info('this.profiles[profileId] = ' + !!this.profiles[profileId]) 567 if (this.profiles[profileId]) { 568 let profile = this.profiles[profileId]; 569 let profileConnectionState = profile.getDeviceState(deviceId); 570 LogUtil.info(`${this.TAG} connectProfile , connectionState = ${profileConnectionState}`); 571 if (profileConnectionState === 0) { 572 try { 573 profile.connect(deviceId); 574 } catch (BusinessError) { 575 LogUtil.info(`${this.TAG} connect failed. BusinessError is ` + JSON.stringify(BusinessError)); 576 let innerEvent: emitter.InnerEvent = { 577 eventId: profileId 578 }; 579 let eventData: emitter.EventData = { 580 data: { 581 'State': true, 582 'Enable': false, 583 'Description': $r('app.string.bt_state_connecting') 584 } 585 } 586 emitter.emit(innerEvent, eventData) 587 setTimeout(() => { 588 eventData = { 589 data: { 590 'State': false, 591 'Enable': true, 592 'Description': '' 593 } 594 } 595 emitter.emit(innerEvent, eventData) 596 }, 500) 597 } 598 } 599 } 600 } 601 602 /** 603 * Disconnect profile 604 */ 605 disconnectProfile(deviceId: string, profileId: number): void { 606 LogUtil.info('bluetooth.disconnectProfile start'); 607 if (this.profiles[profileId]) { 608 let profile = this.profiles[profileId]; 609 let profileConnectionState = profile.getDeviceState(deviceId); 610 LogUtil.info(`${this.TAG} disconnectProfile , connectionState = ${profileConnectionState}`); 611 if (profileConnectionState === 2) { 612 try { 613 profile.disconnect(deviceId); 614 } catch (BusinessError) { 615 LogUtil.info(`${this.TAG} disconnect failed. BusinessError is ` + JSON.stringify(BusinessError)); 616 let innerEvent: emitter.InnerEvent = { 617 eventId: profileId 618 }; 619 let eventData: emitter.EventData = { 620 data: { 621 'State': false, 622 'Enable': false, 623 'Description': '' 624 } 625 } 626 emitter.emit(innerEvent, eventData) 627 setTimeout(() => { 628 eventData = { 629 data: { 630 'State': true, 631 'Enable': true, 632 'Description': $r('app.string.bt_state_connected') 633 } 634 } 635 emitter.emit(innerEvent, eventData) 636 }, 500) 637 } 638 } 639 } 640 } 641 642 /** 643 * Subscribe device connection state Change 644 */ 645 subscribeDeviceStateChange(callback: (data: { 646 profileId: number; 647 deviceId: string; 648 profileConnectionState: number; 649 }) => void): void { 650 for (let i = 0;i < this.profiles.length; i++) { 651 if (this.profiles[i]) { 652 let profile = this.profiles[i]; 653 profile.on('connectionStateChange', (data) => { 654 if (callback) { 655 let result = { 656 profileId: i, 657 deviceId: data.deviceId, 658 profileConnectionState: data.state 659 }; 660 LogUtil.info(`${this.TAG} subscribeDeviceStateChange->connectionStateChange, 661 return:${result.profileId} - ${result.profileConnectionState}`); 662 callback(result); 663 } 664 }) 665 } 666 } 667 } 668 669 /** 670 * unsubscribe device connection state Change 671 */ 672 unsubscribeDeviceStateChange(callback?: (data: { 673 profileId: number; 674 deviceId: string; 675 profileConnectionState: number; 676 }) => void): void { 677 for (let i = 0;i < this.profiles.length; i++) { 678 if (this.profiles[i]) { 679 let profile = this.profiles[i]; 680 profile.off('connectionStateChange'); 681 } 682 } 683 } 684} 685 686let bluetoothModel = new BluetoothModel(); 687 688export default bluetoothModel as BluetoothModel;