• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 { HdcClient } from './hdcclient/HdcClient';
17import { UsbTransmissionChannel } from './transmission/UsbTransmissionChannel';
18import { HDC_DEVICE_FILTERS } from './common/ConstantType';
19import { FormatCommand } from './hdcclient/FormatCommand';
20import { log } from '../log/Log';
21import { HdcStream } from './hdcclient/HdcStream';
22import { HdcCommand } from './hdcclient/HdcCommand';
23import { SpRecordTrace } from '../trace/component/SpRecordTrace';
24import { DataMessage } from './message/DataMessage';
25
26export class HdcDeviceManager {
27  static escapeCharacterDict = {
28    Escape: [27],
29    Tab: [9],
30    Backspace: [8],
31    Enter: [13],
32    Insert: [27, 91, 50, 126],
33    Home: [27, 91, 49, 126],
34    Delete: [27, 91, 51, 126],
35    End: [27, 91, 52, 126],
36    PageDown: [27, 91, 54, 126],
37    PageUp: [27, 91, 53, 126],
38    ArrowUp: [27, 91, 65],
39    ArrowDown: [27, 91, 66],
40    ArrowLeft: [27, 91, 68],
41    ArrowRight: [27, 91, 67],
42  };
43  static ctrlKey = {
44    c: [3],
45  };
46  private static clientList: Map<string, HdcClient> = new Map();
47  private static currentHdcClient: HdcClient;
48  private static FILE_RECV_PREFIX_STRING = 'hdc file recv -cwd C:\\ ';
49
50  /**
51   * getDevices
52   */
53  // @ts-ignore
54  public static async getDevices(): Promise<USBDevice[]> {
55    // @ts-ignore
56    return navigator.usb.getDevices();
57  }
58
59  /**
60   * findDevice
61   */
62  // @ts-ignore
63  public static findDevice(): Promise<USBDevice> {
64    if (!('usb' in navigator)) {
65      throw new Error('WebUSB not supported by the browser (requires HTTPS)');
66    }
67    // @ts-ignore
68    return navigator.usb.requestDevice({ filters: HDC_DEVICE_FILTERS });
69  }
70
71  /**
72   * connect by serialNumber
73   *
74   * @param serialNumber serialNumber
75   */
76  public static async connect(serialNumber: string): Promise<boolean> {
77    const client = this.clientList.get(serialNumber);
78    if (client) {
79      if (client.usbDevice!.opened) {
80        log('device Usb is Open');
81        return true;
82      } else {
83        if (SpRecordTrace.serialNumber === serialNumber) {
84          SpRecordTrace.serialNumber = '';
85        }
86        log('device Usb not Open');
87        return false;
88      }
89    } else {
90      const connectDevice = await this.getDeviceBySerialNumber(serialNumber);
91      const usbChannel = await UsbTransmissionChannel.openHdcDevice(connectDevice);
92      if (usbChannel) {
93        const hdcClient = new HdcClient(usbChannel, connectDevice);
94        const connected = await hdcClient.connectDevice();
95        if (connected) {
96          this.currentHdcClient = hdcClient;
97          this.clientList.set(serialNumber, hdcClient);
98        }
99        log(`device Usb connected : ${connected}`);
100        return connected;
101      } else {
102        log('device Usb connected failed: ');
103        return false;
104      }
105    }
106  }
107
108  // @ts-ignore
109  public static async getDeviceBySerialNumber(serialNumber: string): Promise<USBDevice> {
110    // @ts-ignore
111    const devices = await navigator.usb.getDevices();
112    // @ts-ignore
113    return devices.find((dev): boolean => dev.serialNumber === serialNumber);
114  }
115
116  /**
117   * disConnect by serialNumber
118   *
119   * @param serialNumber
120   */
121  public static async disConnect(serialNumber: string): Promise<boolean> {
122    const hdcClient = this.clientList.get(serialNumber);
123    if (hdcClient) {
124      await hdcClient.disconnect();
125      this.clientList.delete(serialNumber);
126      return true;
127    } else {
128      return true;
129    }
130  }
131
132  /**
133   * Execute shell on the currently connected device and return the result as a string
134   *
135   * @param cmd cmd
136   * @param isSkipResult isSkipResult
137   * @param shellResultHandleFun shellResultHandleFun
138   */
139  public static async shellResultAsString(cmd: string, isSkipResult: boolean, shellResultHandleFun?: Function):
140    Promise<string> {
141    if (this.currentHdcClient) {
142      const hdcStream = new HdcStream(this.currentHdcClient, false);
143      await hdcStream.DoCommand(cmd);
144      let result = '';
145      while (true) {
146        const dataMessage = await hdcStream.getMessage();
147        if (dataMessage.channelClose || isSkipResult) {
148          result += dataMessage.getDataToString();
149          await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false));
150          log('result is end, close');
151          break;
152        }
153        if (dataMessage.usbHead.sessionId === -1) {
154          await hdcStream.closeStream();
155          return Promise.resolve('The device is abnormal');
156        }
157        let dataResult = dataMessage.getDataToString();
158        result += dataResult;
159        if (shellResultHandleFun && cmd.endsWith('CONFIG')) {
160          shellResultHandleFun(dataResult);
161        }
162      }
163      await hdcStream.closeStream();
164      await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false));
165      return Promise.resolve(result);
166    }
167    return Promise.reject('not select device');
168  }
169
170  /**
171   * Execute shell on the currently connected device and return the result as a string
172   *
173   * @param cmd cmd
174   */
175  public static async stopHiprofiler(cmd: string): Promise<string> {
176    if (this.currentHdcClient) {
177      const hdcStream = new HdcStream(this.currentHdcClient, true);
178      await hdcStream.DoCommand(cmd);
179      let result = '';
180      let dataMessage = await hdcStream.getMessage();
181      result += dataMessage.getDataToString();
182      while (!dataMessage.channelClose) {
183        dataMessage = await hdcStream.getMessage();
184        result += dataMessage.getDataToString();
185      }
186      await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false));
187      await hdcStream.closeStream();
188      return Promise.resolve(result);
189    }
190    return Promise.reject('not select device');
191  }
192
193  public static startShell(
194    resultCallBack: (res: DataMessage) => void
195  ): ((keyboardEvent: KeyboardEvent | string) => void | undefined) | undefined {
196    if (this.currentHdcClient) {
197      const hdcShellStream = new HdcStream(this.currentHdcClient, false);
198      this.shellInit(hdcShellStream, resultCallBack);
199      return (keyboardEvent: KeyboardEvent | string): void => {
200        let code = undefined;
201        if (keyboardEvent instanceof KeyboardEvent) {
202          const cmd = keyboardEvent.key;
203          if (keyboardEvent.shiftKey && keyboardEvent.key.toUpperCase() === 'SHIFT') {
204            return;
205          } else if (keyboardEvent.metaKey) {
206            return;
207          } else if (keyboardEvent.ctrlKey) {
208            // @ts-ignore
209            code = this.ctrlKey[keyboardEvent.key];
210            if (!code) {
211              return;
212            } else {
213              const dataArray = new Uint8Array(code);
214              hdcShellStream.sendToDaemon(
215                new FormatCommand(HdcCommand.CMD_SHELL_DATA, cmd, false),
216                dataArray,
217                dataArray.length
218              );
219            }
220          } else if (keyboardEvent.altKey) {
221            return;
222          } else {
223            // @ts-ignore
224            code = this.escapeCharacterDict[cmd];
225            if (code) {
226              const dataArray = new Uint8Array(code);
227              hdcShellStream.sendToDaemon(
228                new FormatCommand(HdcCommand.CMD_SHELL_DATA, cmd, false),
229                dataArray,
230                dataArray.length
231              );
232            } else {
233              if (cmd.length === 1) {
234                hdcShellStream.DoCommand(cmd);
235              }
236            }
237          }
238        } else {
239          hdcShellStream.DoCommand(HdcDeviceManager.processCommand(keyboardEvent));
240        }
241      };
242    }
243  }
244
245  private static processCommand(command: string): string {
246    if (command.indexOf('\\') === -1) {
247      return command;
248    }
249    const lines = command.split('\r\n');
250    let processedCommand = '';
251    lines.forEach((line) => {
252      if (line.endsWith('\\')) {
253        line = line.slice(0, -1);
254        processedCommand += line;
255      } else {
256        processedCommand += line;
257        processedCommand += '\n';
258      }
259    });
260    return processedCommand;
261  }
262
263  /**
264   * Execute shell on the currently connected device, the result is returned as Blob
265   *
266   * @param cmd cmd
267   */
268  public static async shellResultAsBlob(cmd: string, isSkipResult: boolean): Promise<Blob> {
269    if (this.currentHdcClient) {
270      const hdcStream = new HdcStream(this.currentHdcClient, false);
271      log(`cmd is ${cmd}`);
272      await hdcStream.DoCommand(cmd);
273      let finalBuffer;
274      while (true) {
275        const dataMessage = await hdcStream.getMessage();
276        if (dataMessage.channelClose || isSkipResult) {
277          log('result is end, close');
278          break;
279        }
280        const res = dataMessage.getData();
281        if (res) {
282          if (!finalBuffer) {
283            finalBuffer = new Uint8Array(res);
284          } else {
285            finalBuffer = HdcDeviceManager.appendBuffer(finalBuffer, new Uint8Array(res));
286          }
287        }
288      }
289      await hdcStream.closeStream();
290      if (finalBuffer) {
291        return Promise.resolve(new Blob([finalBuffer]));
292      }
293      return Promise.resolve(new Blob());
294    }
295    return Promise.reject('not select device');
296  }
297
298  /**
299   * Pull the corresponding file from the device side
300   *
301   * @param filename filename
302   */
303  // 进度
304  public static async fileRecv(filename: string, callBack: (schedule: number) => void): Promise<Blob> {
305    let finalBuffer;
306    if (this.currentHdcClient) {
307      const hdcStream = new HdcStream(this.currentHdcClient, false);
308      await hdcStream.DoCommand(`${HdcDeviceManager.FILE_RECV_PREFIX_STRING + filename} ./`);
309      if (!finalBuffer && hdcStream.fileSize > 0) {
310        finalBuffer = new Uint8Array(hdcStream.fileSize);
311        log(`Uint8Array size is ${finalBuffer.byteLength}`);
312      }
313      let offset = 0;
314      while (true) {
315        const dataMessage = await hdcStream.getMessage();
316        if (dataMessage.channelClose) {
317          log('result is end, close');
318          break;
319        }
320        if (dataMessage.commandFlag === HdcCommand.CMD_FILE_FINISH) {
321          await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '', false));
322          log('CMD_FILE_FINISH is end, close');
323          break;
324        }
325        const res = dataMessage.getData();
326        if (res) {
327          const resRS: ArrayBuffer = res.slice(64);
328          if (finalBuffer) {
329            finalBuffer.set(new Uint8Array(resRS), offset);
330            offset += resRS.byteLength;
331            callBack(Number(((offset / hdcStream.fileSize) * 100).toFixed(3)));
332          }
333        }
334        if (hdcStream.fileSize !== -1 && offset >= hdcStream.fileSize) {
335          callBack(100);
336          await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_FILE_FINISH, '', false));
337        }
338      }
339    }
340    if (finalBuffer) {
341      return Promise.resolve(new Blob([finalBuffer]));
342    } else {
343      return Promise.resolve(new Blob([]));
344    }
345  }
346
347  private static shellInit(hdcShellStream: HdcStream, resultCallBack: (res: DataMessage) => void): void {
348    hdcShellStream.DoCommand('hdc_std shell').then(async () => {
349      while (true) {
350        const data = await hdcShellStream.getMessage();
351        resultCallBack(data);
352        if (data.channelClose) {
353          const channelClose = new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false);
354          hdcShellStream.DoCommandRemote(channelClose).then(() => {
355            hdcShellStream.closeStream();
356          });
357          return;
358        }
359      }
360    });
361  }
362
363  /**
364   * appendBuffer
365   *
366   * @param buffer1 firstBuffer
367   * @param buffer2 secondBuffer
368   * @private
369   */
370  private static appendBuffer(buffer1: Uint8Array, buffer2: Uint8Array): Uint8Array {
371    const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
372    tmp.set(buffer1, 0);
373    tmp.set(buffer2, buffer1.byteLength);
374    return tmp;
375  }
376}
377