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 */
15let rpc = requireNapi('rpc');
16let accessControl = requireNapi('abilityAccessCtrl');
17
18const EVENT_CALL_NOTIFY = 1;
19const REQUEST_SUCCESS = 0;
20const REQUEST_FAILED = 1;
21const PERMISSION_ABILITY_BACKGROUND_COMMUNICATION = 'ohos.permission.ABILITY_BACKGROUND_COMMUNICATION';
22
23const ERROR_CODE_INVALID_PARAM = 401;
24const ERROR_CODE_FUNC_REGISTERED = 16200004;
25const ERROR_CODE_FUNC_NOT_EXIST = 16200005;
26const ERROR_CODE_INNER_ERROR = 16000050;
27
28const ERROR_MSG_INVALID_PARAM = 'Invalid input parameter.';
29const ERROR_MSG_FUNC_REGISTERED = 'Method registered. The method has registered.';
30const ERROR_MSG_FUNC_NOT_EXIST = 'Method not registered. The method has not registered.';
31const ERROR_MSG_INNER_ERROR = 'Inner Error.';
32
33let errMap = new Map();
34errMap.set(ERROR_CODE_INVALID_PARAM, ERROR_MSG_INVALID_PARAM);
35errMap.set(ERROR_CODE_FUNC_REGISTERED, ERROR_MSG_FUNC_REGISTERED);
36errMap.set(ERROR_CODE_FUNC_NOT_EXIST, ERROR_MSG_FUNC_NOT_EXIST);
37errMap.set(ERROR_CODE_INNER_ERROR, ERROR_MSG_INNER_ERROR);
38
39class BusinessError extends Error {
40  constructor(code) {
41    let msg = '';
42    if (errMap.has(code)) {
43      msg = errMap.get(code);
44    } else {
45      msg = ERROR_MSG_INNER_ERROR;
46    }
47    super(msg);
48    this.code = code;
49  }
50}
51
52class ThrowInvalidParamError extends Error {
53  constructor(msg) {
54    let message = ERROR_MSG_INVALID_PARAM + msg;
55    super(message);
56    this.code = ERROR_CODE_INVALID_PARAM;
57  }
58}
59
60class Callee extends rpc.RemoteObject {
61  constructor(des) {
62    if (typeof des === 'string') {
63      super(des);
64      this.callList = new Map();
65      this.startUpNewRule = false;
66      console.log('Callee constructor is OK ' + typeof des);
67    } else {
68      console.log('Callee constructor error, des is ' + typeof des);
69      return null;
70    }
71  }
72
73  setNewRuleFlag(flag) {
74    this.startUpNewRule = flag;
75  }
76
77  onRemoteMessageRequest(code, data, reply, option) {
78    console.log('Callee onRemoteMessageRequest code [' + typeof code + ' ' + code + ']');
79    if (!this.StartUpRuleCheck()) {
80      return false;
81    }
82
83    if (typeof code !== 'number' || typeof data !== 'object' ||
84      typeof reply !== 'object' || typeof option !== 'object') {
85      console.log('Callee onRemoteMessageRequest error, code is [' +
86        typeof code + '], data is [' + typeof data + '], reply is [' +
87        typeof reply + '], option is [' + typeof option + ']');
88      return false;
89    }
90
91    console.log('Callee onRemoteMessageRequest code proc');
92    if (code === EVENT_CALL_NOTIFY) {
93      if (this.callList == null) {
94        console.log('Callee onRemoteMessageRequest error, this.callList is nullptr');
95        return false;
96      }
97
98      let method = data.readString();
99      console.log('Callee onRemoteMessageRequest method [' + method + ']');
100      let func = this.callList.get(method);
101      if (typeof func !== 'function') {
102        console.log('Callee onRemoteMessageRequest error, get func is ' + typeof func);
103        return false;
104      }
105
106      let result = func(data);
107      if (typeof result === 'object' && result != null) {
108        reply.writeInt(REQUEST_SUCCESS);
109        reply.writeString(typeof result);
110        reply.writeParcelable(result);
111        console.log('Callee onRemoteMessageRequest code proc Packed data');
112      } else {
113        reply.writeInt(REQUEST_FAILED);
114        reply.writeString(typeof result);
115        console.log('Callee onRemoteMessageRequest error, retval is ' + REQUEST_FAILED + ', type is ' + typeof result);
116      }
117    } else {
118      console.log('Callee onRemoteMessageRequest error, code is ' + code);
119      return false;
120    }
121    console.log('Callee onRemoteMessageRequest code proc success');
122    return true;
123  }
124
125  on(method, callback) {
126    if (typeof method !== 'string' || method === '' || typeof callback !== 'function') {
127      console.log(
128        'Callee on error, method is [' + typeof method + '], typeof callback [' + typeof callback + ']');
129      throw new ThrowInvalidParamError('Parameter error: Failed to get method or callback.' +
130        'method must be a non-empty string, callback must be a function.');
131    }
132
133    if (this.callList == null) {
134      console.log('Callee on error, this.callList is nullptr');
135      throw new BusinessError(ERROR_CODE_INNER_ERROR);
136    }
137
138    if (this.callList.has(method)) {
139      console.log('Callee on error, [' + method + '] has registered');
140      throw new BusinessError(ERROR_CODE_FUNC_REGISTERED);
141    }
142
143    this.callList.set(method, callback);
144    console.log('Callee on method [' + method + ']');
145  }
146
147  off(method) {
148    if (typeof method !== 'string' || method === '') {
149      console.log('Callee off error, method is [' + typeof method + ']');
150      throw new ThrowInvalidParamError('Parameter error: Failed to get method, must be a string.');
151    }
152
153    if (this.callList == null) {
154      console.log('Callee off error, this.callList is null');
155      throw new BusinessError(ERROR_CODE_INNER_ERROR);
156    }
157
158    if (!this.callList.has(method)) {
159      console.log('Callee off error, this.callList not found ' + method);
160      throw new BusinessError(ERROR_CODE_FUNC_NOT_EXIST);
161    }
162
163    this.callList.delete(method);
164    console.log('Callee off method [' + method + ']');
165  }
166
167  StartUpRuleCheck() {
168    if (this.startUpNewRule && rpc.IPCSkeleton.isLocalCalling()) {
169      console.log('Use new start up rule, check caller permission.');
170      let accessManger = accessControl.createAtManager();
171      let accessTokenId = rpc.IPCSkeleton.getCallingTokenId();
172      let grantStatus =
173        accessManger.verifyAccessTokenSync(accessTokenId, PERMISSION_ABILITY_BACKGROUND_COMMUNICATION);
174      if (grantStatus === accessControl.GrantStatus.PERMISSION_DENIED) {
175        console.log(
176          'Callee onRemoteMessageRequest error, the Caller does not have PERMISSION_ABILITY_BACKGROUND_COMMUNICATION');
177        return false;
178      }
179    }
180    return true;
181  }
182}
183
184export default Callee;
185