• 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) => 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   */
137  public static async shellResultAsString(cmd: string, isSkipResult: boolean): Promise<string> {
138    if (this.currentHdcClient) {
139      const hdcStream = new HdcStream(this.currentHdcClient, false);
140      await hdcStream.DoCommand(cmd);
141      let result = '';
142      while (true) {
143        const dataMessage = await hdcStream.getMessage();
144        if (dataMessage.channelClose || isSkipResult) {
145          result += dataMessage.getDataToString();
146          await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false));
147          log('result is end, close');
148          break;
149        }
150        if (dataMessage.usbHead.sessionId === -1) {
151          await hdcStream.closeStream();
152          return Promise.resolve('The device is abnormal');
153        }
154        result += dataMessage.getDataToString();
155      }
156      await hdcStream.closeStream();
157      await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false));
158      return Promise.resolve(result);
159    }
160    return Promise.reject('not select device');
161  }
162
163  /**
164   * Execute shell on the currently connected device and return the result as a string
165   *
166   * @param cmd cmd
167   */
168  public static async stopHiprofiler(cmd: string): Promise<string> {
169    if (this.currentHdcClient) {
170      const hdcStream = new HdcStream(this.currentHdcClient, true);
171      await hdcStream.DoCommand(cmd);
172      let result = '';
173      let dataMessage = await hdcStream.getMessage();
174      result += dataMessage.getDataToString();
175      while (!dataMessage.channelClose) {
176        dataMessage = await hdcStream.getMessage();
177        result += dataMessage.getDataToString();
178      }
179      await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false));
180      await hdcStream.closeStream();
181      return Promise.resolve(result);
182    }
183    return Promise.reject('not select device');
184  }
185
186  public static startShell(
187    resultCallBack: (res: DataMessage) => void
188  ): ((keyboardEvent: KeyboardEvent | string) => void | undefined) | undefined {
189    if (this.currentHdcClient) {
190      const hdcShellStream = new HdcStream(this.currentHdcClient, false);
191      this.shellInit(hdcShellStream, resultCallBack);
192      return (keyboardEvent: KeyboardEvent | string): void => {
193        let code = undefined;
194        if (keyboardEvent instanceof KeyboardEvent) {
195          const cmd = keyboardEvent.key;
196          if (keyboardEvent.shiftKey && keyboardEvent.key.toUpperCase() === 'SHIFT') {
197            return;
198          } else if (keyboardEvent.metaKey) {
199            return;
200          } else if (keyboardEvent.ctrlKey) {
201            // @ts-ignore
202            code = this.ctrlKey[keyboardEvent.key];
203            if (!code) {
204              return;
205            } else {
206              const dataArray = new Uint8Array(code);
207              hdcShellStream.sendToDaemon(
208                new FormatCommand(HdcCommand.CMD_SHELL_DATA, cmd, false),
209                dataArray,
210                dataArray.length
211              );
212            }
213          } else if (keyboardEvent.altKey) {
214            return;
215          } else {
216            // @ts-ignore
217            code = this.escapeCharacterDict[cmd];
218            if (code) {
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            } else {
226              if (cmd.length === 1) {
227                hdcShellStream.DoCommand(cmd);
228              }
229            }
230          }
231        } else {
232          hdcShellStream.DoCommand(HdcDeviceManager.processCommand(keyboardEvent));
233        }
234      };
235    }
236  }
237
238  private static processCommand(command: string): string {
239    if (command.indexOf('\\') === -1) {
240      return command;
241    }
242    const lines = command.split('\r\n');
243    let processedCommand = '';
244    lines.forEach((line) => {
245      if (line.endsWith('\\')) {
246        line = line.slice(0, -1);
247        processedCommand += line;
248      } else {
249        processedCommand += line;
250        processedCommand += '\n';
251      }
252    });
253    return processedCommand;
254  }
255
256  /**
257   * Execute shell on the currently connected device, the result is returned as Blob
258   *
259   * @param cmd cmd
260   */
261  public static async shellResultAsBlob(cmd: string, isSkipResult: boolean): Promise<Blob> {
262    if (this.currentHdcClient) {
263      const hdcStream = new HdcStream(this.currentHdcClient, false);
264      log(`cmd is ${cmd}`);
265      await hdcStream.DoCommand(cmd);
266      let finalBuffer;
267      while (true) {
268        const dataMessage = await hdcStream.getMessage();
269        if (dataMessage.channelClose || isSkipResult) {
270          log('result is end, close');
271          break;
272        }
273        const res = dataMessage.getData();
274        if (res) {
275          if (!finalBuffer) {
276            finalBuffer = new Uint8Array(res);
277          } else {
278            finalBuffer = HdcDeviceManager.appendBuffer(finalBuffer, new Uint8Array(res));
279          }
280        }
281      }
282      await hdcStream.closeStream();
283      if (finalBuffer) {
284        return Promise.resolve(new Blob([finalBuffer]));
285      }
286      return Promise.resolve(new Blob());
287    }
288    return Promise.reject('not select device');
289  }
290
291  /**
292   * Pull the corresponding file from the device side
293   *
294   * @param filename filename
295   */
296  // 进度
297  public static async fileRecv(filename: string, callBack: (schedule: number) => void): Promise<Blob> {
298    let finalBuffer;
299    if (this.currentHdcClient) {
300      const hdcStream = new HdcStream(this.currentHdcClient, false);
301      await hdcStream.DoCommand(`${HdcDeviceManager.FILE_RECV_PREFIX_STRING + filename} ./`);
302      if (!finalBuffer && hdcStream.fileSize > 0) {
303        finalBuffer = new Uint8Array(hdcStream.fileSize);
304        log(`Uint8Array size is ${finalBuffer.byteLength}`);
305      }
306      let offset = 0;
307      while (true) {
308        const dataMessage = await hdcStream.getMessage();
309        if (dataMessage.channelClose) {
310          log('result is end, close');
311          break;
312        }
313        if (dataMessage.commandFlag === HdcCommand.CMD_FILE_FINISH) {
314          await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '', false));
315          log('CMD_FILE_FINISH is end, close');
316          break;
317        }
318        const res = dataMessage.getData();
319        if (res) {
320          const resRS: ArrayBuffer = res.slice(64);
321          if (finalBuffer) {
322            finalBuffer.set(new Uint8Array(resRS), offset);
323            offset += resRS.byteLength;
324            callBack(Number(((offset / hdcStream.fileSize) * 100).toFixed(3)));
325          }
326        }
327        if (hdcStream.fileSize !== -1 && offset >= hdcStream.fileSize) {
328          callBack(100);
329          await hdcStream.DoCommandRemote(new FormatCommand(HdcCommand.CMD_FILE_FINISH, '', false));
330        }
331      }
332    }
333    if (finalBuffer) {
334      return Promise.resolve(new Blob([finalBuffer]));
335    } else {
336      return Promise.resolve(new Blob([]));
337    }
338  }
339
340  private static shellInit(hdcShellStream: HdcStream, resultCallBack: (res: DataMessage) => void): void {
341    hdcShellStream.DoCommand('hdc_std shell').then(async () => {
342      while (true) {
343        const data = await hdcShellStream.getMessage();
344        resultCallBack(data);
345        if (data.channelClose) {
346          const channelClose = new FormatCommand(HdcCommand.CMD_KERNEL_CHANNEL_CLOSE, '0', false);
347          hdcShellStream.DoCommandRemote(channelClose).then(() => {
348            hdcShellStream.closeStream();
349          });
350          return;
351        }
352      }
353    });
354  }
355
356  /**
357   * appendBuffer
358   *
359   * @param buffer1 firstBuffer
360   * @param buffer2 secondBuffer
361   * @private
362   */
363  private static appendBuffer(buffer1: Uint8Array, buffer2: Uint8Array): Uint8Array {
364    const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
365    tmp.set(buffer1, 0);
366    tmp.set(buffer2, buffer1.byteLength);
367    return tmp;
368  }
369}
370