1/* 2 * Copyright (c) 2025-2025 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 systemParameter from '@ohos.systemParameterEnhance'; 17import deviceInfo from '@ohos.deviceInfo'; 18import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; 19import opp from '@ohos.bluetooth.opp'; 20import common from '@ohos.app.ability.common'; 21import fs from '@ohos.file.fs'; 22import fileUri from '@ohos.file.fileuri'; 23import { BusinessError } from '@ohos.base'; 24 25export const CLOSE = 1; 26 27export const BACK = 0; 28 29const TAG: string = 'BluetoothShare: '; 30const BUNDLE_NAME: string = 'com.huawei.hmos.settings'; 31const ABILITY_NAME: string = 'BluetoothOppServiceUIExtensionAbility'; 32const CONTROL_TYPE_ALLOW_SEND_RECEIVE: string = '1'; 33const CONTROL_TYPE_DISALLOW_SEND_ALLOW_RECEIVE: string = '2'; 34const MAX_FILE_NUM: number = 300; 35const TRANSMIT_CONTROL_PROP_KEY: string = 'persist.distributed_scene.datafiles_trans_ctrl'; 36 37@Entry 38@Component 39export struct BtServicesComponent { 40 uiExtensionProxy?: UIExtensionProxy; 41 private serverMac: string = ''; 42 private fileHolders: Array<opp.FileHolder> = []; 43 /** 44 * 设备类型:PC 45 */ 46 private static readonly DEVICE_TYPE_PC: string = 'pc'; 47 48 /** 49 * 设备类型: 2in1 50 */ 51 private static readonly DEVICE_TYPE_PC_NEW: string = '2in1'; 52 53 build() { 54 Column() { 55 UIExtensionComponent({ 56 bundleName: BUNDLE_NAME, 57 abilityName: ABILITY_NAME, 58 parameters: { 59 'ability.want.params.uiExtensionType': 'sys/commonUI', 60 } 61 }) 62 .backgroundColor('#01111111') 63 .onRemoteReady((proxy) => { 64 console.log(TAG, 'onRemoteReady.'); 65 this.uiExtensionProxy = proxy; 66 }) 67 .onError((error) => { 68 console.log(TAG, `onError code: ${error?.code} message: ${error?.message}`); 69 }) 70 .onTerminated(() => { 71 }) 72 .onReceive((data: Record<string, string | Object>) => { 73 console.log(TAG, 'onReceive: ' + data?.action); 74 if (!data) { 75 console.error(TAG, 'onReceive error'); 76 return; 77 } 78 this.dealReceivedData(data); 79 }) 80 .size({ width: '100%', height: '100%'}) 81 .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM, SafeAreaEdge.TOP]) 82 } 83 } 84 85 private needDisableShare() : boolean { 86 try { 87 console.log(TAG, 'systemParameter get start'); 88 const info: string = systemParameter.getSync(TRANSMIT_CONTROL_PROP_KEY, CONTROL_TYPE_ALLOW_SEND_RECEIVE); 89 return info === CONTROL_TYPE_DISALLOW_SEND_ALLOW_RECEIVE; 90 } catch (err) { 91 console.error(TAG, 'systemParameter get failed, msg: ${err.message}'); 92 return false; 93 } 94 } 95 96 /** 97 * 获取设备类型 98 * @return string 99 */ 100 private static getDeviceType() : string { 101 return deviceInfo.deviceType; 102 } 103 104 private static isPC() : boolean { 105 return BtServicesComponent.getDeviceType() === BtServicesComponent.DEVICE_TYPE_PC || 106 BtServicesComponent.getDeviceType() === BtServicesComponent.DEVICE_TYPE_PC_NEW; 107 } 108 109 /** 110 * 关闭、退出蓝牙分享页面 111 */ 112 private closeSheetPage(session: UIExtensionContentSession, code: number, message?: string) { 113 if (!session) { 114 console.log(TAG, `invalid session`); 115 return; 116 } 117 console.log(TAG, `closeSheetPage start`); 118 let result: common.AbilityResult = { 119 resultCode: code 120 }; 121 if (message) { 122 result.want = { 123 parameters: { 124 message: message 125 } 126 } 127 } 128 try { 129 session?.terminateSelfWithResult(result).then(() => { 130 console.log(TAG, `terminateSelfWithResult success`); 131 }).catch((error: Error) => { 132 console.log(TAG, `terminateSelfWithResult failed, err name: ${error?.name}`); 133 }); 134 } catch (err) { 135 console.log(TAG, `closeSheetPage error, msg: ${err.message}`); 136 } 137 } 138 139 aboutToAppear() : void { 140 console.log(TAG, 'aboutToAppear.'); 141 if (BtServicesComponent.isPC() && this.needDisableShare()) { 142 console.log(TAG, 'is PC IT device'); 143 const session: UIExtensionContentSession | undefined = 144 LocalStorage.getShared()?.get<UIExtensionContentSession>('session'); 145 if (session === undefined) { 146 console.log(TAG, 'cannot get session'); 147 return; 148 } 149 console.log(TAG, 'closeSheetPage'); 150 this.closeSheetPage(session, CLOSE); 151 } 152 } 153 154 aboutToDisappear() : void { 155 console.log(TAG, 'aboutToDisappear.'); 156 } 157 158 private async dealReceivedData(data: Record<string, Object>): Promise<void> { 159 const session: UIExtensionContentSession | undefined = LocalStorage.getShared()?.get<UIExtensionContentSession>('session'); 160 if (session === undefined) { 161 console.log(TAG, 'cannot get session.'); 162 return; 163 } 164 if (data['action'] === 'sendDeviceInfo') { 165 console.log(TAG, 'sendDeviceInfo message.'); 166 let serverMac: string = data['deviceInfo'] as string; 167 this.serverMac = serverMac; 168 this.sendData(); 169 } 170 if (data['action'] === 'closeSheet') { 171 console.log(TAG, 'closeSheet message.'); 172 this.closeSheetPage(session, CLOSE); 173 } 174 if (data['action'] === 'backSheet') { 175 console.log(TAG, 'backSheet message.'); 176 this.closeSheetPage(session, BACK); 177 } 178 } 179 180 private dealSysFileUris(sysShareFileUris: string[], sysShareFilelength: number) : string[] { 181 let result: string[] = []; 182 for (let i = 0; i < sysShareFilelength; i++) { 183 let sandboxUri: string = new fileUri.FileUri(sysShareFileUris[i]).path; 184 let isDirectory = fs.statSync(sandboxUri).isDirectory(); 185 if (isDirectory) { 186 console.log(TAG, 'share file directory.'); 187 let directoryFiles: string[] = fs.listFileSync(sandboxUri); 188 console.log(TAG, 'directoryFiles length is ' + directoryFiles.length); 189 let length: number = (directoryFiles.length > MAX_FILE_NUM) ? MAX_FILE_NUM : directoryFiles.length; 190 let uris: string[] = []; 191 for (let j = 0; j < length; j++) { 192 uris.push(sandboxUri + '/' + directoryFiles[j]); 193 } 194 result.push(...this.dealSysFileUris(uris, length)); 195 } else { 196 result.push(sysShareFileUris[i]); 197 } 198 } 199 return result; 200 } 201 202 private async sendData() : Promise<void> { 203 console.log(`${TAG} sendData is called.`); 204 let sysShareFileUris: string[] = AppStorage.Get('sendFileUris'); 205 let sysShareFilelength: number = AppStorage.Get('sendFileUrisLength'); 206 if ((sysShareFileUris == undefined) || (sysShareFileUris == null) || 207 (sysShareFilelength == undefined) || (sysShareFilelength == null)) { 208 console.log(TAG, 'sysShareFileUris is undefined'); 209 return; 210 } 211 try { 212 let oppProfile = opp.createOppServerProfile(); 213 let fileUris: string[] = this.dealSysFileUris(sysShareFileUris, sysShareFilelength); 214 let fileUrisLength: number = (fileUris.length > MAX_FILE_NUM) ? MAX_FILE_NUM : fileUris.length; 215 for (let i = 0; i < fileUrisLength; i++) { 216 let file = fs.openSync(fileUris[i], fs.OpenMode.READ_ONLY); 217 let filePath = decodeURIComponent(fileUris[i]); 218 console.log(`${TAG} deal file:` + file.fd); 219 let stat: fs.Stat = fs.statSync(file.fd); 220 let fileHolder: opp.FileHolder = { 221 filePath: filePath, 222 fileSize: stat.size, 223 fileFd: file.fd 224 }; 225 this.fileHolders.push(fileHolder); 226 } 227 if (this.fileHolders.length <= 0) { 228 console.log(TAG, 'fileHolders is undefined'); 229 return; 230 } 231 await oppProfile.sendFile(this.serverMac, this.fileHolders); 232 const session: UIExtensionContentSession | undefined = 233 LocalStorage.getShared()?.get<UIExtensionContentSession>('session'); 234 for (let i = 0; i < this.fileHolders.length; i++) { 235 console.log(TAG, 'close fileHolders fd.'); 236 if (this.fileHolders[i].fileFd != -1) { 237 fs.close(this.fileHolders[i].fileFd); 238 this.fileHolders[i].fileFd = -1; 239 } 240 } 241 } catch (err) { 242 console.error(`${TAG} sendData err, code is ${err.code}, message is ${err.message}`); 243 } 244 const session: UIExtensionContentSession | undefined = 245 LocalStorage.getShared()?.get<UIExtensionContentSession>('session'); 246 if (session == undefined) { 247 console.log(TAG, 'cannot get session.'); 248 return; 249 } 250 this.closeSheetPage(session, CLOSE); 251 } 252}