1/*
2 * Copyright (c) 2022-2023 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 */
15let rpc = requireNapi('rpc');
16
17const EVENT_CALL_NOTIFY = 1;
18const REQUEST_SUCCESS = 0;
19
20const ERROR_CODE_INVALID_PARAM = 401;
21const ERROR_CODE_CALLER_RELEASED = 16200001;
22const ERROR_CODE_CLAAEE_INVALID = 16200002;
23const ERROR_CODE_INNER_ERROR = 16000050;
24
25const ERROR_MSG_INVALID_PARAM = 'Invalid input parameter.';
26const ERROR_MSG_CALLER_RELEASED = 'Caller released. The caller has been released.';
27const ERROR_MSG_CLAAEE_INVALID = 'Callee invalid. The callee does not exist.';
28const ERROR_MSG_INNER_ERROR = 'Inner Error.';
29
30let errMap = new Map();
31errMap.set(ERROR_CODE_INVALID_PARAM, ERROR_MSG_INVALID_PARAM);
32errMap.set(ERROR_CODE_CALLER_RELEASED, ERROR_MSG_CALLER_RELEASED);
33errMap.set(ERROR_CODE_CLAAEE_INVALID, ERROR_MSG_CLAAEE_INVALID);
34errMap.set(ERROR_CODE_INNER_ERROR, ERROR_MSG_INNER_ERROR);
35
36class BusinessError extends Error {
37  constructor(code) {
38    let msg = '';
39    if (errMap.has(code)) {
40      msg = errMap.get(code);
41    } else {
42      msg = ERROR_MSG_INNER_ERROR;
43    }
44    super(msg);
45    this.code = code;
46  }
47}
48
49class ThrowInvalidParamError extends Error {
50  constructor(msg) {
51    let message = ERROR_MSG_INVALID_PARAM + msg;
52    super(message);
53    this.code = ERROR_CODE_INVALID_PARAM;
54  }
55}
56
57class Caller {
58  constructor(obj) {
59    console.log('Caller::constructor obj is ' + typeof obj);
60    this.__call_obj__ = obj;
61    this.releaseState = false;
62  }
63
64  call(method, data) {
65    return new Promise(async (resolve, reject) => {
66      const checkError = this.callCheck(method, data);
67      if (checkError != null) {
68        reject(checkError);
69        return;
70      }
71
72      console.log('Caller call msgData rpc.MessageSequence create');
73      let msgData = this.buildMsgData(method, data);
74      let msgReply = rpc.MessageSequence.create();
75
76      let retData = undefined;
77      try {
78        retData = await this.__call_obj__.callee.sendMessageRequest(EVENT_CALL_NOTIFY, msgData, msgReply,
79          rpc.MessageOption());
80        console.log('Caller call msgData rpc.sendMessageRequest called');
81        if (retData.errCode !== 0) {
82          msgData.reclaim();
83          msgReply.reclaim();
84          console.log('Caller call return errCode ' + retData.errCode);
85          reject(new BusinessError(retData.errCode));
86          return;
87        }
88      } catch (e) {
89        console.log('Caller call msgData rpc.sendMessageRequest error ' + e);
90      }
91
92      try {
93        let retval = retData.reply.readInt();
94        let str = retData.reply.readString();
95        if (retval === REQUEST_SUCCESS && str === 'object') {
96          msgData.reclaim();
97          msgReply.reclaim();
98        } else {
99          console.log('Caller call retval is [' + retval + '], str [' + str + ']');
100          msgData.reclaim();
101          msgReply.reclaim();
102          reject(new BusinessError(retval));
103          return;
104        }
105      } catch (e) {
106        console.log('Caller call msgData sendMessageRequest retval error');
107        msgData.reclaim();
108        msgReply.reclaim();
109        reject(new BusinessError(ERROR_CODE_INNER_ERROR));
110        return;
111      }
112
113      console.log('Caller call msgData sendMessageRequest end');
114      resolve(undefined);
115      return;
116    });
117  }
118
119  callWithResult(method, data) {
120    return new Promise(async (resolve, reject) => {
121      console.log('Caller callWithResult method [' + method + ']');
122      const checkError = this.callCheck(method, data);
123      if (checkError != null) {
124        reject(checkError);
125        return;
126      }
127
128      console.log('Caller callWithResult msgData rpc.MessageSequence create');
129      let msgData = this.buildMsgData(method, data);
130      let msgReply = rpc.MessageSequence.create();
131
132      let reply = undefined;
133      let retData = undefined;
134      try {
135        retData = await this.__call_obj__.callee.sendMessageRequest(EVENT_CALL_NOTIFY, msgData, msgReply,
136          rpc.MessageOption());
137        console.log('Caller callWithResult msgData rpc.sendMessageRequest called');
138        if (retData.errCode !== 0) {
139          msgData.reclaim();
140          msgReply.reclaim();
141          console.log('Caller callWithResult return errCode ' + retData.errCode);
142          reject(new BusinessError(retData.errCode));
143          return;
144        }
145      } catch (e) {
146        console.log('Caller call msgData rpc.MessageSequence error ' + e);
147      }
148
149      try {
150        let retval = retData.reply.readInt();
151        let str = retData.reply.readString();
152        if (retval === REQUEST_SUCCESS && str === 'object') {
153          msgData.reclaim();
154          reply = retData.reply;
155        } else {
156          console.log('Caller callWithResult retval is [' + retval + '], str [' + str + ']');
157          msgData.reclaim();
158          msgReply.reclaim();
159          reject(new BusinessError(retval));
160          return;
161        }
162      } catch (e) {
163        console.log('Caller callWithResult msgData sendMessageRequest retval error');
164        msgData.reclaim();
165        msgReply.reclaim();
166        reject(new BusinessError(ERROR_CODE_INNER_ERROR));
167        return;
168      }
169
170      console.log('Caller callWithResult msgData sendMessageRequest end');
171      resolve(reply);
172      return;
173    });
174  }
175
176  release() {
177    console.log('Caller release js called.');
178    if (this.releaseState === true) {
179      console.log('Caller release remoteObj releaseState is true');
180      throw new BusinessError(ERROR_CODE_CALLER_RELEASED);
181    }
182
183    if (this.__call_obj__.callee == null) {
184      console.log('Caller release call remoteObj is released');
185      throw new BusinessError(ERROR_CODE_CLAAEE_INVALID);
186    }
187
188    this.releaseState = true;
189    this.__call_obj__.release();
190  }
191
192  onRelease(callback) {
193    console.log('Caller onRelease jscallback called.');
194    if (typeof callback !== 'function') {
195      console.log('Caller onRelease ' + typeof callback);
196      throw new ThrowInvalidParamError('Parameter error: Failed to get callback, must be a function.');
197    }
198
199    if (this.releaseState === true) {
200      console.log('Caller onRelease remoteObj releaseState is true');
201      throw new BusinessError(ERROR_CODE_CALLER_RELEASED);
202    }
203
204    this.__call_obj__.onRelease(callback);
205  }
206
207  onRemoteStateChange(callback) {
208    console.log('Caller onRemoteStateChange jscallback called.');
209    if (typeof callback !== 'function') {
210      console.log('Caller onRemoteStateChange ' + typeof callback);
211      throw new ThrowInvalidParamError('Parameter error: Failed to get callback, must be a function.');
212    }
213
214    if (this.releaseState === true) {
215      console.log('Caller onRemoteStateChange remoteObj releaseState is true');
216      throw new BusinessError(ERROR_CODE_CALLER_RELEASED);
217    }
218
219    this.__call_obj__.onRemoteStateChange(callback);
220  }
221
222  on(type, callback) {
223    console.log('Caller onRelease jscallback called.');
224    if (typeof type !== 'string' || type !== 'release') {
225      console.log(
226        'Caller onRelease error, input [type] is invalid.');
227      throw new ThrowInvalidParamError('Parameter error: Failed to get type, must be string type release.');
228    }
229
230    if (typeof callback !== 'function') {
231      console.log('Caller onRelease error ' + typeof callback);
232      throw new ThrowInvalidParamError('Parameter error: Failed to get callback, must be a function.');
233    }
234
235    if (this.releaseState === true) {
236      console.log('Caller onRelease error, remoteObj releaseState is true');
237      throw new BusinessError(ERROR_CODE_CALLER_RELEASED);
238    }
239
240    this.__call_obj__.onRelease(callback);
241  }
242
243  off(type, callback) {
244    if (typeof type !== 'string' || type !== 'release') {
245      console.log(
246        'Caller onRelease error, input [type] is invalid.');
247      throw new ThrowInvalidParamError('Parameter error: Failed to get type, must be string type release.');
248    }
249
250    if (callback && typeof callback !== 'function') {
251      console.log('Caller onRelease error ' + typeof callback);
252      throw new ThrowInvalidParamError('Parameter error: Failed to get callback, must be a function.');
253    }
254    // Empty
255  }
256
257  callCheck(method, data) {
258    if (typeof method !== 'string' || typeof data !== 'object') {
259      console.log('Caller callCheck ' + typeof method + ' ' + typeof data);
260      return new ThrowInvalidParamError('Parameter error: Failed to get method or data, ' +
261        'method must be a string, data must be a rpc.Parcelable');
262    }
263
264    if (method === '' || data == null) {
265      console.log('Caller callCheck ' + method + ', ' + data);
266      return new ThrowInvalidParamError('Parameter error: method or data is empty, Please check it.');
267    }
268
269    if (this.releaseState === true) {
270      console.log('Caller callCheck this.callee release');
271      return new BusinessError(ERROR_CODE_CALLER_RELEASED);
272    }
273
274    if (this.__call_obj__.callee == null) {
275      console.log('Caller callCheck this.callee is nullptr');
276      return new BusinessError(ERROR_CODE_CLAAEE_INVALID);
277    }
278    return null;
279  }
280
281  buildMsgData(method, data) {
282    let msgData = rpc.MessageSequence.create();
283    msgData.writeString(method);
284    msgData.writeParcelable(data);
285    return msgData;
286  }
287}
288
289export default Caller;
290