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