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 { DataMessage } from '../message/DataMessage';
17import { HdcClient } from './HdcClient';
18import { FormatCommand } from './FormatCommand';
19import { HdcCommand } from './HdcCommand';
20import { Utils } from '../common/Utils';
21import { AsyncQueue } from './AsyncQueue';
22import { PayloadHead } from '../message/PayloadHead';
23import { Serialize } from '../common/Serialize';
24
25export class HdcStream {
26  private dataMessages: AsyncQueue<DataMessage> = new AsyncQueue<DataMessage>();
27  private readonly channelId: number;
28  private interactiveShellMode: boolean = false;
29  private hdcClient: HdcClient;
30  public fileSize: number = -1;
31
32  constructor(hdcClient: HdcClient, isStopCmd: boolean) {
33    this.hdcClient = hdcClient;
34    this.channelId = Utils.getLocalId();
35    this.hdcClient.bindStream(this.channelId, this);
36  }
37
38  public async DoCommand(cmd: string): Promise<boolean> {
39    let formatCommand;
40    if (this.interactiveShellMode) {
41      formatCommand = new FormatCommand(HdcCommand.CMD_SHELL_DATA, cmd, false);
42    } else {
43      formatCommand = Utils.formatCommand(cmd);
44    }
45    return this.DoCommandRemote(formatCommand);
46  }
47
48  public async DoCommandRemote(command: FormatCommand): Promise<boolean> {
49    switch (command.cmdFlag) {
50      case HdcCommand.CMD_SHELL_INIT:
51      case HdcCommand.CMD_SHELL_DATA:
52      case HdcCommand.CMD_UNITY_EXECUTE:
53      case HdcCommand.CMD_UNITY_TERMINATE:
54      case HdcCommand.CMD_UNITY_REMOUNT:
55      case HdcCommand.CMD_UNITY_REBOOT:
56      case HdcCommand.CMD_UNITY_RUNMODE:
57      case HdcCommand.CMD_UNITY_HILOG: {
58        let textEncoder = new TextEncoder();
59        let data = textEncoder.encode(command.parameters);
60        let sendResult = await this.sendToDaemon(command, data, data.length);
61        if (sendResult) {
62          if (HdcCommand.CMD_SHELL_INIT === command.cmdFlag) {
63            this.interactiveShellMode = true;
64          }
65        }
66        break;
67      }
68      case HdcCommand.CMD_FILE_INIT: {
69        await this.FileRecvCommand(command);
70        break;
71      }
72      case HdcCommand.CMD_FILE_FINISH:
73      case HdcCommand.CMD_KERNEL_CHANNEL_CLOSE: {
74        let dataView = new DataView(new ArrayBuffer(1));
75        if (command.parameters === '0') {
76          dataView.setUint8(0, 0);
77        } else {
78          dataView.setUint8(0, 1);
79        }
80        await this.sendToDaemon(command, new Uint8Array(dataView.buffer), 1);
81        break;
82      }
83    }
84    return false;
85  }
86
87  async FileRecvCommand(command: FormatCommand): Promise<void> {
88    let cmdFlag: string = '';
89    let sizeCmdFlag: number = 0;
90    if (HdcCommand.CMD_FILE_INIT === command.cmdFlag) {
91      cmdFlag = 'send ';
92      sizeCmdFlag = 5; // 5: cmdFlag send size
93    }
94    if (!(command.parameters.length > cmdFlag.length)) {
95    } else {
96      let textEncoder = new TextEncoder();
97      let data = textEncoder.encode(command.parameters);
98      let aa = data.slice(5);
99      await this.sendToDaemon(command, aa, aa.length);
100      let fileRecvDataMessage = await this.getMessage();
101      let fileRecvPlayHeadArray = fileRecvDataMessage.body!.buffer.slice(0, PayloadHead.getPayloadHeadLength());
102      let fileRecvResultPayloadHead: PayloadHead = PayloadHead.parsePlayHead(new DataView(fileRecvPlayHeadArray));
103      let fileRecvHeadSize = fileRecvResultPayloadHead.headSize;
104      let resPlayProtectBuffer = fileRecvDataMessage.body!.buffer.slice(11, 11 + fileRecvHeadSize);
105      Serialize.parsePayloadProtect(resPlayProtectBuffer);
106      await this.handleCommandFileCheck();
107    }
108  }
109
110  private async handleCommandFileCheck(): Promise<void> {
111    let fileCheckDataMessage = await this.getMessage();
112    let fileCheckPlayHeadArray = fileCheckDataMessage.body!.buffer.slice(0, PayloadHead.getPayloadHeadLength());
113    let fileCheckResultPayloadHead: PayloadHead = PayloadHead.parsePlayHead(new DataView(fileCheckPlayHeadArray));
114    let fileCheckHeadSize = fileCheckResultPayloadHead.headSize;
115    let fileCheckDataSize = fileCheckResultPayloadHead.dataSize;
116    let fileCheckResPlayProtectBuffer = fileCheckDataMessage.body!.buffer.slice(
117      PayloadHead.getPayloadHeadLength(),
118      PayloadHead.getPayloadHeadLength() + fileCheckHeadSize
119    );
120    let fileCheckPayloadProtect = Serialize.parsePayloadProtect(fileCheckResPlayProtectBuffer);
121    if (fileCheckPayloadProtect.commandFlag === HdcCommand.CMD_FILE_CHECK) {
122      if (fileCheckDataSize > 0) {
123        let fileCheckTransferConfigBuffer = fileCheckDataMessage.body!.buffer.slice(
124          PayloadHead.getPayloadHeadLength() + fileCheckHeadSize,
125          PayloadHead.getPayloadHeadLength() + fileCheckHeadSize + fileCheckDataSize
126        );
127        let fileCheckTransferConfig = Serialize.parseTransferConfig(fileCheckTransferConfigBuffer);
128        this.fileSize = fileCheckTransferConfig.fileSize;
129      }
130      let fileBegin = new FormatCommand(HdcCommand.CMD_FILE_BEGIN, '', false);
131      await this.sendToDaemon(fileBegin, new Uint8Array(0), 0);
132    }
133  }
134
135  async sendToDaemon(command: FormatCommand, payload: Uint8Array, dataLength: number): Promise<boolean> {
136    return await this.hdcClient.readDataProcessing.send(
137      this.hdcClient.sessionId,
138      this.channelId,
139      command.cmdFlag,
140      payload,
141      dataLength
142    );
143  }
144
145  putMessageInQueue(dataMessage: DataMessage): void {
146    this.dataMessages.enqueue(dataMessage);
147  }
148
149  getMessage(): Promise<DataMessage> {
150    return this.dataMessages.dequeue();
151  }
152
153  async closeStream(): Promise<void> {
154    this.hdcClient.unbindStream(this.channelId);
155  }
156}
157