1fb726d48Sopenharmony_ci/*
2fb726d48Sopenharmony_ci * Copyright (C) 2022 Huawei Device Co., Ltd.
3fb726d48Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4fb726d48Sopenharmony_ci * you may not use this file except in compliance with the License.
5fb726d48Sopenharmony_ci * You may obtain a copy of the License at
6fb726d48Sopenharmony_ci *
7fb726d48Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8fb726d48Sopenharmony_ci *
9fb726d48Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10fb726d48Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11fb726d48Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fb726d48Sopenharmony_ci * See the License for the specific language governing permissions and
13fb726d48Sopenharmony_ci * limitations under the License.
14fb726d48Sopenharmony_ci */
15fb726d48Sopenharmony_ci
16fb726d48Sopenharmony_ciimport { Serialize } from '../common/Serialize';
17fb726d48Sopenharmony_ciimport { HdcCommand } from './HdcCommand';
18fb726d48Sopenharmony_ciimport { Utils } from '../common/Utils';
19fb726d48Sopenharmony_ciimport { HANDSHAKE_MESSAGE } from '../common/ConstantType';
20fb726d48Sopenharmony_ciimport { PayloadHead } from '../message/PayloadHead';
21fb726d48Sopenharmony_ciimport { TransmissionInterface } from '../transmission/TransmissionInterface';
22fb726d48Sopenharmony_ciimport { DataProcessing } from '../transmission/DataProcessing';
23fb726d48Sopenharmony_ciimport { DataListener } from './DataListener';
24fb726d48Sopenharmony_ciimport { DataMessage } from '../message/DataMessage';
25fb726d48Sopenharmony_ciimport { SessionHandShake } from '../message/SessionHandShake';
26fb726d48Sopenharmony_ciimport { AuthType } from '../message/AuthType';
27fb726d48Sopenharmony_ciimport { debug, log } from '../../log/Log';
28fb726d48Sopenharmony_ciimport { HdcStream } from './HdcStream';
29fb726d48Sopenharmony_ciimport { toHex16 } from '../common/BaseConversion';
30fb726d48Sopenharmony_ciimport { USBHead } from '../message/USBHead';
31fb726d48Sopenharmony_ci
32fb726d48Sopenharmony_ciexport class HdcClient implements DataListener {
33fb726d48Sopenharmony_ci  // @ts-ignore
34fb726d48Sopenharmony_ci  usbDevice: USBDevice | undefined;
35fb726d48Sopenharmony_ci  sessionId: number = 0;
36fb726d48Sopenharmony_ci  private transmissionChannel: TransmissionInterface;
37fb726d48Sopenharmony_ci  public readDataProcessing: DataProcessing;
38fb726d48Sopenharmony_ci  private cmdStreams = new Map();
39fb726d48Sopenharmony_ci  private isSuccess: boolean = false;
40fb726d48Sopenharmony_ci  private handBody: DataView | undefined;
41fb726d48Sopenharmony_ci  private message: DataMessage | undefined;
42fb726d48Sopenharmony_ci  private isSigna: boolean = false;
43fb726d48Sopenharmony_ci
44fb726d48Sopenharmony_ci  constructor(
45fb726d48Sopenharmony_ci    transmissionChannel: TransmissionInterface,
46fb726d48Sopenharmony_ci    // @ts-ignore
47fb726d48Sopenharmony_ci    usbDevice: USBDevice | undefined
48fb726d48Sopenharmony_ci  ) {
49fb726d48Sopenharmony_ci    this.transmissionChannel = transmissionChannel;
50fb726d48Sopenharmony_ci    this.usbDevice = usbDevice;
51fb726d48Sopenharmony_ci    this.readDataProcessing = new DataProcessing(this, transmissionChannel);
52fb726d48Sopenharmony_ci  }
53fb726d48Sopenharmony_ci
54fb726d48Sopenharmony_ci  async connectDevice(): Promise<boolean> {
55fb726d48Sopenharmony_ci    debug('start Connect Device');
56fb726d48Sopenharmony_ci    this.sessionId = Utils.getSessionId();
57fb726d48Sopenharmony_ci    log(`sessionId is ${this.sessionId}`);
58fb726d48Sopenharmony_ci    this.isSuccess = false;
59fb726d48Sopenharmony_ci    await this.handShakeConnect(AuthType.AUTH_NONE, 'authtype        1               1');
60fb726d48Sopenharmony_ci    let timeStamp = new Date().getTime();
61fb726d48Sopenharmony_ci    while (await this.readHandShakeMsg()) {
62fb726d48Sopenharmony_ci      if (new Date().getTime() - timeStamp > 10000) {
63fb726d48Sopenharmony_ci        break;
64fb726d48Sopenharmony_ci      }
65fb726d48Sopenharmony_ci      // 后续daemon修复发送通道关闭信息后,可放开this.message?.channelClose以作判断
66fb726d48Sopenharmony_ci      // 非握手包指令,不予理会
67fb726d48Sopenharmony_ci      if (this.message?.commandFlag !== HdcCommand.CMD_KERNEL_HANDSHAKE) {
68fb726d48Sopenharmony_ci        continue;
69fb726d48Sopenharmony_ci      }
70fb726d48Sopenharmony_ci      let backMessage = Serialize.parseHandshake(new Uint8Array(this.message.resArrayBuffer!));
71fb726d48Sopenharmony_ci      // 后续daemon修复增加sessionId数据判定后,可放开backMessage.sessionId !== this.sessionId以作判断
72fb726d48Sopenharmony_ci      const returnBuf: string = backMessage.buf;
73fb726d48Sopenharmony_ci      const returnAuth: number = backMessage.authType;
74fb726d48Sopenharmony_ci      switch (returnAuth) {
75fb726d48Sopenharmony_ci        case AuthType.AUTH_NONE:
76fb726d48Sopenharmony_ci          continue;
77fb726d48Sopenharmony_ci        case AuthType.AUTH_TOKEN:
78fb726d48Sopenharmony_ci          continue;
79fb726d48Sopenharmony_ci        case AuthType.AUTH_SIGNATURE:
80fb726d48Sopenharmony_ci          const hdcMsgUrl = this.isSigna ? 'signatureHdcMsg' : 'encryptHdcMsg';
81fb726d48Sopenharmony_ci          const response = await fetch(`${window.location.origin}/application/${hdcMsgUrl}?message=` + returnBuf);
82fb726d48Sopenharmony_ci          const dataBody = await response.json();
83fb726d48Sopenharmony_ci          let signatureHdcMsg = '';
84fb726d48Sopenharmony_ci          if (dataBody.success) {
85fb726d48Sopenharmony_ci            signatureHdcMsg = dataBody.data.signatures;
86fb726d48Sopenharmony_ci          } else {
87fb726d48Sopenharmony_ci            break;
88fb726d48Sopenharmony_ci          }
89fb726d48Sopenharmony_ci          await this.handShakeConnect(AuthType.AUTH_SIGNATURE, signatureHdcMsg);
90fb726d48Sopenharmony_ci          timeStamp = new Date().getTime();
91fb726d48Sopenharmony_ci          continue;
92fb726d48Sopenharmony_ci        case AuthType.AUTH_PUBLICKEY:
93fb726d48Sopenharmony_ci          if (returnBuf === 'authtype        1               1') {
94fb726d48Sopenharmony_ci            this.isSigna = true;
95fb726d48Sopenharmony_ci          } else {
96fb726d48Sopenharmony_ci            this.isSigna = false;
97fb726d48Sopenharmony_ci          }
98fb726d48Sopenharmony_ci          const responsePub = await fetch(`${window.location.origin}/application/hdcPublicKey`);
99fb726d48Sopenharmony_ci          const data = await responsePub.json();
100fb726d48Sopenharmony_ci          const publicKey = data.success && (`smartPerf-Host` + String.fromCharCode(12) + data.data.publicKey);
101fb726d48Sopenharmony_ci          await this.handShakeConnect(AuthType.AUTH_PUBLICKEY, publicKey);
102fb726d48Sopenharmony_ci          timeStamp = new Date().getTime();
103fb726d48Sopenharmony_ci          continue;
104fb726d48Sopenharmony_ci        case AuthType.AUTH_OK:
105fb726d48Sopenharmony_ci          if (returnBuf.toLocaleLowerCase().indexOf('unauth') === -1 || returnBuf.includes('SUCCESS')) {
106fb726d48Sopenharmony_ci            this.handShakeSuccess(this.handBody!);
107fb726d48Sopenharmony_ci            this.isSuccess = true;
108fb726d48Sopenharmony_ci            break;
109fb726d48Sopenharmony_ci          } else {
110fb726d48Sopenharmony_ci            continue;
111fb726d48Sopenharmony_ci          }
112fb726d48Sopenharmony_ci        default:
113fb726d48Sopenharmony_ci          continue;
114fb726d48Sopenharmony_ci      }
115fb726d48Sopenharmony_ci    }
116fb726d48Sopenharmony_ci    return this.isSuccess;
117fb726d48Sopenharmony_ci  }
118fb726d48Sopenharmony_ci
119fb726d48Sopenharmony_ci  private async handShakeConnect(authType: number, buf: string): Promise<void> {
120fb726d48Sopenharmony_ci    // @ts-ignore
121fb726d48Sopenharmony_ci    let handShake: SessionHandShake = new SessionHandShake(
122fb726d48Sopenharmony_ci      HANDSHAKE_MESSAGE,
123fb726d48Sopenharmony_ci      authType,
124fb726d48Sopenharmony_ci      this.sessionId,
125fb726d48Sopenharmony_ci      // @ts-ignore
126fb726d48Sopenharmony_ci      this.usbDevice.serialNumber,
127fb726d48Sopenharmony_ci      buf,
128fb726d48Sopenharmony_ci      'Ver: 3.0.0b'
129fb726d48Sopenharmony_ci    );
130fb726d48Sopenharmony_ci    let hs = Serialize.serializeSessionHandShake(handShake);
131fb726d48Sopenharmony_ci    debug('start Connect hs ', hs);
132fb726d48Sopenharmony_ci    await this.readDataProcessing.send(
133fb726d48Sopenharmony_ci      handShake.sessionId,
134fb726d48Sopenharmony_ci      0,
135fb726d48Sopenharmony_ci      HdcCommand.CMD_KERNEL_HANDSHAKE,
136fb726d48Sopenharmony_ci      hs,
137fb726d48Sopenharmony_ci      hs.length
138fb726d48Sopenharmony_ci    );
139fb726d48Sopenharmony_ci  }
140fb726d48Sopenharmony_ci
141fb726d48Sopenharmony_ci  private async readHandShakeMsg(): Promise<boolean> {
142fb726d48Sopenharmony_ci    if (this.isSuccess) {
143fb726d48Sopenharmony_ci      return false;
144fb726d48Sopenharmony_ci    }
145fb726d48Sopenharmony_ci    let handShake = await this.readDataProcessing.readUsbHead();
146fb726d48Sopenharmony_ci    this.handBody = await this.readDataProcessing.readBody(handShake!.dataSize);
147fb726d48Sopenharmony_ci    this.message = new DataMessage(handShake!, this.handBody);
148fb726d48Sopenharmony_ci    return true;
149fb726d48Sopenharmony_ci  }
150fb726d48Sopenharmony_ci
151fb726d48Sopenharmony_ci  private handShakeSuccess(handBody: DataView): void {
152fb726d48Sopenharmony_ci    let playHeadArray = handBody.buffer.slice(0, PayloadHead.getPayloadHeadLength());
153fb726d48Sopenharmony_ci    let resultPayloadHead: PayloadHead = PayloadHead.parsePlayHead(new DataView(playHeadArray));
154fb726d48Sopenharmony_ci    debug('resultPayloadHead is ', resultPayloadHead);
155fb726d48Sopenharmony_ci    let headSize = resultPayloadHead.headSize;
156fb726d48Sopenharmony_ci    let dataSize = resultPayloadHead.dataSize;
157fb726d48Sopenharmony_ci    let resPlayProtectBuffer = handBody.buffer.slice(
158fb726d48Sopenharmony_ci      PayloadHead.getPayloadHeadLength(),
159fb726d48Sopenharmony_ci      PayloadHead.getPayloadHeadLength() + headSize
160fb726d48Sopenharmony_ci    );
161fb726d48Sopenharmony_ci    debug('PlayProtect is ', resPlayProtectBuffer);
162fb726d48Sopenharmony_ci    let resData = handBody.buffer.slice(
163fb726d48Sopenharmony_ci      PayloadHead.getPayloadHeadLength() + headSize,
164fb726d48Sopenharmony_ci      PayloadHead.getPayloadHeadLength() + headSize + dataSize
165fb726d48Sopenharmony_ci    );
166fb726d48Sopenharmony_ci    debug('resData is ', resData);
167fb726d48Sopenharmony_ci    this.readDataProcessing.startReadData().then(() => {});
168fb726d48Sopenharmony_ci  }
169fb726d48Sopenharmony_ci  public async disconnect(): Promise<void> {
170fb726d48Sopenharmony_ci    try {
171fb726d48Sopenharmony_ci      await this.transmissionChannel.close();
172fb726d48Sopenharmony_ci      this.readDataProcessing.stopReadData();
173fb726d48Sopenharmony_ci      this.cmdStreams.forEach((value) => {
174fb726d48Sopenharmony_ci        value.putMessageInQueue(new DataMessage(new USBHead([0, 1], -1, -1, -1)));
175fb726d48Sopenharmony_ci      });
176fb726d48Sopenharmony_ci      this.cmdStreams.clear();
177fb726d48Sopenharmony_ci    } catch (e) {}
178fb726d48Sopenharmony_ci  }
179fb726d48Sopenharmony_ci
180fb726d48Sopenharmony_ci  public bindStream(channel: number, hdcStream: HdcStream): void {
181fb726d48Sopenharmony_ci    this.cmdStreams.set(channel, hdcStream);
182fb726d48Sopenharmony_ci  }
183fb726d48Sopenharmony_ci
184fb726d48Sopenharmony_ci  public unbindStream(channel: number): boolean {
185fb726d48Sopenharmony_ci    this.cmdStreams.delete(channel);
186fb726d48Sopenharmony_ci    return true;
187fb726d48Sopenharmony_ci  }
188fb726d48Sopenharmony_ci
189fb726d48Sopenharmony_ci  public unbindStopStream(channel: number): boolean {
190fb726d48Sopenharmony_ci    this.cmdStreams.delete(channel);
191fb726d48Sopenharmony_ci    return true;
192fb726d48Sopenharmony_ci  }
193fb726d48Sopenharmony_ci
194fb726d48Sopenharmony_ci  createDataMessage(data: DataMessage): void {
195fb726d48Sopenharmony_ci    if (this.cmdStreams.has(data.getChannelId())) {
196fb726d48Sopenharmony_ci      let stream = this.cmdStreams.get(data.getChannelId());
197fb726d48Sopenharmony_ci      if (stream) {
198fb726d48Sopenharmony_ci        stream.putMessageInQueue(data);
199fb726d48Sopenharmony_ci      }
200fb726d48Sopenharmony_ci    }
201fb726d48Sopenharmony_ci  }
202fb726d48Sopenharmony_ci}
203