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 16import { Serialize } from '../common/Serialize'; 17import { HdcCommand } from './HdcCommand'; 18import { Utils } from '../common/Utils'; 19import { HANDSHAKE_MESSAGE } from '../common/ConstantType'; 20import { PayloadHead } from '../message/PayloadHead'; 21import { TransmissionInterface } from '../transmission/TransmissionInterface'; 22import { DataProcessing } from '../transmission/DataProcessing'; 23import { DataListener } from './DataListener'; 24import { DataMessage } from '../message/DataMessage'; 25import { SessionHandShake } from '../message/SessionHandShake'; 26import { AuthType } from '../message/AuthType'; 27import { debug, log } from '../../log/Log'; 28import { HdcStream } from './HdcStream'; 29import { toHex16 } from '../common/BaseConversion'; 30import { USBHead } from '../message/USBHead'; 31 32export class HdcClient implements DataListener { 33 // @ts-ignore 34 usbDevice: USBDevice | undefined; 35 sessionId: number = 0; 36 private transmissionChannel: TransmissionInterface; 37 public readDataProcessing: DataProcessing; 38 private cmdStreams = new Map(); 39 private isSuccess: boolean = false; 40 private handBody: DataView | undefined; 41 private message: DataMessage | undefined; 42 43 constructor( 44 transmissionChannel: TransmissionInterface, 45 // @ts-ignore 46 usbDevice: USBDevice | undefined 47 ) { 48 this.transmissionChannel = transmissionChannel; 49 this.usbDevice = usbDevice; 50 this.readDataProcessing = new DataProcessing(this, transmissionChannel); 51 } 52 53 async connectDevice(): Promise<boolean> { 54 debug('start Connect Device'); 55 this.sessionId = Utils.getSessionId(); 56 log(`sessionId is ${this.sessionId}`); 57 this.isSuccess = false; 58 await this.handShakeConnect(AuthType.AUTH_NONE, ''); 59 let timeStamp = new Date().getTime(); 60 while (await this.readHandShakeMsg()) { 61 if (new Date().getTime() - timeStamp > 10000) { 62 break; 63 } 64 // 后续daemon修复发送通道关闭信息后,可放开this.message?.channelClose以作判断 65 // 非握手包指令,不予理会 66 if (this.message?.commandFlag !== HdcCommand.CMD_KERNEL_HANDSHAKE) { 67 continue; 68 } 69 let backMessage = Serialize.parseHandshake(new Uint8Array(this.message.resArrayBuffer!)); 70 // 后续daemon修复增加sessionId数据判定后,可放开backMessage.sessionId !== this.sessionId以作判断 71 const returnBuf: string = backMessage.buf; 72 const returnAuth: number = backMessage.authType; 73 switch (returnAuth) { 74 case AuthType.AUTH_NONE: 75 continue; 76 case AuthType.AUTH_TOKEN: 77 continue; 78 case AuthType.AUTH_SIGNATURE: 79 const response = await fetch(`${window.location.origin}/application/encryptHdcMsg?message=` + returnBuf); 80 const dataBody = await response.json(); 81 const encryptHdcMsg = dataBody.success && dataBody.data.signatures; 82 await this.handShakeConnect(AuthType.AUTH_SIGNATURE, encryptHdcMsg); 83 timeStamp = new Date().getTime(); 84 continue; 85 case AuthType.AUTH_PUBLICKEY: 86 const responsePub = await fetch(`${window.location.origin}/application/hdcPublicKey`); 87 const data = await responsePub.json(); 88 const publicKey = data.success && (`smartPerf-Host` + String.fromCharCode(12) + data.data.publicKey); 89 await this.handShakeConnect(AuthType.AUTH_PUBLICKEY, publicKey); 90 timeStamp = new Date().getTime(); 91 continue; 92 case AuthType.AUTH_OK: 93 if (returnBuf.toLocaleLowerCase().indexOf('unauth') === -1 || returnBuf.includes('SUCCESS')) { 94 this.handShakeSuccess(this.handBody!); 95 this.isSuccess = true; 96 break; 97 } else { 98 continue; 99 } 100 default: 101 continue; 102 } 103 } 104 return this.isSuccess; 105 } 106 107 private async handShakeConnect(authType: number, buf: string): Promise<void> { 108 // @ts-ignore 109 let handShake: SessionHandShake = new SessionHandShake( 110 HANDSHAKE_MESSAGE, 111 authType, 112 this.sessionId, 113 // @ts-ignore 114 this.usbDevice.serialNumber, 115 buf, 116 'Ver: 3.0.0b' 117 ); 118 let hs = Serialize.serializeSessionHandShake(handShake); 119 debug('start Connect hs ', hs); 120 await this.readDataProcessing.send( 121 handShake.sessionId, 122 0, 123 HdcCommand.CMD_KERNEL_HANDSHAKE, 124 hs, 125 hs.length 126 ); 127 } 128 129 private async readHandShakeMsg(): Promise<boolean> { 130 if (this.isSuccess) { 131 return false; 132 } 133 let handShake = await this.readDataProcessing.readUsbHead(); 134 this.handBody = await this.readDataProcessing.readBody(handShake!.dataSize); 135 this.message = new DataMessage(handShake!, this.handBody); 136 return true; 137 } 138 139 private handShakeSuccess(handBody: DataView): void { 140 let playHeadArray = handBody.buffer.slice(0, PayloadHead.getPayloadHeadLength()); 141 let resultPayloadHead: PayloadHead = PayloadHead.parsePlayHead(new DataView(playHeadArray)); 142 debug('resultPayloadHead is ', resultPayloadHead); 143 let headSize = resultPayloadHead.headSize; 144 let dataSize = resultPayloadHead.dataSize; 145 let resPlayProtectBuffer = handBody.buffer.slice( 146 PayloadHead.getPayloadHeadLength(), 147 PayloadHead.getPayloadHeadLength() + headSize 148 ); 149 debug('PlayProtect is ', resPlayProtectBuffer); 150 let resData = handBody.buffer.slice( 151 PayloadHead.getPayloadHeadLength() + headSize, 152 PayloadHead.getPayloadHeadLength() + headSize + dataSize 153 ); 154 debug('resData is ', resData); 155 this.readDataProcessing.startReadData().then(() => {}); 156 } 157 public async disconnect(): Promise<void> { 158 try { 159 await this.transmissionChannel.close(); 160 this.readDataProcessing.stopReadData(); 161 this.cmdStreams.forEach((value) => { 162 value.putMessageInQueue(new DataMessage(new USBHead([0, 1], -1, -1, -1))); 163 }); 164 this.cmdStreams.clear(); 165 } catch (e) {} 166 } 167 168 public bindStream(channel: number, hdcStream: HdcStream): void { 169 this.cmdStreams.set(channel, hdcStream); 170 } 171 172 public unbindStream(channel: number): boolean { 173 this.cmdStreams.delete(channel); 174 return true; 175 } 176 177 public unbindStopStream(channel: number): boolean { 178 this.cmdStreams.delete(channel); 179 return true; 180 } 181 182 createDataMessage(data: DataMessage): void { 183 if (this.cmdStreams.has(data.getChannelId())) { 184 let stream = this.cmdStreams.get(data.getChannelId()); 185 if (stream) { 186 stream.putMessageInQueue(data); 187 } 188 } 189 } 190} 191