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