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